xref: /reactos/ntoskrnl/ke/i386/v86vdm.c (revision 8a978a17)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/ke/i386/v86vdm.c
5  * PURPOSE:         V8086 and VDM Trap Emulation
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  *                  Alex Ionescu (alex.ionescu@reactos.org)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define KiVdmGetInstructionSize(x) ((x) & 0xFF)
17 #define KiVdmGetPrefixFlags(x)     ((x) & 0xFFFFFF00)
18 
19 /* GLOBALS ********************************************************************/
20 
21 ULONG KeI386EFlagsAndMaskV86 = EFLAGS_USER_SANITIZE;
22 ULONG KeI386EFlagsOrMaskV86 = EFLAGS_INTERRUPT_MASK;
23 PVOID Ki386IopmSaveArea;
24 BOOLEAN KeI386VirtualIntExtensions = FALSE;
25 const PULONG KiNtVdmState = (PULONG)FIXED_NTVDMSTATE_LINEAR_PC_AT;
26 
27 /* UNHANDLED OPCODES **********************************************************/
28 
29 KiVdmUnhandledOpcode(F);
30 KiVdmUnhandledOpcode(OUTSW);
31 KiVdmUnhandledOpcode(OUTSB);
32 KiVdmUnhandledOpcode(INSB);
33 KiVdmUnhandledOpcode(INSW);
34 KiVdmUnhandledOpcode(NPX);
35 KiVdmUnhandledOpcode(INBimm);
36 KiVdmUnhandledOpcode(INWimm);
37 KiVdmUnhandledOpcode(OUTBimm);
38 KiVdmUnhandledOpcode(OUTWimm);
39 KiVdmUnhandledOpcode(INB);
40 KiVdmUnhandledOpcode(INW);
41 KiVdmUnhandledOpcode(OUTB);
42 KiVdmUnhandledOpcode(OUTW);
43 KiVdmUnhandledOpcode(HLT);
44 KiVdmUnhandledOpcode(INTO);
45 KiVdmUnhandledOpcode(INV);
46 
47 /* OPCODE HANDLERS ************************************************************/
48 
49 BOOLEAN
50 FASTCALL
51 KiVdmOpcodePUSHF(IN PKTRAP_FRAME TrapFrame,
52                  IN ULONG Flags)
53 {
54     ULONG Esp, V86EFlags, TrapEFlags;
55 
56     /* Get current V8086 flags and mask out interrupt flag */
57     V86EFlags = *KiNtVdmState;
58     V86EFlags &= ~EFLAGS_INTERRUPT_MASK;
59 
60     /* Get trap frame EFLags */
61     TrapEFlags = TrapFrame->EFlags;
62     /* Check for VME support */
63     if(KeI386VirtualIntExtensions)
64     {
65         /* Copy the virtual interrupt flag to the interrupt flag */
66         TrapEFlags &= ~EFLAGS_INTERRUPT_MASK;
67         if(TrapEFlags & EFLAGS_VIF)
68             TrapEFlags |= EFLAGS_INTERRUPT_MASK;
69     }
70     /* Leave only align, nested task and interrupt */
71     TrapEFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
72 
73     /* Add in those flags if they exist, and add in the IOPL flag */
74     V86EFlags |= TrapEFlags;
75     V86EFlags |= EFLAGS_IOPL;
76 
77     /* Build flat ESP */
78     Esp = (TrapFrame->HardwareSegSs << 4) + (USHORT)TrapFrame->HardwareEsp;
79 
80     /* Check for OPER32 */
81     if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
82     {
83         /* Save EFlags */
84         Esp -= 4;
85         *(PULONG)Esp = V86EFlags;
86     }
87     else
88     {
89         /* Save EFLags */
90         Esp -= 2;
91         *(PUSHORT)Esp = (USHORT)V86EFlags;
92     }
93 
94     /* Set new ESP and EIP */
95     TrapFrame->HardwareEsp = Esp - (TrapFrame->HardwareSegSs << 4);
96     TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
97 
98     /* We're done */
99     return TRUE;
100 }
101 
102 BOOLEAN
103 FASTCALL
104 KiVdmOpcodePOPF(IN PKTRAP_FRAME TrapFrame,
105                 IN ULONG Flags)
106 {
107     ULONG Esp, V86EFlags, EFlags, TrapEFlags;
108 
109     /* Build flat ESP */
110     Esp = (TrapFrame->HardwareSegSs << 4) + (USHORT)TrapFrame->HardwareEsp;
111 
112     /* Check for OPER32 */
113     if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
114     {
115         /* Read EFlags */
116         EFlags = *(PULONG)Esp;
117         Esp += 4;
118     }
119     else
120     {
121         /* Read EFlags */
122         EFlags = *(PUSHORT)Esp;
123         Esp += 2;
124     }
125 
126     /* Set new ESP */
127     TrapFrame->HardwareEsp = Esp - (TrapFrame->HardwareSegSs << 4);
128 
129     /* Mask out IOPL from the flags */
130     EFlags &= ~EFLAGS_IOPL;
131 
132     /* Save the V86 flags, but mask out the nested task flag */
133     V86EFlags = EFlags & ~EFLAGS_NESTED_TASK;
134 
135     /* Now leave only alignment, nested task and interrupt flag */
136     EFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
137 
138     /* Get trap EFlags */
139     TrapEFlags = TrapFrame->EFlags;
140 
141     /* Check for VME support */
142     if(KeI386VirtualIntExtensions)
143     {
144         /* Copy the IF flag into the VIF one */
145         V86EFlags &= ~EFLAGS_VIF;
146         if(V86EFlags & EFLAGS_INTERRUPT_MASK)
147         {
148             V86EFlags |= EFLAGS_VIF;
149             /* Don't set the interrupt flag */
150             V86EFlags &= ~EFLAGS_INTERRUPT_MASK;
151         }
152     }
153 
154     /* Add V86 flag */
155     V86EFlags |= EFLAGS_V86_MASK;
156 
157     /* Update EFlags in trap frame */
158     TrapFrame->EFlags |= V86EFlags;
159 
160     /* Check if ESP0 needs to be fixed up */
161     if (TrapEFlags & EFLAGS_V86_MASK) Ki386AdjustEsp0(TrapFrame);
162 
163     /* Update the V8086 EFlags state */
164     KiVdmClearVdmEFlags(EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
165     KiVdmSetVdmEFlags(EFlags);
166 
167     /* FIXME: Check for VDM interrupts */
168 
169     /* Update EIP */
170     TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
171 
172     /* We're done */
173     return TRUE;
174 }
175 
176 BOOLEAN
177 FASTCALL
178 KiVdmOpcodeINTnn(IN PKTRAP_FRAME TrapFrame,
179                  IN ULONG Flags)
180 {
181     ULONG Esp, V86EFlags, TrapEFlags, Eip, Interrupt;
182 
183     /* Read trap frame EFlags */
184     TrapEFlags = TrapFrame->EFlags;
185 
186     /* Remove interrupt flag from V8086 EFlags */
187     V86EFlags = *KiNtVdmState;
188     KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
189 
190     /* Keep only alignment and interrupt flag from the V8086 state */
191     V86EFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_INTERRUPT_MASK);
192 
193     /* Check for VME support */
194     ASSERT(KeI386VirtualIntExtensions == FALSE);
195 
196     /* Mask in the relevant V86 EFlags into the trap flags */
197     V86EFlags |= (TrapEFlags & ~EFLAGS_INTERRUPT_MASK);
198 
199     /* And mask out the VIF, nested task and TF flag from the trap flags */
200     TrapFrame->EFlags = TrapEFlags &~ (EFLAGS_VIF | EFLAGS_NESTED_TASK | EFLAGS_TF);
201 
202     /* Add the IOPL flag to the local trap flags */
203     V86EFlags |= EFLAGS_IOPL;
204 
205     /* Build flat ESP */
206     Esp = (TrapFrame->HardwareSegSs << 4) + TrapFrame->HardwareEsp;
207 
208     /* Push EFlags */
209     Esp -= 2;
210     *(PUSHORT)(Esp) = (USHORT)V86EFlags;
211 
212     /* Push CS */
213     Esp -= 2;
214     *(PUSHORT)(Esp) = (USHORT)TrapFrame->SegCs;
215 
216     /* Push IP */
217     Esp -= 2;
218     *(PUSHORT)(Esp) = (USHORT)TrapFrame->Eip + KiVdmGetInstructionSize(Flags) + 1;
219 
220     /* Update ESP */
221     TrapFrame->HardwareEsp = (USHORT)Esp;
222 
223     /* Get flat EIP */
224     Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
225 
226     /* Now get the *next* EIP address (current is original + the count - 1) */
227     Eip += KiVdmGetInstructionSize(Flags);
228 
229     /* Now read the interrupt number */
230     Interrupt = *(PUCHAR)Eip;
231 
232     /* Read the EIP from its IVT entry */
233     Interrupt = *(PULONG)(Interrupt * 4);
234     TrapFrame->Eip = (USHORT)Interrupt;
235 
236     /* Now get the CS segment */
237     Interrupt = (USHORT)(Interrupt >> 16);
238 
239     /* Check if the trap was not V8086 trap */
240     if (!(TrapFrame->EFlags & EFLAGS_V86_MASK))
241     {
242         /* Was it a kernel CS? */
243         Interrupt |= RPL_MASK;
244         if (TrapFrame->SegCs == KGDT_R0_CODE)
245         {
246             /* Add the RPL mask */
247             TrapFrame->SegCs = Interrupt;
248         }
249         else
250         {
251             /* Set user CS */
252             TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
253         }
254     }
255     else
256     {
257         /* Set IVT CS */
258         TrapFrame->SegCs = Interrupt;
259     }
260 
261     /* We're done */
262     return TRUE;
263 }
264 
265 BOOLEAN
266 FASTCALL
267 KiVdmOpcodeIRET(IN PKTRAP_FRAME TrapFrame,
268                 IN ULONG Flags)
269 {
270     ULONG Esp, V86EFlags, EFlags, TrapEFlags, Eip;
271 
272     /* Build flat ESP */
273     Esp = (TrapFrame->HardwareSegSs << 4) + TrapFrame->HardwareEsp;
274 
275     /* Check for OPER32 */
276     if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
277     {
278         /* Build segmented EIP */
279         TrapFrame->Eip = *(PULONG)Esp;
280         TrapFrame->SegCs = *(PUSHORT)(Esp + 4);
281 
282         /* Set new ESP */
283         TrapFrame->HardwareEsp += 12;
284 
285         /* Get EFLAGS */
286         EFlags = *(PULONG)(Esp + 8);
287     }
288     else
289     {
290         /* Build segmented EIP */
291         TrapFrame->Eip = *(PUSHORT)Esp;
292         TrapFrame->SegCs = *(PUSHORT)(Esp + 2);
293 
294         /* Set new ESP */
295         TrapFrame->HardwareEsp += 6;
296 
297         /* Get EFLAGS */
298         EFlags = *(PUSHORT)(Esp + 4);
299     }
300 
301     /* Mask out EFlags */
302     EFlags &= ~(EFLAGS_IOPL + EFLAGS_VIF + EFLAGS_NESTED_TASK + EFLAGS_VIP);
303     V86EFlags = EFlags;
304 
305     /* Check for VME support */
306     ASSERT(KeI386VirtualIntExtensions == FALSE);
307 
308     /* Add V86 and Interrupt flag */
309     EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
310 
311     /* Update EFlags in trap frame */
312     TrapEFlags = TrapFrame->EFlags;
313     TrapFrame->EFlags = (TrapFrame->EFlags & EFLAGS_VIP) | EFlags;
314 
315     /* Check if ESP0 needs to be fixed up */
316     if (!(TrapEFlags & EFLAGS_V86_MASK)) Ki386AdjustEsp0(TrapFrame);
317 
318     /* Update the V8086 EFlags state */
319     KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
320     KiVdmSetVdmEFlags(V86EFlags);
321 
322     /* Build flat EIP and check if this is the BOP instruction */
323     Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
324     if (*(PUSHORT)Eip == 0xC4C4)
325     {
326         /* Dispatch the BOP */
327         VdmDispatchBop(TrapFrame);
328     }
329     else
330     {
331         /* FIXME: Check for VDM interrupts */
332        DPRINT("FIXME: Check for VDM interrupts\n");
333     }
334 
335     /* We're done */
336     return TRUE;
337 }
338 
339 BOOLEAN
340 FASTCALL
341 KiVdmOpcodeCLI(IN PKTRAP_FRAME TrapFrame,
342                IN ULONG Flags)
343 {
344     /* Check for VME support */
345     ASSERT(KeI386VirtualIntExtensions == FALSE);
346 
347     /* Disable interrupts */
348     KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
349 
350     /* Skip instruction */
351     TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
352 
353     /* Done */
354     return TRUE;
355 }
356 
357 BOOLEAN
358 FASTCALL
359 KiVdmOpcodeSTI(IN PKTRAP_FRAME TrapFrame,
360                IN ULONG Flags)
361 {
362     /* Check for VME support */
363     ASSERT(KeI386VirtualIntExtensions == FALSE);
364 
365     /* Enable interrupts */
366     KiVdmSetVdmEFlags(EFLAGS_INTERRUPT_MASK);
367 
368     /* Skip instruction */
369     TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
370 
371     /* Done */
372     return TRUE;
373 }
374 
375 /* MASTER OPCODE HANDLER ******************************************************/
376 
377 BOOLEAN
378 FASTCALL
379 KiVdmHandleOpcode(IN PKTRAP_FRAME TrapFrame,
380                   IN ULONG Flags)
381 {
382     ULONG Eip;
383 
384     /* Get flat EIP of the *current* instruction (not the original EIP) */
385     Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
386     Eip += KiVdmGetInstructionSize(Flags) - 1;
387 
388     /* Read the opcode entry */
389     switch (*(PUCHAR)Eip)
390     {
391         case 0xF:               return KiCallVdmHandler(F);
392         case 0x26:              return KiCallVdmPrefixHandler(PFX_FLAG_ES);
393         case 0x2E:              return KiCallVdmPrefixHandler(PFX_FLAG_CS);
394         case 0x36:              return KiCallVdmPrefixHandler(PFX_FLAG_SS);
395         case 0x3E:              return KiCallVdmPrefixHandler(PFX_FLAG_DS);
396         case 0x64:              return KiCallVdmPrefixHandler(PFX_FLAG_FS);
397         case 0x65:              return KiCallVdmPrefixHandler(PFX_FLAG_GS);
398         case 0x66:              return KiCallVdmPrefixHandler(PFX_FLAG_OPER32);
399         case 0x67:              return KiCallVdmPrefixHandler(PFX_FLAG_ADDR32);
400         case 0xF0:              return KiCallVdmPrefixHandler(PFX_FLAG_LOCK);
401         case 0xF2:              return KiCallVdmPrefixHandler(PFX_FLAG_REPNE);
402         case 0xF3:              return KiCallVdmPrefixHandler(PFX_FLAG_REP);
403         case 0x6C:              return KiCallVdmHandler(INSB);
404         case 0x6D:              return KiCallVdmHandler(INSW);
405         case 0x6E:              return KiCallVdmHandler(OUTSB);
406         case 0x6F:              return KiCallVdmHandler(OUTSW);
407         case 0x98:              return KiCallVdmHandler(NPX);
408         case 0xD8:              return KiCallVdmHandler(NPX);
409         case 0xD9:              return KiCallVdmHandler(NPX);
410         case 0xDA:              return KiCallVdmHandler(NPX);
411         case 0xDB:              return KiCallVdmHandler(NPX);
412         case 0xDC:              return KiCallVdmHandler(NPX);
413         case 0xDD:              return KiCallVdmHandler(NPX);
414         case 0xDE:              return KiCallVdmHandler(NPX);
415         case 0xDF:              return KiCallVdmHandler(NPX);
416         case 0x9C:              return KiCallVdmHandler(PUSHF);
417         case 0x9D:              return KiCallVdmHandler(POPF);
418         case 0xCD:              return KiCallVdmHandler(INTnn);
419         case 0xCE:              return KiCallVdmHandler(INTO);
420         case 0xCF:              return KiCallVdmHandler(IRET);
421         case 0xE4:              return KiCallVdmHandler(INBimm);
422         case 0xE5:              return KiCallVdmHandler(INWimm);
423         case 0xE6:              return KiCallVdmHandler(OUTBimm);
424         case 0xE7:              return KiCallVdmHandler(OUTWimm);
425         case 0xEC:              return KiCallVdmHandler(INB);
426         case 0xED:              return KiCallVdmHandler(INW);
427         case 0xEE:              return KiCallVdmHandler(OUTB);
428         case 0xEF:              return KiCallVdmHandler(OUTW);
429         case 0xF4:              return KiCallVdmHandler(HLT);
430         case 0xFA:              return KiCallVdmHandler(CLI);
431         case 0xFB:              return KiCallVdmHandler(STI);
432         default:
433             DPRINT1("Unhandled instruction: 0x%02x.\n", *(PUCHAR)Eip);
434             return KiCallVdmHandler(INV);
435     }
436 }
437 
438 /* PREFIX HANDLER *************************************************************/
439 
440 BOOLEAN
441 FASTCALL
442 KiVdmOpcodePrefix(IN PKTRAP_FRAME TrapFrame,
443                   IN ULONG Flags)
444 {
445     /* Increase instruction size */
446     Flags++;
447 
448     /* Handle the next opcode */
449     return KiVdmHandleOpcode(TrapFrame, Flags);
450 }
451 
452 /* TRAP HANDLER ***************************************************************/
453 
454 BOOLEAN
455 FASTCALL
456 Ki386HandleOpcodeV86(IN PKTRAP_FRAME TrapFrame)
457 {
458     /* Clean up */
459     TrapFrame->Eip &= 0xFFFF;
460     TrapFrame->HardwareEsp &= 0xFFFF;
461 
462     /* We start with only 1 byte per instruction */
463     return KiVdmHandleOpcode(TrapFrame, 1);
464 }
465 
466 ULONG_PTR
467 FASTCALL
468 KiExitV86Mode(IN PKTRAP_FRAME TrapFrame)
469 {
470     PKPCR Pcr = KeGetPcr();
471     ULONG_PTR StackFrameUnaligned;
472     PKV8086_STACK_FRAME StackFrame;
473     PKTHREAD Thread;
474     PKV86_FRAME V86Frame;
475     PFX_SAVE_AREA NpxFrame;
476 
477     /* Get the stack frame back */
478     StackFrameUnaligned = TrapFrame->Esi;
479     StackFrame = (PKV8086_STACK_FRAME)(ROUND_UP(StackFrameUnaligned - 4, 16) + 4);
480     V86Frame = &StackFrame->V86Frame;
481     NpxFrame = &StackFrame->NpxArea;
482     ASSERT((ULONG_PTR)NpxFrame % 16 == 0);
483 
484     /* Copy the FPU frame back */
485     Thread = KeGetCurrentThread();
486     RtlCopyMemory(KiGetThreadNpxArea(Thread), NpxFrame, sizeof(FX_SAVE_AREA));
487 
488     /* Set initial stack back */
489     Thread->InitialStack = (PVOID)((ULONG_PTR)V86Frame->ThreadStack + sizeof(FX_SAVE_AREA));
490 
491     /* Set ESP0 back in the KTSS */
492     Pcr->TSS->Esp0 = (ULONG_PTR)Thread->InitialStack;
493     Pcr->TSS->Esp0 -= sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, V86Es);
494     Pcr->TSS->Esp0 -= NPX_FRAME_LENGTH;
495 
496     /* Restore TEB addresses */
497     Thread->Teb = V86Frame->ThreadTeb;
498     KiSetTebBase(KeGetPcr(), V86Frame->ThreadTeb);
499 
500     /* Enable interrupts and return a pointer to the trap frame */
501     _enable();
502     return StackFrameUnaligned;
503 }
504 
505 VOID
506 FASTCALL
507 KiEnterV86Mode(IN ULONG_PTR StackFrameUnaligned)
508 {
509     PKTHREAD Thread;
510     PKV8086_STACK_FRAME StackFrame = (PKV8086_STACK_FRAME)(ROUND_UP(StackFrameUnaligned - 4, 16) + 4);
511     PKTRAP_FRAME TrapFrame = &StackFrame->TrapFrame;
512     PKV86_FRAME V86Frame = &StackFrame->V86Frame;
513     PFX_SAVE_AREA NpxFrame = &StackFrame->NpxArea;
514 
515     ASSERT((ULONG_PTR)NpxFrame % 16 == 0);
516 
517     /* Build fake user-mode trap frame */
518     TrapFrame->SegCs = KGDT_R0_CODE | RPL_MASK;
519     TrapFrame->SegEs = TrapFrame->SegDs = TrapFrame->SegFs = TrapFrame->SegGs = 0;
520     TrapFrame->ErrCode = 0;
521 
522     /* Get the current thread's initial stack */
523     Thread = KeGetCurrentThread();
524     V86Frame->ThreadStack = KiGetThreadNpxArea(Thread);
525 
526     /* Save TEB addresses */
527     V86Frame->ThreadTeb = Thread->Teb;
528     V86Frame->PcrTeb = KeGetPcr()->NtTib.Self;
529 
530     /* Save return EIP */
531     TrapFrame->Eip = (ULONG_PTR)Ki386BiosCallReturnAddress;
532 
533     /* Save our stack (after the frames) */
534     TrapFrame->Esi = StackFrameUnaligned;
535     TrapFrame->Edi = (ULONG_PTR)_AddressOfReturnAddress() + 4;
536 
537     /* Sanitize EFlags and enable interrupts */
538     TrapFrame->EFlags = __readeflags() & 0x60DD7;
539     TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
540 
541     /* Fill out the rest of the frame */
542     TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK;
543     TrapFrame->HardwareEsp = 0x11FFE;
544     TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
545     TrapFrame->Dr7 = 0;
546 
547     /* Set some debug fields if trap debugging is enabled */
548     KiFillTrapFrameDebug(TrapFrame);
549 
550     /* Disable interrupts */
551     _disable();
552 
553     /* Copy the thread's NPX frame */
554     RtlCopyMemory(NpxFrame, V86Frame->ThreadStack, sizeof(FX_SAVE_AREA));
555 
556     /* Clear exception list */
557     KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
558 
559     /* Set new ESP0 */
560     KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
561 
562     /* Set new initial stack */
563     Thread->InitialStack = V86Frame;
564 
565     /* Set VDM TEB */
566     Thread->Teb = (PTEB)TRAMPOLINE_TEB;
567     KiSetTebBase(KeGetPcr(), (PVOID)TRAMPOLINE_TEB);
568 
569     /* Enable interrupts */
570     _enable();
571 
572     /* Start VDM execution */
573     NtVdmControl(VdmStartExecution, NULL);
574 
575     /* Exit to V86 mode */
576     KiEoiHelper(TrapFrame);
577 }
578 
579 VOID
580 NTAPI
581 Ke386SetIOPL(VOID)
582 {
583 
584     PKTHREAD Thread = KeGetCurrentThread();
585     PKPROCESS Process = Thread->ApcState.Process;
586     PKTRAP_FRAME TrapFrame;
587     CONTEXT Context;
588 
589     /* IOPL was enabled for this process/thread */
590     Process->Iopl = TRUE;
591     Thread->Iopl = TRUE;
592 
593     /* Get the trap frame on exit */
594     TrapFrame = KeGetTrapFrame(Thread);
595 
596     /* Convert to a context */
597     Context.ContextFlags = CONTEXT_CONTROL;
598     KeTrapFrameToContext(TrapFrame, NULL, &Context);
599 
600     /* Set the IOPL flag */
601     Context.EFlags |= EFLAGS_IOPL;
602 
603     /* Convert back to a trap frame */
604     KeContextToTrapFrame(&Context, NULL, TrapFrame, CONTEXT_CONTROL, UserMode);
605 }
606 
607 /* PUBLIC FUNCTIONS ***********************************************************/
608 
609 /*
610  * @implemented
611  */
612 NTSTATUS
613 NTAPI
614 Ke386CallBios(IN ULONG Int,
615               OUT PCONTEXT Context)
616 {
617     PUCHAR Trampoline = (PUCHAR)TRAMPOLINE_BASE;
618     PTEB VdmTeb = (PTEB)TRAMPOLINE_TEB;
619     PVDM_TIB VdmTib = (PVDM_TIB)TRAMPOLINE_TIB;
620     ULONG ContextSize = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
621     PKTHREAD Thread = KeGetCurrentThread();
622     PKTSS Tss = KeGetPcr()->TSS;
623     PKPROCESS Process = Thread->ApcState.Process;
624     PVDM_PROCESS_OBJECTS VdmProcessObjects;
625     USHORT OldOffset, OldBase;
626 
627     /* Start with a clean TEB */
628     RtlZeroMemory(VdmTeb, sizeof(TEB));
629 
630     /* Write the interrupt and bop */
631     *Trampoline++ = 0xCD;
632     *Trampoline++ = (UCHAR)Int;
633     *(PULONG)Trampoline = TRAMPOLINE_BOP;
634 
635     /* Setup the VDM TEB and TIB */
636     VdmTeb->Vdm = (PVOID)TRAMPOLINE_TIB;
637     RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
638     VdmTib->Size = sizeof(VDM_TIB);
639 
640     /* Set a blank VDM state */
641     *VdmState = 0;
642 
643     /* Copy the context */
644     RtlCopyMemory(&VdmTib->VdmContext, Context, ContextSize);
645     VdmTib->VdmContext.SegCs = (ULONG_PTR)Trampoline >> 4;
646     VdmTib->VdmContext.SegSs = (ULONG_PTR)Trampoline >> 4;
647     VdmTib->VdmContext.Eip = 0;
648     VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG_PTR);
649     VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
650     VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
651 
652     /* This can't be a real VDM process */
653     ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
654 
655     /* Allocate VDM structure */
656     VdmProcessObjects = ExAllocatePoolWithTag(NonPagedPool,
657                                               sizeof(VDM_PROCESS_OBJECTS),
658                                               '  eK');
659     if (!VdmProcessObjects) return STATUS_NO_MEMORY;
660 
661     /* Set it up */
662     RtlZeroMemory(VdmProcessObjects, sizeof(VDM_PROCESS_OBJECTS));
663     VdmProcessObjects->VdmTib = VdmTib;
664     PsGetCurrentProcess()->VdmObjects = VdmProcessObjects;
665 
666     /* Set the system affinity for the current thread */
667     KeSetSystemAffinityThread(1);
668 
669     /* Make sure there's space for two IOPMs, then copy & clear the current */
670     ASSERT(((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / 8].LimitLow >=
671             (0x2000 + IOPM_OFFSET - 1));
672     RtlCopyMemory(Ki386IopmSaveArea, &Tss->IoMaps[0].IoMap, IOPM_SIZE);
673     RtlZeroMemory(&Tss->IoMaps[0].IoMap, IOPM_SIZE);
674 
675     /* Save the old offset and base, and set the new ones */
676     OldOffset = Process->IopmOffset;
677     OldBase = Tss->IoMapBase;
678     Process->IopmOffset = (USHORT)IOPM_OFFSET;
679     Tss->IoMapBase = (USHORT)IOPM_OFFSET;
680 
681     /* Switch stacks and work the magic */
682     Ki386SetupAndExitToV86Mode(VdmTeb);
683 
684     /* Restore IOPM */
685     RtlCopyMemory(&Tss->IoMaps[0].IoMap, Ki386IopmSaveArea, IOPM_SIZE);
686     Process->IopmOffset = OldOffset;
687     Tss->IoMapBase = OldBase;
688 
689     /* Restore affinity */
690     KeRevertToUserAffinityThread();
691 
692     /* Restore context */
693     RtlCopyMemory(Context, &VdmTib->VdmContext, ContextSize);
694     Context->ContextFlags = CONTEXT_FULL;
695 
696     /* Free VDM objects */
697     ExFreePoolWithTag(PsGetCurrentProcess()->VdmObjects, '  eK');
698     PsGetCurrentProcess()->VdmObjects = NULL;
699 
700     /* Return status */
701     return STATUS_SUCCESS;
702 }
703 
704 /*
705  * @implemented
706  */
707 BOOLEAN
708 NTAPI
709 Ke386IoSetAccessProcess(IN PKPROCESS Process,
710                         IN ULONG MapNumber)
711 {
712     USHORT MapOffset;
713     PKPRCB Prcb;
714     KAFFINITY TargetProcessors;
715 
716     if(MapNumber > IOPM_COUNT)
717         return FALSE;
718 
719     MapOffset = KiComputeIopmOffset(MapNumber);
720 
721     Process->IopmOffset = MapOffset;
722 
723     TargetProcessors = Process->ActiveProcessors;
724     Prcb = KeGetCurrentPrcb();
725     if (TargetProcessors & Prcb->SetMember)
726         KeGetPcr()->TSS->IoMapBase = MapOffset;
727 
728     return TRUE;
729 }
730 
731 /*
732  * @implemented
733  */
734 BOOLEAN
735 NTAPI
736 Ke386SetIoAccessMap(IN ULONG MapNumber,
737                     IN PKIO_ACCESS_MAP IopmBuffer)
738 {
739     PKPROCESS CurrentProcess;
740     PKPRCB Prcb;
741     PVOID pt;
742 
743     if ((MapNumber > IOPM_COUNT) || (MapNumber == IO_ACCESS_MAP_NONE))
744         return FALSE;
745 
746     Prcb = KeGetCurrentPrcb();
747 
748     // Copy the IOP map and load the map for the current process.
749     pt = &(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
750     RtlMoveMemory(pt, (PVOID)IopmBuffer, IOPM_SIZE);
751     CurrentProcess = Prcb->CurrentThread->ApcState.Process;
752     KeGetPcr()->TSS->IoMapBase = CurrentProcess->IopmOffset;
753 
754     return TRUE;
755 }
756 
757 /*
758  * @implemented
759  */
760 BOOLEAN
761 NTAPI
762 Ke386QueryIoAccessMap(IN ULONG MapNumber,
763                       IN PKIO_ACCESS_MAP IopmBuffer)
764 {
765     ULONG i;
766     PVOID Map;
767     PUCHAR p;
768 
769     if (MapNumber > IOPM_COUNT)
770         return FALSE;
771 
772     if (MapNumber == IO_ACCESS_MAP_NONE)
773     {
774         // no access, simply return a map of all 1s
775         p = (PUCHAR)IopmBuffer;
776         for (i = 0; i < IOPM_SIZE; i++) {
777             p[i] = (UCHAR)-1;
778         }
779     }
780     else
781     {
782         // copy the bits
783         Map = (PVOID)&(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
784         RtlMoveMemory((PVOID)IopmBuffer, Map, IOPM_SIZE);
785     }
786 
787     return TRUE;
788 }
789