xref: /reactos/ntoskrnl/ke/i386/v86vdm.c (revision 7eead935)
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     ULONG_PTR StackFrameUnaligned;
471     PKV8086_STACK_FRAME StackFrame;
472     PKTHREAD Thread;
473     PKTRAP_FRAME PmTrapFrame;
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     PmTrapFrame = &StackFrame->TrapFrame;
481     V86Frame = &StackFrame->V86Frame;
482     NpxFrame = &StackFrame->NpxArea;
483     ASSERT((ULONG_PTR)NpxFrame % 16 == 0);
484 
485     /* Copy the FPU frame back */
486     Thread = KeGetCurrentThread();
487     RtlCopyMemory(KiGetThreadNpxArea(Thread), NpxFrame, sizeof(FX_SAVE_AREA));
488 
489     /* Set initial stack back */
490     Thread->InitialStack = (PVOID)((ULONG_PTR)V86Frame->ThreadStack + sizeof(FX_SAVE_AREA));
491 
492     /* Set ESP0 back in the KTSS */
493     KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&PmTrapFrame->V86Es;
494 
495     /* Restore TEB addresses */
496     Thread->Teb = V86Frame->ThreadTeb;
497     KiSetTebBase(KeGetPcr(), V86Frame->ThreadTeb);
498 
499     /* Enable interrupts and return a pointer to the trap frame */
500     _enable();
501     return StackFrameUnaligned;
502 }
503 
504 VOID
505 FASTCALL
506 KiEnterV86Mode(IN ULONG_PTR StackFrameUnaligned)
507 {
508     PKTHREAD Thread;
509     PKV8086_STACK_FRAME StackFrame = (PKV8086_STACK_FRAME)(ROUND_UP(StackFrameUnaligned - 4, 16) + 4);
510     PKTRAP_FRAME TrapFrame = &StackFrame->TrapFrame;
511     PKV86_FRAME V86Frame = &StackFrame->V86Frame;
512     PFX_SAVE_AREA NpxFrame = &StackFrame->NpxArea;
513 
514     ASSERT((ULONG_PTR)NpxFrame % 16 == 0);
515 
516     /* Build fake user-mode trap frame */
517     TrapFrame->SegCs = KGDT_R0_CODE | RPL_MASK;
518     TrapFrame->SegEs = TrapFrame->SegDs = TrapFrame->SegFs = TrapFrame->SegGs = 0;
519     TrapFrame->ErrCode = 0;
520 
521     /* Get the current thread's initial stack */
522     Thread = KeGetCurrentThread();
523     V86Frame->ThreadStack = KiGetThreadNpxArea(Thread);
524 
525     /* Save TEB addresses */
526     V86Frame->ThreadTeb = Thread->Teb;
527     V86Frame->PcrTeb = KeGetPcr()->NtTib.Self;
528 
529     /* Save return EIP */
530     TrapFrame->Eip = (ULONG_PTR)Ki386BiosCallReturnAddress;
531 
532     /* Save our stack (after the frames) */
533     TrapFrame->Esi = StackFrameUnaligned;
534     TrapFrame->Edi = (ULONG_PTR)_AddressOfReturnAddress() + 4;
535 
536     /* Sanitize EFlags and enable interrupts */
537     TrapFrame->EFlags = __readeflags() & 0x60DD7;
538     TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
539 
540     /* Fill out the rest of the frame */
541     TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK;
542     TrapFrame->HardwareEsp = 0x11FFE;
543     TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
544     TrapFrame->Dr7 = 0;
545 
546     /* Set some debug fields if trap debugging is enabled */
547     KiFillTrapFrameDebug(TrapFrame);
548 
549     /* Disable interrupts */
550     _disable();
551 
552     /* Copy the thread's NPX frame */
553     RtlCopyMemory(NpxFrame, V86Frame->ThreadStack, sizeof(FX_SAVE_AREA));
554 
555     /* Clear exception list */
556     KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
557 
558     /* Set new ESP0 */
559     KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
560 
561     /* Set new initial stack */
562     Thread->InitialStack = V86Frame;
563 
564     /* Set VDM TEB */
565     Thread->Teb = (PTEB)TRAMPOLINE_TEB;
566     KiSetTebBase(KeGetPcr(), (PVOID)TRAMPOLINE_TEB);
567 
568     /* Enable interrupts */
569     _enable();
570 
571     /* Start VDM execution */
572     NtVdmControl(VdmStartExecution, NULL);
573 
574     /* Exit to V86 mode */
575     KiEoiHelper(TrapFrame);
576 }
577 
578 VOID
579 NTAPI
580 Ke386SetIOPL(VOID)
581 {
582 
583     PKTHREAD Thread = KeGetCurrentThread();
584     PKPROCESS Process = Thread->ApcState.Process;
585     PKTRAP_FRAME TrapFrame;
586     CONTEXT Context;
587 
588     /* IOPL was enabled for this process/thread */
589     Process->Iopl = TRUE;
590     Thread->Iopl = TRUE;
591 
592     /* Get the trap frame on exit */
593     TrapFrame = KeGetTrapFrame(Thread);
594 
595     /* Convert to a context */
596     Context.ContextFlags = CONTEXT_CONTROL;
597     KeTrapFrameToContext(TrapFrame, NULL, &Context);
598 
599     /* Set the IOPL flag */
600     Context.EFlags |= EFLAGS_IOPL;
601 
602     /* Convert back to a trap frame */
603     KeContextToTrapFrame(&Context, NULL, TrapFrame, CONTEXT_CONTROL, UserMode);
604 }
605 
606 /* PUBLIC FUNCTIONS ***********************************************************/
607 
608 /*
609  * @implemented
610  */
611 NTSTATUS
612 NTAPI
613 Ke386CallBios(IN ULONG Int,
614               OUT PCONTEXT Context)
615 {
616     PUCHAR Trampoline = (PUCHAR)TRAMPOLINE_BASE;
617     PTEB VdmTeb = (PTEB)TRAMPOLINE_TEB;
618     PVDM_TIB VdmTib = (PVDM_TIB)TRAMPOLINE_TIB;
619     ULONG ContextSize = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
620     PKTHREAD Thread = KeGetCurrentThread();
621     PKTSS Tss = KeGetPcr()->TSS;
622     PKPROCESS Process = Thread->ApcState.Process;
623     PVDM_PROCESS_OBJECTS VdmProcessObjects;
624     USHORT OldOffset, OldBase;
625 
626     /* Start with a clean TEB */
627     RtlZeroMemory(VdmTeb, sizeof(TEB));
628 
629     /* Write the interrupt and bop */
630     *Trampoline++ = 0xCD;
631     *Trampoline++ = (UCHAR)Int;
632     *(PULONG)Trampoline = TRAMPOLINE_BOP;
633 
634     /* Setup the VDM TEB and TIB */
635     VdmTeb->Vdm = (PVOID)TRAMPOLINE_TIB;
636     RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
637     VdmTib->Size = sizeof(VDM_TIB);
638 
639     /* Set a blank VDM state */
640     *VdmState = 0;
641 
642     /* Copy the context */
643     RtlCopyMemory(&VdmTib->VdmContext, Context, ContextSize);
644     VdmTib->VdmContext.SegCs = (ULONG_PTR)Trampoline >> 4;
645     VdmTib->VdmContext.SegSs = (ULONG_PTR)Trampoline >> 4;
646     VdmTib->VdmContext.Eip = 0;
647     VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG_PTR);
648     VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
649     VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
650 
651     /* This can't be a real VDM process */
652     ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
653 
654     /* Allocate VDM structure */
655     VdmProcessObjects = ExAllocatePoolWithTag(NonPagedPool,
656                                               sizeof(VDM_PROCESS_OBJECTS),
657                                               '  eK');
658     if (!VdmProcessObjects) return STATUS_NO_MEMORY;
659 
660     /* Set it up */
661     RtlZeroMemory(VdmProcessObjects, sizeof(VDM_PROCESS_OBJECTS));
662     VdmProcessObjects->VdmTib = VdmTib;
663     PsGetCurrentProcess()->VdmObjects = VdmProcessObjects;
664 
665     /* Set the system affinity for the current thread */
666     KeSetSystemAffinityThread(1);
667 
668     /* Make sure there's space for two IOPMs, then copy & clear the current */
669     ASSERT(((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / 8].LimitLow >=
670             (0x2000 + IOPM_OFFSET - 1));
671     RtlCopyMemory(Ki386IopmSaveArea, &Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
672     RtlZeroMemory(&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
673 
674     /* Save the old offset and base, and set the new ones */
675     OldOffset = Process->IopmOffset;
676     OldBase = Tss->IoMapBase;
677     Process->IopmOffset = (USHORT)IOPM_OFFSET;
678     Tss->IoMapBase = (USHORT)IOPM_OFFSET;
679 
680     /* Switch stacks and work the magic */
681     Ki386SetupAndExitToV86Mode(VdmTeb);
682 
683     /* Restore IOPM */
684     RtlCopyMemory(&Tss->IoMaps[0].IoMap, Ki386IopmSaveArea, PAGE_SIZE * 2);
685     Process->IopmOffset = OldOffset;
686     Tss->IoMapBase = OldBase;
687 
688     /* Restore affinity */
689     KeRevertToUserAffinityThread();
690 
691     /* Restore context */
692     RtlCopyMemory(Context, &VdmTib->VdmContext, ContextSize);
693     Context->ContextFlags = CONTEXT_FULL;
694 
695     /* Free VDM objects */
696     ExFreePoolWithTag(PsGetCurrentProcess()->VdmObjects, '  eK');
697     PsGetCurrentProcess()->VdmObjects = NULL;
698 
699     /* Return status */
700     return STATUS_SUCCESS;
701 }
702 
703 /*
704  * @implemented
705  */
706 BOOLEAN
707 NTAPI
708 Ke386IoSetAccessProcess(IN PKPROCESS Process,
709                         IN ULONG MapNumber)
710 {
711     USHORT MapOffset;
712     PKPRCB Prcb;
713     KAFFINITY TargetProcessors;
714 
715     if(MapNumber > IOPM_COUNT)
716         return FALSE;
717 
718     MapOffset = KiComputeIopmOffset(MapNumber);
719 
720     Process->IopmOffset = MapOffset;
721 
722     TargetProcessors = Process->ActiveProcessors;
723     Prcb = KeGetCurrentPrcb();
724     if (TargetProcessors & Prcb->SetMember)
725         KeGetPcr()->TSS->IoMapBase = MapOffset;
726 
727     return TRUE;
728 }
729 
730 /*
731  * @implemented
732  */
733 BOOLEAN
734 NTAPI
735 Ke386SetIoAccessMap(IN ULONG MapNumber,
736                     IN PKIO_ACCESS_MAP IopmBuffer)
737 {
738     PKPROCESS CurrentProcess;
739     PKPRCB Prcb;
740     PVOID pt;
741 
742     if ((MapNumber > IOPM_COUNT) || (MapNumber == IO_ACCESS_MAP_NONE))
743         return FALSE;
744 
745     Prcb = KeGetCurrentPrcb();
746 
747     // Copy the IOP map and load the map for the current process.
748     pt = &(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
749     RtlMoveMemory(pt, (PVOID)IopmBuffer, IOPM_SIZE);
750     CurrentProcess = Prcb->CurrentThread->ApcState.Process;
751     KeGetPcr()->TSS->IoMapBase = CurrentProcess->IopmOffset;
752 
753     return TRUE;
754 }
755 
756 /*
757  * @implemented
758  */
759 BOOLEAN
760 NTAPI
761 Ke386QueryIoAccessMap(IN ULONG MapNumber,
762                       IN PKIO_ACCESS_MAP IopmBuffer)
763 {
764     ULONG i;
765     PVOID Map;
766     PUCHAR p;
767 
768     if (MapNumber > IOPM_COUNT)
769         return FALSE;
770 
771     if (MapNumber == IO_ACCESS_MAP_NONE)
772     {
773         // no access, simply return a map of all 1s
774         p = (PUCHAR)IopmBuffer;
775         for (i = 0; i < IOPM_SIZE; i++) {
776             p[i] = (UCHAR)-1;
777         }
778     }
779     else
780     {
781         // copy the bits
782         Map = (PVOID)&(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
783         RtlMoveMemory((PVOID)IopmBuffer, Map, IOPM_SIZE);
784     }
785 
786     return TRUE;
787 }
788