2 * FILE: ntoskrnl/ke/i386/v86m_sup.S
3 * COPYRIGHT: See COPYING in the top level directory
4 * PURPOSE: Virtual 8086 (V86) Mode Support
5 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
6 * NOTE: See asmmacro.S for the V86 trap code.
9 /* INCLUDES ******************************************************************/
12 #include <internal/i386/asmmacro.S>
14 .intel_syntax noprefix
16 /* FIXME: Can we make a nice macro to generate V86 Opcode handlers? */
18 /* GLOBALS *******************************************************************/
21 // This table contains indexes into the OpcodeDispatchV86 Table for opcodes in
23 // There are 256 entries.
26 INVALID_V86_OPCODE 15 /* OP 00-14: UNHANDLED */
27 .byte 1 /* OP 0F: 0F */
28 INVALID_V86_OPCODE 22 /* OP 10-25: UNHANDLED */
29 .byte 2 /* OP 26: ES Prefix */
30 INVALID_V86_OPCODE 7 /* OP 27-2D: UNHANDLED */
31 .byte 3 /* OP 2E: CS Prefix */
32 INVALID_V86_OPCODE 7 /* OP 2F-35: UNHANDLED */
33 .byte 4 /* OP 36: SS Prefix */
34 INVALID_V86_OPCODE 7 /* OP 37-3D: UNHANDLED */
35 .byte 5 /* OP 3E: DS Prefix */
36 INVALID_V86_OPCODE 37 /* OP 3F-63: UNHANDLED */
37 .byte 6 /* OP 64: FS Prefix */
38 .byte 7 /* OP 65: GS Prefix */
39 .byte 8 /* OP 66: OPER32 Prefix */
40 .byte 9 /* OP 67: ADDR32 Prefix */
41 INVALID_V86_OPCODE 4 /* OP 68-6B: UNHANDLED */
42 .byte 10 /* OP 6C: INSB */
43 .byte 11 /* OP 6D: INSW */
44 .byte 12 /* OP 6E: OUTSB */
45 .byte 13 /* OP 6F: OUTSW */
46 INVALID_V86_OPCODE 43 /* OP 70-9A: UNHANDLED */
47 .byte 19 /* OP 9B: NPX */
48 .byte 14 /* OP 9C: PUSHF */
49 .byte 15 /* OP 9D: POPF */
50 INVALID_V86_OPCODE 47 /* OP 9E-CC: UNHANDLED */
51 .byte 16 /* OP CD: INTnn */
52 .byte 17 /* OP CE: INTO */
53 .byte 18 /* OP CF: IRETD */
54 INVALID_V86_OPCODE 8 /* OP D0-D7: UNHANDLED */
55 .byte 19 /* OP D8: NPX */
56 .byte 19 /* OP D9: NPX */
57 .byte 19 /* OP DA: NPX */
58 .byte 19 /* OP DB: NPX */
59 .byte 19 /* OP DC: NPX */
60 .byte 19 /* OP DD: NPX */
61 .byte 19 /* OP DE: NPX */
62 .byte 19 /* OP DF: NPX */
63 INVALID_V86_OPCODE 4 /* OP DE-E3: UNHANDLED */
64 .byte 20 /* OP E4: INBimm */
65 .byte 21 /* OP E5: INWimm */
66 .byte 22 /* OP E6: OUTBimm */
67 .byte 23 /* OP E7: OUTWimm */
68 INVALID_V86_OPCODE 4 /* OP E8-EB: UNHANDLED */
69 .byte 24 /* OP EC: INB */
70 .byte 25 /* OP EF: INW */
71 .byte 26 /* OP EE: OUTB */
72 .byte 27 /* OP EF: OUTW */
73 .byte 28 /* OP F0: LOCK Prefix */
74 .byte 0 /* OP F1: UNHANDLED */
75 .byte 29 /* OP F2: REPNE Prefix */
76 .byte 30 /* OP F3: REP Prefix */
77 .byte 33 /* OP F4: HLT */
78 INVALID_V86_OPCODE 5 /* OP F5-F9: UNHANDLED */
79 .byte 31 /* OP FA: CLI */
80 .byte 32 /* OP FB: STI */
81 INVALID_V86_OPCODE 4 /* OP FC-FF: UNHANDLED */
84 // This table contains the emulation routines for
85 // Virtual-8086 Mode. There are 34 entries.
88 .long _OpcodeInvalidV86
90 .long _OpcodeESPrefixV86
91 .long _OpcodeCSPrefixV86
92 .long _OpcodeSSPrefixV86
93 .long _OpcodeDSPrefixV86
94 .long _OpcodeFSPrefixV86
95 .long _OpcodeGSPrefixV86
96 .long _OpcodeOPER32PrefixV86
97 .long _OpcodeADDR32PrefixV86
100 .long _OpcodeOUTSBV86
101 .long _OpcodeOUTSWV86
102 .long _OpcodePUSHFV86
104 .long _OpcodeINTnnV86
108 .long _OpcodeINBimmV86
109 .long _OpcodeINWimmV86
110 .long _OpcodeOUTBimmV86
111 .long _OpcodeOUTWimmV86
116 .long _OpcodeLOCKPrefixV86
117 .long _OpcodeREPNEPrefixV86
118 .long _OpcodeREPPrefixV86
123 _ExVdmOpcodeDispatchCounts:
129 .asciz "Received V86 Emulation Opcode: %lx\n"
131 /* VIRTUAL-8086 MODE OPCODER HANDLERS ****************************************/
133 .func OpcodeInvalidV86
143 GENERATE_PREFIX_HANDLER ES
144 GENERATE_PREFIX_HANDLER CS
145 GENERATE_PREFIX_HANDLER DS
146 GENERATE_PREFIX_HANDLER FS
147 GENERATE_PREFIX_HANDLER GS
148 GENERATE_PREFIX_HANDLER SS
149 GENERATE_PREFIX_HANDLER OPER32
150 GENERATE_PREFIX_HANDLER ADDR32
151 GENERATE_PREFIX_HANDLER LOCK
152 GENERATE_PREFIX_HANDLER REP
153 GENERATE_PREFIX_HANDLER REPNE
179 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
182 /* Get EFLAGS and mask out IF */
183 mov edx, [ebp+KTRAP_FRAME_EFLAGS]
184 and eax, ~EFLAGS_INTERRUPT_MASK
186 /* Mask align check and interrupt mask */
187 and eax, EFLAGS_ALIGN_CHECK + EFLAGS_NESTED_TASK + EFLAGS_INTERRUPT_MASK
194 movzx ecx, word ptr [ebp+KTRAP_FRAME_SS]
196 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
199 /* Check if there is an OPER32 prefix */
200 test ebx, PREFIX_FLAG_OPER32
208 /* Update ESP and EIP */
209 mov [ebp+KTRAP_FRAME_ESP], dx
210 add [ebp+KTRAP_FRAME_EIP], edi
218 /* Skip the prefix, push EFLAGS and jump back */
228 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
231 mov ecx, [ebp+KTRAP_FRAME_SS]
233 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
239 /* Check for OPER32 prefix */
240 test ebx, PREFIX_FLAG_OPER32
250 mov [ebp+KTRAP_FRAME_ESP], edx
252 /* Mask out EFLAGS */
253 and eax, ~EFLAGS_IOPL
255 and ebx, ~EFLAGS_NESTED_TASK
256 and ecx, EFLAGS_ALIGN_CHECK + EFLAGS_NESTED_TASK + EFLAGS_INTERRUPT_MASK
258 /* FIXME: Support VME */
260 /* Save VDM State pointer */
263 /* Set new EFLAGS, make sure to add IF and V86 */
264 or ebx, EFLAGS_INTERRUPT_MASK + EFLAGS_V86_MASK
265 push [ebp+KTRAP_FRAME_EFLAGS]
266 mov [ebp+KTRAP_FRAME_EFLAGS], ebx
268 /* Make sure we were in V86 mode */
269 test ebx, EFLAGS_V86_MASK
275 /* Check if we have to update ESP0 and fixup the stack from our push */
276 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
282 call _Ki386AdjustEsp0@4
286 /* Restore VDM state */
289 /* Update the flags in the VDM State */
290 LOCK and dword ptr [eax], ~(EFLAGS_ALIGN_CHECK + EFLAGS_NESTED_TASK + EFLAGS_INTERRUPT_MASK)
294 add [ebp+KTRAP_FRAME_EIP], edi
296 /* FIXME: Check for VDM Pending interrupts */
307 mov edx, [ebp+KTRAP_FRAME_EFLAGS]
309 /* Remove the flag in the VDM State */
310 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
312 LOCK and dword ptr [eax], ~EFLAGS_INTERRUPT_MASK
314 /* Mask it out from EFLAGS too */
316 and eax, ~EFLAGS_INTERRUPT_MASK
318 /* Mask out the alignment check and IF flag from the VDM state */
319 and ecx, EFLAGS_ALIGN_CHECK + EFLAGS_INTERRUPT_MASK
321 /* FIXME: Support VME */
323 /* Now mask out VIF and TF */
325 and edx, ~(EFLAGS_VIF + EFLAGS_NESTED_TASK + EFLAGS_TF)
326 mov [ebp+KTRAP_FRAME_EFLAGS], edx
328 /* Set the IOPL Mask */
331 /* Get stack flat address */
332 movzx ecx, word ptr [ebp+KTRAP_FRAME_SS]
334 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
338 mov word ptr [ecx+edx], ax
341 mov ax, word ptr [ebp+KTRAP_FRAME_CS]
343 mov word ptr [ecx+edx], ax
346 movzx eax, word ptr [ebp+KTRAP_FRAME_EIP]
350 mov word ptr [ecx+edx], ax
353 mov [ebp+KTRAP_FRAME_ESP], dx
355 /* Get the interrupt */
357 movzx ecx, byte ptr [esi]
358 /* FIXME: Analyze and see if this is a hooked VDM (PM) Interrupt */
360 /* Get the entry in the IVT */
366 mov word ptr [ebp+KTRAP_FRAME_EIP], bx
368 /* Check if this was V86 mode */
369 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
372 /* Check if it was a kernel CS */
377 /* Set user-mode CS */
378 mov ax, KGDT_R3_CODE + RPL_MASK
382 mov [ebp+KTRAP_FRAME_CS], ax
397 /* Get the VDM State */
398 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
401 movzx ecx, word ptr [ebp+KTRAP_FRAME_SS]
403 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
406 /* Check for OPER32 prefix */
407 test ebx, PREFIX_FLAG_OPER32
411 movzx edi, word ptr [ecx]
412 mov [ebp+KTRAP_FRAME_EIP], edi
413 movzx esi, word ptr [ecx+2]
414 mov [ebp+KTRAP_FRAME_CS], esi
418 mov [ebp+KTRAP_FRAME_ESP], edx
421 movzx ebx, word ptr [ecx+4]
425 /* Mask out EFLAGS */
426 and ebx, ~(EFLAGS_IOPL + EFLAGS_VIF + EFLAGS_NESTED_TASK + EFLAGS_VIP)
429 /* FIXME: Check for VME support */
434 /* Enable V86 and Interrupts */
435 or ebx, EFLAGS_V86_MASK + EFLAGS_INTERRUPT_MASK
438 mov eax, [ebp+KTRAP_FRAME_EFLAGS]
441 /* Mask out VIP and set new eflags */
444 mov [ebp+KTRAP_FRAME_EFLAGS], eax
446 /* Check if we have to update ESP0 */
448 test ebx, EFLAGS_V86_MASK
451 /* Save ECX and ECX */
455 /* Update esp0 and restore registers */
457 call _Ki386AdjustEsp0@4
463 /* Put VDM state in EAX and update VDM EFlags */
465 and ecx, EFLAGS_INTERRUPT_MASK
466 LOCK and dword ptr [eax], ~EFLAGS_INTERRUPT_MASK
469 /* Get flat EIP and check if this is the BOP */
476 /* FIXME: Check for VDM interrupts */
485 /* Call the BOP handler */
487 call _VdmDispatchBop@4
492 /* Get 32-bit flat EIP */
494 mov [ebp+KTRAP_FRAME_EIP], edi
495 movzx esi, word ptr [ecx+4]
496 mov [ebp+KTRAP_FRAME_CS], esi
500 mov [ebp+KTRAP_FRAME_ESP], edx
502 /* Get EFLAGS and continue */
512 .func OpcodeINBimmV86
517 .func OpcodeINWimmV86
522 .func OpcodeOUTBimmV86
527 .func OpcodeOUTWimmV86
556 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
558 /* FIXME: Support VME */
560 /* FIXME: Support VDM Interrupts */
562 /* Disable interrupts */
563 LOCK and dword ptr [eax], ~EFLAGS_INTERRUPT_MASK
565 /* Update EIP (remember EDI == instruction size) */
566 add [ebp+KTRAP_FRAME_EIP], edi
577 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
579 /* FIXME: Support VME */
581 /* Enable interrupts */
582 LOCK or dword ptr [eax], EFLAGS_INTERRUPT_MASK
584 /* Update EIP (remember EDI == instruction size) */
585 add [ebp+KTRAP_FRAME_EIP], edi
587 /* FIXME: Support VDM Interrupts */
594 .func OpcodeGenericPrefixV86
595 _OpcodeGenericPrefixV86:
597 /* Skip instruction */
601 /* Get the instruction */
602 movzx ecx, byte ptr [esi]
604 /* Get the opcode index */
605 movzx edx, byte ptr OpcodeIndex[ecx]
608 jmp OpcodeDispatchV86[edx*4]
616 /* FUNCTIONS *****************************************************************/
618 .globl _Ki386SetupAndExitToV86Mode@4
619 .func Ki386SetupAndExitToV86Mode@4
620 _Ki386SetupAndExitToV86Mode@4:
622 /* Save nonvolatiles */
628 /* Give us a little stack */
632 /* Go past the KTRAP_FRAME and NPX Frame and set a new frame in EAX */
633 sub esp, NPX_FRAME_LENGTH
635 sub esp, KTRAP_FRAME_LENGTH
638 /* Create a fake user-mode frame */
639 mov dword ptr [eax+KTRAP_FRAME_CS], KGDT_R0_CODE + RPL_MASK
640 mov dword ptr [eax+KTRAP_FRAME_ES], 0
641 mov dword ptr [eax+KTRAP_FRAME_DS], 0
642 mov dword ptr [eax+KTRAP_FRAME_FS], 0
643 mov dword ptr [eax+KTRAP_FRAME_GS], 0
644 mov dword ptr [eax+KTRAP_FRAME_ERROR_CODE], 0
646 /* Get the current thread's initial stack */
647 mov ebx, [fs:KPCR_SELF]
648 mov edi, [ebx+KPCR_CURRENT_THREAD]
649 mov edx, [edi+KTHREAD_INITIAL_STACK]
650 sub edx, NPX_FRAME_LENGTH
652 /* Save it on our stack, as well as the real TEB addresses */
654 mov edx, [edi+KTHREAD_TEB]
656 mov edx, [fs:KPCR_TEB]
659 /* Set our ESP in ESI, and the return function in EIP */
660 mov edi, offset _Ki386BiosCallReturnAddress
661 mov [eax+KTRAP_FRAME_ESI], ecx
662 mov [eax+KTRAP_FRAME_EIP], edi
664 /* Push the flags and sanitize them */
668 or edi, EFLAGS_INTERRUPT_MASK
670 /* Set SS and ESP, and fill out the rest of the frame */
671 mov dword ptr [eax+KTRAP_FRAME_SS], KGDT_R3_DATA + RPL_MASK;
672 mov dword ptr [eax+KTRAP_FRAME_ESP], 0x11FFE;
673 mov dword ptr [eax+KTRAP_FRAME_EFLAGS], edi
674 mov dword ptr [eax+KTRAP_FRAME_EXCEPTION_LIST], -1
675 mov dword ptr [eax+KTRAP_FRAME_PREVIOUS_MODE], -1
676 mov dword ptr [eax+KTRAP_FRAME_DR7], 0
677 mov dword ptr [eax+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
679 /* Jump past the frame now */
680 add eax, KTRAP_FRAME_LENGTH
683 /* Save the current stack */
686 /* Get the current thread's intial stack again */
687 mov edi, [ebx+KPCR_CURRENT_THREAD]
688 mov esi, [edi+KTHREAD_INITIAL_STACK]
689 sub esi, NPX_FRAME_LENGTH
691 /* Set the size of the copy, and the destination, and copy the NPX frame */
692 mov ecx, NPX_FRAME_LENGTH / 4
699 /* Get the current thread and TSS */
700 mov edi, [ebx+KPCR_CURRENT_THREAD]
701 mov esi, [ebx+KPCR_TSS]
703 /* Bias the V86 vrame */
704 sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
706 /* Set exception list and new ESP */
707 mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
708 mov [esi+KTSS_ESP0], eax
710 /* Now skip past the NPX frame and V86 fields and set this as the intial stack */
711 add eax, NPX_FRAME_LENGTH + (KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS)
712 mov [edi+KTHREAD_INITIAL_STACK], eax
714 /* Setup our fake TEB pointer */
716 mov [fs:KPCR_TEB], eax
717 mov [edi+KTHREAD_TEB], eax
719 /* Setup the descriptors for the fake TEB */
720 mov ebx, [fs:KPCR_GDT]
728 * Start VDM execution. This will save this fake 32-bit KTRAP_FRAME and
729 * initialize a real 16-bit VDM context frame
732 push 0 // VdmStartExecution
735 /* Exit to V86 mode */
737 jmp _Kei386EoiHelper@0
740 .globl _Ki386BiosCallReturnAddress
741 .func Ki386BiosCallReturnAddress
742 _Ki386BiosCallReturnAddress:
745 mov eax, [fs:KPCR_SELF]
747 /* Get NPX destination */
748 mov edi, [ebp+KTRAP_FRAME_ESI]
751 /* Get initial stack */
752 mov ecx, [eax+KPCR_CURRENT_THREAD]
753 mov esi, [ecx+KTHREAD_INITIAL_STACK]
754 sub esi, NPX_FRAME_LENGTH
756 /* Set length and copy the NPX frame */
757 mov ecx, NPX_FRAME_LENGTH / 4
761 mov esp, [ebp+KTRAP_FRAME_ESI]
764 /* Set initial stack */
765 mov ecx, [eax+KPCR_CURRENT_THREAD]
766 mov [ecx+KTHREAD_INITIAL_STACK], edi
768 /* Get TSS and set the ESP 0 */
769 mov eax, [eax+KPCR_TSS]
770 sub edi, NPX_FRAME_LENGTH + (KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS)
771 mov [eax+KTSS_ESP0], edi
773 /* Restore KTHREAD TEB in EDX */
775 mov [ecx+KTHREAD_TEB], edx
777 /* Restore PCR TEB in EDX */
779 mov [fs:KPCR_TEB], edx
781 /* Setup the descriptors for the real TEB */
782 mov ebx, [fs:KPCR_GDT]
788 /* Enable interrupts and pop back non-volatiles */
797 .globl _Ki386HandleOpcodeV86@0
798 .func Ki386HandleOpcodeV86@0
799 _Ki386HandleOpcodeV86@0:
802 mov esi, [ebp+KTRAP_FRAME_CS]
804 add esi, [ebp+KTRAP_FRAME_EIP]
806 /* Get the opcode entry in the table */
807 movzx ecx, byte ptr [esi]
808 movzx edx, byte ptr OpcodeIndex[ecx]
810 /* Set instruction length and prefix flags */
814 /* Accounting statistics */
815 // inc dword ptr _ExVdmOpcodeDispatchCounts[edx*4] // FIXME: Generates protection fault
817 /* Handle the opcode */
818 jmp OpcodeDispatchV86[edx*4]