#include "clib.h" #include "i386.h" #include "paging.h" #include "vm86.h" #include "kmalloc.h" #include "phmem.h" #define LINEAR(seg,ofs) (addr_t)(tss->seg*16L+((word)(dword)tss->ofs)) /* * questo e` il gestore dei #GPF generati dai task V86. * praticamente il suo scopo e` di emulare le istruzioni. * * BUG: le istruzioni non tengono conto del prefisso 66h, * le istruzioni di stringa OUTS/INS non ci sono * mancano le istruzioni per trappare le istruzioni quando * cr4[VME] e` 0. */ int handle_v86_gpfault(tss_t* tss) { v86_tss* v86= (v86_tss*)tss; u8 opcodes[4]; dword dw; bool db66 = false; addr_t va; va = LINEAR(cs,eip); read_from_task(tss, va, opcodes, sizeof(opcodes)); // kprintf("VME: opcode %x at %x:%x\n", *(dword*)opcodes, tss->cs,tss->eip); if (opcodes[0] == 0x66) { *(dword*)opcodes = *(dword*)((u8*)opcodes+1); DEBUG_FATAL("DB 66h opcode in v86 task!"); } switch (opcodes[0]) { /* INT */ case 0xCD: execute_v86_int(tss, opcodes[1], true); return 0; case 0xD8 ... 0xDF: kprintf("FPU instruction! at cs:ip %x:%x\n", tss->cs, tss->eip); DEBUG_FATAL("FPU ..."); asm("clts"); return 0; /* in al,8 */ case 0xE4: // kprintf("IN AL,%x\n", opcodes[1]); dw = inportb(opcodes[1]); tss->eax |= dw; tss->eip += 2; return 0; /* in ax,8 */ case 0xE5: // kprintf("IN AX,%x\n", opcodes[1]); dw = inportw(opcodes[1]); tss->eax |= dw; tss->eip += 2; return 0; /* out 8,AL */ case 0xE6: // kprintf("OUT %x,AL\n", opcodes[1]); if (opcodes[1] != 0x20 && opcodes[1] != 0xA0) { outportb(opcodes[1], tss->eax); } tss->eip += 2; return 0; /* out 8,Ax */ case 0xE7: outportw(opcodes[1], tss->eax); tss->eip += 2; return 0; /* in al,dx */ case 0xEC: dw = inportb(tss->edx); tss->eax |= dw; tss->eip++; return 0; /* in ax,dx */ case 0xED: dw = inportw(tss->edx); tss->eax |= dw; tss->eip++; return 0; /* out dx,al */ case 0xEE: // kprintf("OUT DX(%x),AL\n", (word)tss->edx); outportb(tss->edx, tss->eax); tss->eip++; return 0; /* out dx,ax */ case 0xEF: // kprintf("OUT DX(%x),AL\n", (word)tss->edx); outportw(tss->edx, tss->eax); tss->eip++; return 0; /* sti */ case 0xFB: /* iret */ case 0xcf: /* popf */ case 0x9d: if (tss->eflags & F_VIP) { int i; for (i = 0; i < 0x10; i++) { if (v86->irqs & (1 shl i)) { // kprintf("VIP IRQ %x\n",i); if (i < 8) { execute_v86_int(tss,8+i,false); } else { execute_v86_int(tss, 0x68+i,false); } } } v86->irqs = 0; tss->eflags &= ~F_VIP; } // tss->eflags &= ~F_VIP; if (opcodes[0] == 0xFB) tss->eip++; return 0; #if 0 /* iret */ case 0xcf: { word buf[3]; read_from_task(tss, LINEAR(ss,esp)-2, buf, 6); kprintf("IRET: ip %xh, cs%xh, fl %xh\n", buf[0], buf[1], buf[2]); if (tss->eflags & F_VIP) { kprintf("IRET col VIP\n"); } else kprintf("IRET senza VIP??\n"); return 1; } #endif /* lock */ case 0xf0: tss->eip++; return 0; /* hlt */ case 0xf4: exit(0xf4); default : kprintf("unhandled VM86 opcode %x (%x)\n", opcodes[0], *(dword*)opcodes); return 1; } return 1; } /* * CX = # word to copy * ES:SI = ptr */ static void emulate_int15ah87(tss_t* tss) { byte buf[14]; addr_t from; addr_t to; DEBUG_FATAL("int 15!"); read_from_task(tss, LINEAR(es,esi) + 0x10, buf, 14); from = (addr_t)(buf[2] + (buf[3] shl 8)+ (buf[4] shl 16)); to = (addr_t)(buf[10] + (buf[11] shl 8)+ (buf[12] shl 16)); kprintf("INT15ax8701 from %x(%x) to %x(%x)\n", from, buf[5], to, buf[13]); /* *FIXME: non si dovrebbe usare memcpy!! * */ memcpy(to, from, ((word)tss->ecx)*2); tss->eflags &= ~1; tss->eip += 2; tss->eax &= 0xFFFF00FF; } void execute_v86_int(tss_t* tss, int intno, bool inc_eip) { word buf[4]; // kprintf("VMM86 INT %x ax %xh bx %xh cx %xh dx %xh cs %xh ip %xh\n", // intno, tss->eax, tss->ebx, tss->ecx, tss->edx, tss->cs, tss->eip); if (intno == 0x15 && ((word)tss->eax shr 8)== 0x87) { emulate_int15ah87(tss); return; } (word)tss->esp -= 6; if (inc_eip) buf[0] = ((word)(dword)tss->eip)+2; else buf[0] = ((word)(dword)tss->eip); buf[1] = (word)tss->cs; buf[2] = (word)tss->eflags; write_to_task(tss, LINEAR(ss, esp), buf, 6); read_from_task(tss, (addr_t)(intno*4), buf, 4); tss->cs = buf[1]; (dword)tss->eip = buf[0]; tss->eflags &= ~F_VIF; } void execute_vm86_irq(tss_t* tss, int irqno) { v86_tss* v86 = (v86_tss*)tss; if (tss->eflags & F_VIF) { if (irqno >= 8) irqno+=0x68; else irqno+=8; // if (irqno != 8) kprintf("VIF INT %x\n", irqno); execute_v86_int(tss, irqno, false); } else { // if (irqno != 0) kprintf("METTO il VIP IRQ %x\n",irqno); tss->eflags |= F_VIP; v86->irqs |= 1 shl irqno; } } v86_tss* alloc_v86_task(const char *name) { v86_tss *tss = NULL; addr_t va; int r; pte_t*pte; tss = (v86_tss*)kmalloc(sizeof(v86_tss)); if (tss == NULL) goto _ret; /* * riempio con 0xFF perche` l'ultimo byte della bitmap * deve essere 0xFF, e io non so come il gcc mi allinea * tutti i campi. inoltre assegno un valore di default * alle bitmap. */ memset(tss, 0xFF, sizeof(v86_tss)); tss->tss.limit = sizeof(v86_tss) - 1; if (alloc_tss(&tss->tss, name) == NULL) goto _ret; tss->tss.eflags = F_VM | F_VIF | F_IF; tss->tss.ioofs = (dword)&tss->iobitmap - (dword)&tss->tss; tss->irqs = 0; alloc_standard_task_pd(&tss->tss); if (tss->tss.cr3 == NULL) goto _ret; if (set_memory_limit(&tss->tss, 0, 0x110000) < 0x110000) goto _ret; if (alloc_vpages(&tss->tss, 0, 0x110, pfUser + pfWritable) != 0) goto _ret; /* * FIXME: c'e` da mettere a posto per bene le pagine, * non c'e` bisogno di copiarle tutte (video,rom &c) * per fare una cosa fatta bene ci vuole in copy-on-write */ if (write_to_task(&tss->tss, 0, 0, 0x110000) != 0) goto _ret; return tss; _ret: ; if (tss->tss.cr3 != NULL) free_task_pgtable(&tss->tss); if (tss) kfree(tss); WARNING("return NULL"); return NULL; }