/* * ReactOS kernel * Copyright (C) 2000, 2001 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/i386/v86m.c * PURPOSE: Support for v86 mode * PROGRAMMER: David Welch (welch@cwcom.net) */ /* INCLUDES *****************************************************************/ #include #include #include #include #include #include #define NDEBUG #include /* GLOBALS *******************************************************************/ #define IOPL_FLAG ((1 << 12) | (1 << 13)) #define INTERRUPT_FLAG (1 << 9) #define TRAP_FLAG (1 << 8) #define DIRECTION_FLAG (1 << 10) #define VALID_FLAGS (0xDFF) /* FUNCTIONS *****************************************************************/ ULONG KeV86GPF(PKV86M_TRAP_FRAME VTf, PKTRAP_FRAME Tf) { PUCHAR ip; PUSHORT sp; PULONG dsp; BOOL BigDataPrefix = FALSE; BOOL BigAddressPrefix = FALSE; BOOL RepPrefix = FALSE; ULONG i = 0; BOOL Exit = FALSE; ip = (PUCHAR)((Tf->Cs & 0xFFFF) * 16 + (Tf->Eip & 0xFFFF)); sp = (PUSHORT)((Tf->Ss & 0xFFFF) * 16 + (Tf->Esp & 0xFFFF)); dsp = (PULONG)sp; DPRINT("KeV86GPF handling %x at %x:%x ss:sp %x:%x Flags %x\n", ip[0], Tf->Cs, Tf->Eip, Tf->Ss, Tf->Esp, VTf->regs->Flags); while (!Exit) { switch (ip[i]) { /* 32-bit data prefix */ case 0x66: BigDataPrefix = TRUE; i++; Tf->Eip++; break; /* 32-bit address prefix */ case 0x67: BigAddressPrefix = TRUE; i++; Tf->Eip++; break; /* rep prefix */ case 0xFC: RepPrefix = TRUE; i++; Tf->Eip++; break; /* sti */ case 0xFB: if (BigDataPrefix || BigAddressPrefix || RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_EMULATE_CLI_STI) { Tf->Eip++; VTf->regs->Vif = 1; return(0); } Exit = TRUE; break; /* cli */ case 0xFA: if (BigDataPrefix || BigAddressPrefix || RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_EMULATE_CLI_STI) { Tf->Eip++; VTf->regs->Vif = 0; return(0); } Exit = TRUE; break; /* pushf */ case 0x9C: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_EMULATE_CLI_STI) { Tf->Eip++; if (!BigAddressPrefix) { Tf->Esp = Tf->Esp - 2; sp = sp - 1; sp[0] = Tf->Eflags & 0xFFFF; if (VTf->regs->Vif == 1) { sp[0] = sp[0] | INTERRUPT_FLAG; } else { sp[0] = sp[0] & (~INTERRUPT_FLAG); } } else { Tf->Esp = Tf->Esp - 4; dsp = dsp - 1; dsp[0] = Tf->Eflags; dsp[0] = dsp[0] & VALID_FLAGS; if (VTf->regs->Vif == 1) { dsp[0] = dsp[0] | INTERRUPT_FLAG; } else { dsp[0] = dsp[0] & (~INTERRUPT_FLAG); } } return(0); } Exit = TRUE; break; /* popf */ case 0x9D: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_EMULATE_CLI_STI) { Tf->Eip++; if (!BigAddressPrefix) { Tf->Eflags = Tf->Eflags & (~0xFFFF); Tf->Eflags = Tf->Eflags | (sp[0] & VALID_FLAGS); if (Tf->Eflags & INTERRUPT_FLAG) { VTf->regs->Vif = 1; } else { VTf->regs->Vif = 0; } Tf->Eflags = Tf->Eflags | INTERRUPT_FLAG; Tf->Esp = Tf->Esp + 2; } else { Tf->Eflags = Tf->Eflags | (dsp[0] & VALID_FLAGS); if (dsp[0] & INTERRUPT_FLAG) { VTf->regs->Vif = 1; } else { VTf->regs->Vif = 0; } Tf->Eflags = Tf->Eflags | INTERRUPT_FLAG; Tf->Esp = Tf->Esp + 2; } return(0); } Exit = TRUE; break; /* iret */ case 0xCF: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_EMULATE_CLI_STI) { Tf->Eip = sp[0]; Tf->Cs = sp[1]; Tf->Eflags = Tf->Eflags & (~0xFFFF); Tf->Eflags = Tf->Eflags | sp[2]; if (Tf->Eflags & INTERRUPT_FLAG) { VTf->regs->Vif = 1; } else { VTf->regs->Vif = 0; } Tf->Eflags = Tf->Eflags & (~INTERRUPT_FLAG); Tf->Esp = Tf->Esp + 6; return(0); } Exit = TRUE; break; /* out imm8, al */ case 0xE6: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { WRITE_PORT_UCHAR((PUCHAR)(ULONG)ip[i + 1], Tf->Eax & 0xFF); Tf->Eip = Tf->Eip + 2; return(0); } Exit = TRUE; break; /* out imm8, ax */ case 0xE7: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { if (!BigDataPrefix) { WRITE_PORT_USHORT((PUSHORT)(ULONG)ip[1], Tf->Eax & 0xFFFF); } else { WRITE_PORT_ULONG((PULONG)(ULONG)ip[1], Tf->Eax); } Tf->Eip = Tf->Eip + 2; return(0); } Exit = TRUE; break; /* out dx, al */ case 0xEE: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { WRITE_PORT_UCHAR((PUCHAR)(Tf->Edx & 0xFFFF), Tf->Eax & 0xFF); Tf->Eip = Tf->Eip + 1; return(0); } Exit = TRUE; break; /* out dx, ax */ case 0xEF: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { if (!BigDataPrefix) { WRITE_PORT_USHORT((PUSHORT)(Tf->Edx & 0xFFFF), Tf->Eax & 0xFFFF); } else { WRITE_PORT_ULONG((PULONG)(Tf->Edx & 0xFFFF), Tf->Eax); } Tf->Eip = Tf->Eip + 1; return(0); } Exit = TRUE; break; /* in al, imm8 */ case 0xE4: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { UCHAR v; v = READ_PORT_UCHAR((PUCHAR)(ULONG)ip[1]); Tf->Eax = Tf->Eax & (~0xFF); Tf->Eax = Tf->Eax | v; Tf->Eip = Tf->Eip + 2; return(0); } Exit = TRUE; break; /* in ax, imm8 */ case 0xE5: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { USHORT v; v = READ_PORT_USHORT((PUSHORT)(ULONG)ip[1]); Tf->Eax = Tf->Eax & (~0xFFFF); Tf->Eax = Tf->Eax | v; Tf->Eip = Tf->Eip + 2; return(0); } Exit = TRUE; break; /* in al, dx */ case 0xEC: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { UCHAR v; v = READ_PORT_UCHAR((PUCHAR)(Tf->Edx & 0xFFFF)); Tf->Eax = Tf->Eax & (~0xFF); Tf->Eax = Tf->Eax | v; Tf->Eip = Tf->Eip + 1; return(0); } Exit = TRUE; break; /* in ax, dx */ case 0xED: if (RepPrefix) { *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { USHORT v; v = READ_PORT_USHORT((PUSHORT)(Tf->Edx & 0xFFFF)); Tf->Eax = Tf->Eax & (~0xFFFF); Tf->Eax = Tf->Eax | v; Tf->Eip = Tf->Eip + 1; return(0); } Exit = TRUE; break; /* outsb */ case 0x6E: if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { ULONG Count; PUCHAR Port; PUCHAR Buffer; ULONG Offset; Count = 1; if (RepPrefix) { Count = Tf->Ecx; if (!BigAddressPrefix) { Count = Count & 0xFFFF; } } Port = (PUCHAR)(Tf->Edx & 0xFFFF); Offset = Tf->Edi; if (!BigAddressPrefix) { Offset = Offset & 0xFFFF; } Buffer = (PUCHAR)((Tf->Es * 16) + Offset); for (; Count > 0; Count--) { WRITE_PORT_UCHAR(Port, *Buffer); if (Tf->Eflags & DIRECTION_FLAG) { Buffer++; } else { Buffer--; } } Tf->Eip++; return(0); } Exit = TRUE; break; /* insw/insd */ case 0x6F: if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { ULONG Count; PUCHAR Port; PUSHORT BufferS = NULL; PULONG BufferL = NULL; ULONG Offset; Count = 1; if (RepPrefix) { Count = Tf->Ecx; if (!BigAddressPrefix) { Count = Count & 0xFFFF; } } Port = (PUCHAR)(Tf->Edx & 0xFFFF); Offset = Tf->Edi; if (!BigAddressPrefix) { Offset = Offset & 0xFFFF; } if (BigDataPrefix) { BufferL = (PULONG)((Tf->Es * 16) + Offset); } else { BufferS = (PUSHORT)((Tf->Es * 16) + Offset); } for (; Count > 0; Count--) { if (BigDataPrefix) { WRITE_PORT_ULONG((PULONG)Port, *BufferL); } else { WRITE_PORT_USHORT((PUSHORT)Port, *BufferS); } if (Tf->Eflags & DIRECTION_FLAG) { if (BigDataPrefix) { BufferL++; } else { BufferS++; } } else { if (BigDataPrefix) { BufferL--; } else { BufferS--; } } } Tf->Eip++; return(0); } Exit = TRUE; break; /* insb */ case 0x6C: if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { ULONG Count; PUCHAR Port; PUCHAR Buffer; ULONG Offset; Count = 1; if (RepPrefix) { Count = Tf->Ecx; if (!BigAddressPrefix) { Count = Count & 0xFFFF; } } Port = (PUCHAR)(Tf->Edx & 0xFFFF); Offset = Tf->Edi; if (!BigAddressPrefix) { Offset = Offset & 0xFFFF; } Buffer = (PUCHAR)((Tf->Es * 16) + Offset); for (; Count > 0; Count--) { *Buffer = READ_PORT_UCHAR(Port); if (Tf->Eflags & DIRECTION_FLAG) { Buffer++; } else { Buffer--; } } Tf->Eip++; return(0); } Exit = TRUE; break; /* insw/insd */ case 0x6D: if (VTf->regs->Flags & KV86M_ALLOW_IO_PORT_ACCESS) { ULONG Count; PUCHAR Port; PUSHORT BufferS = NULL; PULONG BufferL = NULL; ULONG Offset; Count = 1; if (RepPrefix) { Count = Tf->Ecx; if (!BigAddressPrefix) { Count = Count & 0xFFFF; } } Port = (PUCHAR)(Tf->Edx & 0xFFFF); Offset = Tf->Edi; if (!BigAddressPrefix) { Offset = Offset & 0xFFFF; } if (BigDataPrefix) { BufferL = (PULONG)((Tf->Es * 16) + Offset); } else { BufferS = (PUSHORT)((Tf->Es * 16) + Offset); } for (; Count > 0; Count--) { if (BigDataPrefix) { *BufferL = READ_PORT_ULONG((PULONG)Port); } else { *BufferS = READ_PORT_USHORT((PUSHORT)Port); } if (Tf->Eflags & DIRECTION_FLAG) { if (BigDataPrefix) { BufferL++; } else { BufferS++; } } else { if (BigDataPrefix) { BufferL--; } else { BufferS--; } } } Tf->Eip++; return(0); } Exit = TRUE; break; /* Int nn */ case 0xCD: { unsigned int inum; unsigned int entry; inum = ip[1]; entry = ((unsigned int *)0)[inum]; Tf->Esp = Tf->Esp - 6; sp = sp - 3; sp[0] = (Tf->Eip & 0xFFFF) + 2; sp[1] = Tf->Cs & 0xFFFF; sp[2] = Tf->Eflags & 0xFFFF; if (VTf->regs->Vif == 1) { sp[2] = sp[2] | INTERRUPT_FLAG; } DPRINT("sp[0] %x sp[1] %x sp[2] %x\n", sp[0], sp[1], sp[2]); Tf->Eip = entry & 0xFFFF; Tf->Cs = entry >> 16; Tf->Eflags = Tf->Eflags & (~TRAP_FLAG); return(0); } } /* FIXME: Also emulate ins and outs */ /* FIXME: Handle opcode prefixes */ /* FIXME: Don't allow the BIOS to write to sensitive I/O ports */ } DPRINT1("V86GPF unhandled (was %x)\n", ip[i]); *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } ULONG KeV86Exception(ULONG ExceptionNr, PKTRAP_FRAME Tf, ULONG address) { PUCHAR Ip; PKV86M_TRAP_FRAME VTf; VTf = (PKV86M_TRAP_FRAME)Tf; /* * Check if we have reached the recovery instruction */ Ip = (PUCHAR)((Tf->Cs & 0xFFFF) * 16 + (Tf->Eip & 0xFFFF)); if (ExceptionNr != 14) { DPRINT("ExceptionNr %d Ip[0] %x Ip[1] %x Ip[2] %x Ip[3] %x Tf->Cs %x " "Tf->Eip %x\n", ExceptionNr, Ip[0], Ip[1], Ip[2], Ip[3], Tf->Cs, Tf->Eip); DPRINT("VTf %x VTf->regs %x\n", VTf, VTf->regs); } if (ExceptionNr == 6 && memcmp(Ip, VTf->regs->RecoveryInstruction, 4) == 0 && (Tf->Cs * 16 + Tf->Eip) == VTf->regs->RecoveryAddress) { *VTf->regs->PStatus = STATUS_SUCCESS; return(1); } /* * Handle the exceptions */ switch (ExceptionNr) { /* Divide error */ case 0: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Single step */ case 1: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* NMI */ case 2: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Breakpoint */ case 3: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Overflow */ case 4: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Array bounds check */ case 5: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Invalid opcode */ case 6: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Device not available */ case 7: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Double fault */ case 8: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Intel reserved */ case 9: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Invalid TSS */ case 10: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Segment not present */ case 11: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION;; return(1); /* Stack fault */ case 12: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* General protection fault */ case 13: return(KeV86GPF(VTf, Tf)); /* Page fault */ case 14: { NTSTATUS Status; Status = MmPageFault(USER_CS, &Tf->Eip, NULL, address, Tf->ErrorCode); if (!NT_SUCCESS(Status)) { DPRINT("V86Exception, halting due to page fault\n"); *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } return(0); } /* Intel reserved */ case 15: case 16: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); /* Alignment check */ case 17: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); default: *VTf->regs->PStatus = STATUS_NONCONTINUABLE_EXCEPTION; return(1); } }