#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;
}