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>
13 .intel_syntax noprefix
15 /* FIXME: Can we make a nice macro to generate V86 Opcode handlers? */
17 /* GLOBALS *******************************************************************/
20 // This table contains indexes into the OpcodeDispatchV86 Table for opcodes in
22 // There are 256 entries.
25 INVALID_V86_OPCODE 15 /* OP 00-14: UNHANDLED */
26 .byte 1 /* OP 0F: 0F */
27 INVALID_V86_OPCODE 22 /* OP 10-25: UNHANDLED */
28 .byte 2 /* OP 26: ES Prefix */
29 INVALID_V86_OPCODE 7 /* OP 27-2D: UNHANDLED */
30 .byte 3 /* OP 2E: CS Prefix */
31 INVALID_V86_OPCODE 7 /* OP 2F-35: UNHANDLED */
32 .byte 4 /* OP 36: SS Prefix */
33 INVALID_V86_OPCODE 7 /* OP 37-3D: UNHANDLED */
34 .byte 5 /* OP 3E: DS Prefix */
35 INVALID_V86_OPCODE 37 /* OP 3F-63: UNHANDLED */
36 .byte 6 /* OP 64: FS Prefix */
37 .byte 7 /* OP 65: GS Prefix */
38 .byte 8 /* OP 66: OPER32 Prefix */
39 .byte 9 /* OP 67: ADDR32 Prefix */
40 INVALID_V86_OPCODE 4 /* OP 68-6B: UNHANDLED */
41 .byte 10 /* OP 6C: INSB */
42 .byte 11 /* OP 6D: INSW */
43 .byte 12 /* OP 6E: OUTSB */
44 .byte 13 /* OP 6F: OUTSW */
45 INVALID_V86_OPCODE 43 /* OP 70-9A: UNHANDLED */
46 .byte 19 /* OP 9B: NPX */
47 .byte 14 /* OP 9C: PUSHF */
48 .byte 15 /* OP 9D: POPF */
49 INVALID_V86_OPCODE 47 /* OP 9E-CC: UNHANDLED */
50 .byte 16 /* OP CD: INTnn */
51 .byte 17 /* OP CE: INTO */
52 .byte 18 /* OP CF: IRETD */
53 INVALID_V86_OPCODE 8 /* OP D0-D7: UNHANDLED */
54 .byte 19 /* OP D8: NPX */
55 .byte 19 /* OP D9: NPX */
56 .byte 19 /* OP DA: NPX */
57 .byte 19 /* OP DB: NPX */
58 .byte 19 /* OP DC: NPX */
59 .byte 19 /* OP DD: NPX */
60 .byte 19 /* OP DE: NPX */
61 .byte 19 /* OP DF: NPX */
62 INVALID_V86_OPCODE 4 /* OP DE-E3: UNHANDLED */
63 .byte 20 /* OP E4: INBimm */
64 .byte 21 /* OP E5: INWimm */
65 .byte 22 /* OP E6: OUTBimm */
66 .byte 23 /* OP E7: OUTWimm */
67 INVALID_V86_OPCODE 4 /* OP E8-EB: UNHANDLED */
68 .byte 24 /* OP EC: INB */
69 .byte 25 /* OP EF: INW */
70 .byte 26 /* OP EE: OUTB */
71 .byte 27 /* OP EF: OUTW */
72 .byte 28 /* OP F0: LOCK Prefix */
73 .byte 0 /* OP F1: UNHANDLED */
74 .byte 29 /* OP F2: REPNE Prefix */
75 .byte 30 /* OP F3: REP Prefix */
76 .byte 33 /* OP F4: HLT */
77 INVALID_V86_OPCODE 5 /* OP F5-F9: UNHANDLED */
78 .byte 31 /* OP FA: CLI */
79 .byte 32 /* OP FB: STI */
80 INVALID_V86_OPCODE 4 /* OP FC-FF: UNHANDLED */
83 // This table contains the emulation routines for
84 // Virtual-8086 Mode. There are 34 entries.
87 .long _OpcodeInvalidV86
89 .long _OpcodeESPrefixV86
90 .long _OpcodeCSPrefixV86
91 .long _OpcodeSSPrefixV86
92 .long _OpcodeDSPrefixV86
93 .long _OpcodeFSPrefixV86
94 .long _OpcodeGSPrefixV86
95 .long _OpcodeOPER32PrefixV86
96 .long _OpcodeADDR32PrefixV86
100 .long _OpcodeOUTSWV86
101 .long _OpcodePUSHFV86
103 .long _OpcodeINTnnV86
107 .long _OpcodeINBimmV86
108 .long _OpcodeINWimmV86
109 .long _OpcodeOUTBimmV86
110 .long _OpcodeOUTWimmV86
115 .long _OpcodeLOCKPrefixV86
116 .long _OpcodeREPNEPrefixV86
117 .long _OpcodeREPPrefixV86
122 _ExVdmOpcodeDispatchCounts:
128 .asciz "Received V86 Emulation Opcode: %lx\n"
130 /* VIRTUAL-8086 MODE OPCODER HANDLERS ****************************************/
132 .func OpcodeInvalidV86
144 GENERATE_PREFIX_HANDLER ES
145 GENERATE_PREFIX_HANDLER CS
146 GENERATE_PREFIX_HANDLER DS
147 GENERATE_PREFIX_HANDLER FS
148 GENERATE_PREFIX_HANDLER GS
149 GENERATE_PREFIX_HANDLER SS
150 GENERATE_PREFIX_HANDLER OPER32
151 GENERATE_PREFIX_HANDLER ADDR32
152 GENERATE_PREFIX_HANDLER LOCK
153 GENERATE_PREFIX_HANDLER REP
154 GENERATE_PREFIX_HANDLER REPNE
186 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
189 /* Get EFLAGS and mask out IF */
190 mov edx, [ebp+KTRAP_FRAME_EFLAGS]
191 and eax, ~EFLAGS_INTERRUPT_MASK
193 /* Mask align check and interrupt mask */
194 and eax, EFLAGS_ALIGN_CHECK + EFLAGS_NESTED_TASK + EFLAGS_INTERRUPT_MASK
201 movzx ecx, word ptr [ebp+KTRAP_FRAME_SS]
203 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
206 /* Check if there is an OPER32 prefix */
207 test ebx, PREFIX_FLAG_OPER32
215 /* Update ESP and EIP */
216 mov [ebp+KTRAP_FRAME_ESP], dx
217 add [ebp+KTRAP_FRAME_EIP], edi
225 /* Skip the prefix, push EFLAGS and jump back */
235 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
238 mov ecx, [ebp+KTRAP_FRAME_SS]
240 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
246 /* Check for OPER32 prefix */
247 test ebx, PREFIX_FLAG_OPER32
257 mov [ebp+KTRAP_FRAME_ESP], edx
259 /* Mask out EFLAGS */
260 and eax, ~EFLAGS_IOPL
262 and ebx, ~EFLAGS_NESTED_TASK
263 and ecx, EFLAGS_ALIGN_CHECK + EFLAGS_NESTED_TASK + EFLAGS_INTERRUPT_MASK
265 /* FIXME: Support VME */
267 /* Save VDM State pointer */
270 /* Set new EFLAGS, make sure to add IF and V86 */
271 or ebx, EFLAGS_INTERRUPT_MASK + EFLAGS_V86_MASK
272 push [ebp+KTRAP_FRAME_EFLAGS]
273 mov [ebp+KTRAP_FRAME_EFLAGS], ebx
275 /* Make sure we were in V86 mode */
276 test ebx, EFLAGS_V86_MASK
282 /* Check if we have to update ESP0 and fixup the stack from our push */
283 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
289 call _Ki386AdjustEsp0@4
293 /* Restore VDM state */
296 /* Update the flags in the VDM State */
297 LOCK and dword ptr [eax], ~(EFLAGS_ALIGN_CHECK + EFLAGS_NESTED_TASK + EFLAGS_INTERRUPT_MASK)
301 add [ebp+KTRAP_FRAME_EIP], edi
303 /* FIXME: Check for VDM Pending interrupts */
314 mov edx, [ebp+KTRAP_FRAME_EFLAGS]
316 /* Remove the flag in the VDM State */
317 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
319 LOCK and dword ptr [eax], ~EFLAGS_INTERRUPT_MASK
321 /* Mask it out from EFLAGS too */
323 and eax, ~EFLAGS_INTERRUPT_MASK
325 /* Mask out the alignment check and IF flag from the VDM state */
326 and ecx, EFLAGS_ALIGN_CHECK + EFLAGS_INTERRUPT_MASK
328 /* FIXME: Support VME */
330 /* Now mask out VIF and TF */
332 and edx, ~(EFLAGS_VIF + EFLAGS_NESTED_TASK + EFLAGS_TF)
333 mov [ebp+KTRAP_FRAME_EFLAGS], edx
335 /* Set the IOPL Mask */
338 /* Get stack flat address */
339 movzx ecx, word ptr [ebp+KTRAP_FRAME_SS]
341 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
345 mov word ptr [ecx+edx], ax
348 mov ax, word ptr [ebp+KTRAP_FRAME_CS]
350 mov word ptr [ecx+edx], ax
353 movzx eax, word ptr [ebp+KTRAP_FRAME_EIP]
357 mov word ptr [ecx+edx], ax
360 mov [ebp+KTRAP_FRAME_ESP], dx
362 /* Get the interrupt */
364 movzx ecx, byte ptr [esi]
365 /* FIXME: Analyze and see if this is a hooked VDM (PM) Interrupt */
367 /* Get the entry in the IVT */
373 mov word ptr [ebp+KTRAP_FRAME_EIP], bx
375 /* Check if this was V86 mode */
376 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
379 /* Check if it was a kernel CS */
384 /* Set user-mode CS */
385 mov ax, KGDT_R3_CODE + RPL_MASK
389 mov [ebp+KTRAP_FRAME_CS], ax
404 /* Get the VDM State */
405 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
408 movzx ecx, word ptr [ebp+KTRAP_FRAME_SS]
410 movzx edx, word ptr [ebp+KTRAP_FRAME_ESP]
413 /* Check for OPER32 prefix */
414 test ebx, PREFIX_FLAG_OPER32
418 movzx edi, word ptr [ecx]
419 mov [ebp+KTRAP_FRAME_EIP], edi
420 movzx esi, word ptr [ecx+2]
421 mov [ebp+KTRAP_FRAME_CS], esi
425 mov [ebp+KTRAP_FRAME_ESP], edx
428 movzx ebx, word ptr [ecx+4]
432 /* Mask out EFLAGS */
433 and ebx, ~(EFLAGS_IOPL + EFLAGS_VIF + EFLAGS_NESTED_TASK + EFLAGS_VIP)
436 /* FIXME: Check for VME support */
441 /* Enable V86 and Interrupts */
442 or ebx, EFLAGS_V86_MASK + EFLAGS_INTERRUPT_MASK
445 mov eax, [ebp+KTRAP_FRAME_EFLAGS]
448 /* Mask out VIP and set new eflags */
451 mov [ebp+KTRAP_FRAME_EFLAGS], eax
453 /* Check if we have to update ESP0 */
455 test ebx, EFLAGS_V86_MASK
458 /* Save ECX and ECX */
462 /* Update esp0 and restore registers */
464 call _Ki386AdjustEsp0@4
470 /* Put VDM state in EAX and update VDM EFlags */
472 and ecx, EFLAGS_INTERRUPT_MASK
473 LOCK and dword ptr [eax], ~EFLAGS_INTERRUPT_MASK
476 /* Get flat EIP and check if this is the BOP */
483 /* FIXME: Check for VDM interrupts */
492 /* Call the BOP handler */
494 call _VdmDispatchBop@4
499 /* Get 32-bit flat EIP */
501 mov [ebp+KTRAP_FRAME_EIP], edi
502 movzx esi, word ptr [ecx+4]
503 mov [ebp+KTRAP_FRAME_CS], esi
507 mov [ebp+KTRAP_FRAME_ESP], edx
509 /* Get EFLAGS and continue */
519 .func OpcodeINBimmV86
524 .func OpcodeINWimmV86
529 .func OpcodeOUTBimmV86
534 .func OpcodeOUTWimmV86
563 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
565 /* FIXME: Support VME */
567 /* FIXME: Support VDM Interrupts */
569 /* Disable interrupts */
570 LOCK and dword ptr [eax], ~EFLAGS_INTERRUPT_MASK
572 /* Update EIP (remember EDI == instruction size) */
573 add [ebp+KTRAP_FRAME_EIP], edi
584 mov eax, FIXED_NTVDMSTATE_LINEAR_PC_AT
586 /* FIXME: Support VME */
588 /* Enable interrupts */
589 LOCK or dword ptr [eax], EFLAGS_INTERRUPT_MASK
591 /* Update EIP (remember EDI == instruction size) */
592 add [ebp+KTRAP_FRAME_EIP], edi
594 /* FIXME: Support VDM Interrupts */
601 .func OpcodeGenericPrefixV86
602 _OpcodeGenericPrefixV86:
604 /* Skip instruction */
608 /* Get the instruction */
609 movzx ecx, byte ptr [esi]
611 /* Get the opcode index */
612 movzx edx, byte ptr OpcodeIndex[ecx]
615 jmp OpcodeDispatchV86[edx*4]
623 /* FUNCTIONS *****************************************************************/
625 .globl _Ki386SetupAndExitToV86Mode@4
626 .func Ki386SetupAndExitToV86Mode@4
627 _Ki386SetupAndExitToV86Mode@4:
629 /* Save nonvolatiles */
635 /* Give us a little stack */
639 /* Go past the KTRAP_FRAME and NPX Frame and set a new frame in EAX */
640 sub esp, NPX_FRAME_LENGTH
642 sub esp, KTRAP_FRAME_LENGTH
645 /* Create a fake user-mode frame */
646 mov dword ptr [eax+KTRAP_FRAME_CS], KGDT_R0_CODE + RPL_MASK
647 mov dword ptr [eax+KTRAP_FRAME_ES], 0
648 mov dword ptr [eax+KTRAP_FRAME_DS], 0
649 mov dword ptr [eax+KTRAP_FRAME_FS], 0
650 mov dword ptr [eax+KTRAP_FRAME_GS], 0
651 mov dword ptr [eax+KTRAP_FRAME_ERROR_CODE], 0
653 /* Get the current thread's initial stack */
654 mov ebx, [fs:KPCR_SELF]
655 mov edi, [ebx+KPCR_CURRENT_THREAD]
656 mov edx, [edi+KTHREAD_INITIAL_STACK]
657 sub edx, NPX_FRAME_LENGTH
659 /* Save it on our stack, as well as the real TEB addresses */
661 mov edx, [edi+KTHREAD_TEB]
663 mov edx, [fs:KPCR_TEB]
666 /* Set our ESP in ESI, and the return function in EIP */
667 mov edi, offset _Ki386BiosCallReturnAddress
668 mov [eax+KTRAP_FRAME_ESI], ecx
669 mov [eax+KTRAP_FRAME_EIP], edi
671 /* Push the flags and sanitize them */
675 or edi, EFLAGS_INTERRUPT_MASK
677 /* Set SS and ESP, and fill out the rest of the frame */
678 mov dword ptr [eax+KTRAP_FRAME_SS], KGDT_R3_DATA + RPL_MASK;
679 mov dword ptr [eax+KTRAP_FRAME_ESP], 0x11FFE;
680 mov dword ptr [eax+KTRAP_FRAME_EFLAGS], edi
681 mov dword ptr [eax+KTRAP_FRAME_EXCEPTION_LIST], -1
682 mov dword ptr [eax+KTRAP_FRAME_PREVIOUS_MODE], -1
683 mov dword ptr [eax+KTRAP_FRAME_DR7], 0
684 mov dword ptr [eax+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
686 /* Jump past the frame now */
687 add eax, KTRAP_FRAME_LENGTH
690 /* Save the current stack */
693 /* Get the current thread's intial stack again */
694 mov edi, [ebx+KPCR_CURRENT_THREAD]
695 mov esi, [edi+KTHREAD_INITIAL_STACK]
696 sub esi, NPX_FRAME_LENGTH
698 /* Set the size of the copy, and the destination, and copy the NPX frame */
699 mov ecx, NPX_FRAME_LENGTH / 4
706 /* Get the current thread and TSS */
707 mov edi, [ebx+KPCR_CURRENT_THREAD]
708 mov esi, [ebx+KPCR_TSS]
710 /* Bias the V86 vrame */
711 sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
713 /* Set exception list and new ESP */
714 mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
715 mov [esi+KTSS_ESP0], eax
717 /* Now skip past the NPX frame and V86 fields and set this as the intial stack */
718 add eax, NPX_FRAME_LENGTH + (KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS)
719 mov [edi+KTHREAD_INITIAL_STACK], eax
721 /* Setup our fake TEB pointer */
723 mov [fs:KPCR_TEB], eax
724 mov [edi+KTHREAD_TEB], eax
726 /* Setup the descriptors for the fake TEB */
727 mov ebx, [fs:KPCR_GDT]
735 * Start VDM execution. This will save this fake 32-bit KTRAP_FRAME and
736 * initialize a real 16-bit VDM context frame
739 push 0 // VdmStartExecution
742 /* Exit to V86 mode */
744 jmp _Kei386EoiHelper@0
747 .globl _Ki386BiosCallReturnAddress
748 .func Ki386BiosCallReturnAddress
749 _Ki386BiosCallReturnAddress:
752 mov eax, [fs:KPCR_SELF]
754 /* Get NPX destination */
755 mov edi, [ebp+KTRAP_FRAME_ESI]
758 /* Get initial stack */
759 mov ecx, [eax+KPCR_CURRENT_THREAD]
760 mov esi, [ecx+KTHREAD_INITIAL_STACK]
761 sub esi, NPX_FRAME_LENGTH
763 /* Set length and copy the NPX frame */
764 mov ecx, NPX_FRAME_LENGTH / 4
768 mov esp, [ebp+KTRAP_FRAME_ESI]
771 /* Set initial stack */
772 mov ecx, [eax+KPCR_CURRENT_THREAD]
773 mov [ecx+KTHREAD_INITIAL_STACK], edi
775 /* Get TSS and set the ESP 0 */
776 mov eax, [eax+KPCR_TSS]
777 sub edi, NPX_FRAME_LENGTH + (KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS)
778 mov [eax+KTSS_ESP0], edi
780 /* Restore KTHREAD TEB in EDX */
782 mov [ecx+KTHREAD_TEB], edx
784 /* Restore PCR TEB in EDX */
786 mov [fs:KPCR_TEB], edx
788 /* Setup the descriptors for the real TEB */
789 mov ebx, [fs:KPCR_GDT]
795 /* Enable interrupts and pop back non-volatiles */
804 .globl _Ki386HandleOpcodeV86@0
805 .func Ki386HandleOpcodeV86@0
806 _Ki386HandleOpcodeV86@0:
809 mov esi, [ebp+KTRAP_FRAME_CS]
811 add esi, [ebp+KTRAP_FRAME_EIP]
813 /* Get the opcode entry in the table */
814 movzx ecx, byte ptr [esi]
815 movzx edx, byte ptr OpcodeIndex[ecx]
817 /* Set instruction length and prefix flags */
821 /* Accounting statistics */
822 // inc dword ptr _ExVdmOpcodeDispatchCounts[edx*4] // FIXME: Generates protection fault
824 /* Handle the opcode */
825 jmp OpcodeDispatchV86[edx*4]