[NTOS]
[reactos.git] / reactos / ntoskrnl / ke / i386 / v86vdm.c
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 DbgPrint("VDM: Handling PUSHF (PREFIX [0x%lx])\n", KiVdmGetPrefixFlags(Flags));
58 V86EFlags = *KiNtVdmState;
59 V86EFlags &= ~EFLAGS_INTERRUPT_MASK;
60
61 /* Get trap frame EFLags and leave only align, nested task and interrupt */
62 TrapEFlags = TrapFrame->EFlags;
63 TrapEFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
64
65 /* Add in those flags if they exist, and add in the IOPL flag */
66 V86EFlags |= TrapEFlags;
67 V86EFlags |= EFLAGS_IOPL;
68
69 /* Build flat ESP */
70 Esp = (TrapFrame->HardwareSegSs << 4) + (USHORT)TrapFrame->HardwareEsp;
71 Esp -= 2;
72
73 /* Check for OPER32 */
74 if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
75 {
76 /* Save EFlags */
77 Esp -= 2;
78 *(PULONG)(Esp - 2) = V86EFlags;
79 }
80 else
81 {
82 /* Save EFLags */
83 *(PUSHORT)Esp = (USHORT)V86EFlags;
84 }
85
86 /* Set new ESP and EIP */
87 TrapFrame->HardwareEsp = (USHORT)Esp;
88 TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
89
90 /* We're done */
91 return TRUE;
92 }
93
94 BOOLEAN
95 FASTCALL
96 KiVdmOpcodePOPF(IN PKTRAP_FRAME TrapFrame,
97 IN ULONG Flags)
98 {
99 ULONG Esp, V86EFlags, EFlags, TrapEFlags;
100
101 /* Build flat ESP */
102 DbgPrint("VDM: Handling POPF (PREFIX [0x%lx])\n", KiVdmGetPrefixFlags(Flags));
103 Esp = (TrapFrame->HardwareSegSs << 4) + (USHORT)TrapFrame->HardwareEsp;
104
105 /* Read EFlags */
106 EFlags = *(PULONG)Esp;
107 Esp += 4;
108
109 /* Check for OPER32 */
110 if (!(KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32))
111 {
112 /* Read correct flags and use correct stack address */
113 Esp -= 2;
114 EFlags &= 0xFFFF;
115 }
116
117 /* Set new ESP */
118 TrapFrame->HardwareEsp = Esp;
119
120 /* Mask out IOPL from the flags */
121 EFlags &= ~EFLAGS_IOPL;
122
123 /* Save the V86 flags, but mask out the nested task flag */
124 V86EFlags = EFlags & ~EFLAGS_NESTED_TASK;
125
126 /* Now leave only alignment, nested task and interrupt flag */
127 EFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
128
129 /* FIXME: Check for VME support */
130
131 /* Add V86 and Interrupt flag */
132 V86EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
133
134 /* Update EFlags in trap frame */
135 TrapEFlags = TrapFrame->EFlags;
136 TrapFrame->EFlags = (TrapFrame->EFlags & EFLAGS_VIP) | V86EFlags;
137
138 /* Check if ESP0 needs to be fixed up */
139 if (TrapEFlags & EFLAGS_V86_MASK) Ki386AdjustEsp0(TrapFrame);
140
141 /* Update the V8086 EFlags state */
142 KiVdmClearVdmEFlags(EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
143 KiVdmSetVdmEFlags(EFlags);
144
145 /* FIXME: Check for VDM interrupts */
146
147 /* Update EIP */
148 TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
149
150 /* We're done */
151 return TRUE;
152 }
153
154 BOOLEAN
155 FASTCALL
156 KiVdmOpcodeINTnn(IN PKTRAP_FRAME TrapFrame,
157 IN ULONG Flags)
158 {
159 ULONG Esp, V86EFlags, TrapEFlags, Eip, Interrupt;
160
161 /* Read trap frame EFlags */
162 TrapEFlags = TrapFrame->EFlags;
163
164 /* Remove interrupt flag from V8086 EFlags */
165 V86EFlags = *KiNtVdmState;
166 KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
167
168 /* Keep only alignment and interrupt flag from the V8086 state */
169 V86EFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_INTERRUPT_MASK);
170
171 /* FIXME: Support VME */
172
173 /* Mask in the relevant V86 EFlags into the trap flags */
174 V86EFlags |= (TrapEFlags & ~EFLAGS_INTERRUPT_MASK);
175
176 /* And mask out the VIF, nested task and TF flag from the trap flags */
177 TrapFrame->EFlags = TrapEFlags &~ (EFLAGS_VIF | EFLAGS_NESTED_TASK | EFLAGS_TF);
178
179 /* Add the IOPL flag to the local trap flags */
180 V86EFlags |= EFLAGS_IOPL;
181
182 /* Build flat ESP */
183 Esp = (TrapFrame->HardwareSegSs << 4) + TrapFrame->HardwareEsp;
184
185 /* Push EFlags */
186 Esp -= 2;
187 *(PUSHORT)(Esp) = (USHORT)V86EFlags;
188
189 /* Push CS */
190 Esp -= 2;
191 *(PUSHORT)(Esp) = (USHORT)TrapFrame->SegCs;
192
193 /* Push IP */
194 Esp -= 2;
195 *(PUSHORT)(Esp) = (USHORT)TrapFrame->Eip + KiVdmGetInstructionSize(Flags) + 1;
196
197 /* Update ESP */
198 TrapFrame->HardwareEsp = (USHORT)Esp;
199
200 /* Get flat EIP */
201 Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
202
203 /* Now get the *next* EIP address (current is original + the count - 1) */
204 Eip += KiVdmGetInstructionSize(Flags);
205
206 /* Now read the interrupt number */
207 Interrupt = *(PUCHAR)Eip;
208 DbgPrint("VDM: Handling INT [0x%lx]\n", Interrupt);
209
210 /* Read the EIP from its IVT entry */
211 Interrupt = *(PULONG)(Interrupt * 4);
212 TrapFrame->Eip = (USHORT)Interrupt;
213
214 /* Now get the CS segment */
215 Interrupt = (USHORT)(Interrupt >> 16);
216
217 /* Check if the trap was not V8086 trap */
218 if (!(TrapFrame->EFlags & EFLAGS_V86_MASK))
219 {
220 /* Was it a kernel CS? */
221 Interrupt |= RPL_MASK;
222 if (TrapFrame->SegCs == KGDT_R0_CODE)
223 {
224 /* Add the RPL mask */
225 TrapFrame->SegCs = Interrupt;
226 }
227 else
228 {
229 /* Set user CS */
230 TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
231 }
232 }
233 else
234 {
235 /* Set IVT CS */
236 TrapFrame->SegCs = Interrupt;
237 }
238
239 /* We're done */
240 return TRUE;
241 }
242
243 BOOLEAN
244 FASTCALL
245 KiVdmOpcodeIRET(IN PKTRAP_FRAME TrapFrame,
246 IN ULONG Flags)
247 {
248 ULONG Esp, V86EFlags, EFlags, TrapEFlags, Eip;
249
250 /* Build flat ESP */
251 DbgPrint("VDM: Handling IRET (PREFIX [0x%lx])\n", KiVdmGetPrefixFlags(Flags));
252 Esp = (TrapFrame->HardwareSegSs << 4) + TrapFrame->HardwareEsp;
253
254 /* Check for OPER32 */
255 if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
256 {
257 /* Build segmented EIP */
258 TrapFrame->Eip = *(PULONG)Esp;
259 TrapFrame->SegCs = *(PUSHORT)(Esp + 4);
260
261 /* Set new ESP */
262 TrapFrame->HardwareEsp += 12;
263
264 /* Get EFLAGS */
265 EFlags = *(PULONG)(Esp + 8);
266 }
267 else
268 {
269 /* Build segmented EIP */
270 TrapFrame->Eip = *(PUSHORT)Esp;
271 TrapFrame->SegCs = *(PUSHORT)(Esp + 2);
272
273 /* Set new ESP */
274 TrapFrame->HardwareEsp += 6;
275
276 /* Get EFLAGS */
277 EFlags = *(PUSHORT)(Esp + 4);
278 }
279
280 /* Mask out EFlags */
281 EFlags &= ~(EFLAGS_IOPL + EFLAGS_VIF + EFLAGS_NESTED_TASK + EFLAGS_VIP);
282 V86EFlags = EFlags;
283
284 /* FIXME: Check for VME support */
285
286 /* Add V86 and Interrupt flag */
287 EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
288
289 /* Update EFlags in trap frame */
290 TrapEFlags = TrapFrame->EFlags;
291 TrapFrame->EFlags = (TrapFrame->EFlags & EFLAGS_VIP) | EFlags;
292
293 /* Check if ESP0 needs to be fixed up */
294 if (!(TrapEFlags & EFLAGS_V86_MASK)) Ki386AdjustEsp0(TrapFrame);
295
296 /* Update the V8086 EFlags state */
297 KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
298 KiVdmSetVdmEFlags(V86EFlags);
299
300 /* Build flat EIP and check if this is the BOP instruction */
301 Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
302 DbgPrint("VDM: Handling IRET EIP @ 0x%p [OPCODE: %lx]\n", Eip, *(PUSHORT)Eip);
303 if (*(PUSHORT)Eip == 0xC4C4)
304 {
305 /* Dispatch the BOP */
306 VdmDispatchBop(TrapFrame);
307 }
308 else
309 {
310 /* FIXME: Check for VDM interrupts */
311 }
312
313 /* We're done */
314 return TRUE;
315 }
316
317 BOOLEAN
318 FASTCALL
319 KiVdmOpcodeCLI(IN PKTRAP_FRAME TrapFrame,
320 IN ULONG Flags)
321 {
322 /* FIXME: Support VME */
323
324 /* Disable interrupts */
325 DbgPrint("VDM: Handling CLI\n");
326 KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
327
328 /* Skip instruction */
329 TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
330
331 /* Done */
332 return TRUE;
333 }
334
335 BOOLEAN
336 FASTCALL
337 KiVdmOpcodeSTI(IN PKTRAP_FRAME TrapFrame,
338 IN ULONG Flags)
339 {
340 /* FIXME: Support VME */
341
342 /* Enable interrupts */
343 DbgPrint("VDM: Handling STI\n");
344 KiVdmSetVdmEFlags(EFLAGS_INTERRUPT_MASK);
345
346 /* Skip instruction */
347 TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
348
349 /* Done */
350 return TRUE;
351 }
352
353 /* MASTER OPCODE HANDLER ******************************************************/
354
355 BOOLEAN
356 FASTCALL
357 KiVdmHandleOpcode(IN PKTRAP_FRAME TrapFrame,
358 IN ULONG Flags)
359 {
360 ULONG Eip;
361
362 /* Get flat EIP of the *current* instruction (not the original EIP) */
363 Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
364 DbgPrint("VDM: Handling Opcode @ 0x%p\n", Eip);
365 Eip += KiVdmGetInstructionSize(Flags) - 1;
366
367 /* Read the opcode entry */
368 switch (*(PUCHAR)Eip)
369 {
370 case 0xF: return KiCallVdmHandler(F);
371 case 0x26: return KiCallVdmPrefixHandler(PFX_FLAG_ES);
372 case 0x2E: return KiCallVdmPrefixHandler(PFX_FLAG_CS);
373 case 0x36: return KiCallVdmPrefixHandler(PFX_FLAG_SS);
374 case 0x3E: return KiCallVdmPrefixHandler(PFX_FLAG_DS);
375 case 0x64: return KiCallVdmPrefixHandler(PFX_FLAG_FS);
376 case 0x65: return KiCallVdmPrefixHandler(PFX_FLAG_GS);
377 case 0x66: return KiCallVdmPrefixHandler(PFX_FLAG_OPER32);
378 case 0x67: return KiCallVdmPrefixHandler(PFX_FLAG_ADDR32);
379 case 0xF0: return KiCallVdmPrefixHandler(PFX_FLAG_LOCK);
380 case 0xF2: return KiCallVdmPrefixHandler(PFX_FLAG_REPNE);
381 case 0xF3: return KiCallVdmPrefixHandler(PFX_FLAG_REP);
382 case 0x6C: return KiCallVdmHandler(INSB);
383 case 0x6D: return KiCallVdmHandler(INSW);
384 case 0x6E: return KiCallVdmHandler(OUTSB);
385 case 0x6F: return KiCallVdmHandler(OUTSW);
386 case 0x98: return KiCallVdmHandler(NPX);
387 case 0xD8: return KiCallVdmHandler(NPX);
388 case 0xD9: return KiCallVdmHandler(NPX);
389 case 0xDA: return KiCallVdmHandler(NPX);
390 case 0xDB: return KiCallVdmHandler(NPX);
391 case 0xDC: return KiCallVdmHandler(NPX);
392 case 0xDD: return KiCallVdmHandler(NPX);
393 case 0xDE: return KiCallVdmHandler(NPX);
394 case 0xDF: return KiCallVdmHandler(NPX);
395 case 0x9C: return KiCallVdmHandler(PUSHF);
396 case 0x9D: return KiCallVdmHandler(POPF);
397 case 0xCD: return KiCallVdmHandler(INTnn);
398 case 0xCE: return KiCallVdmHandler(INTO);
399 case 0xCF: return KiCallVdmHandler(IRET);
400 case 0xE4: return KiCallVdmHandler(INBimm);
401 case 0xE5: return KiCallVdmHandler(INWimm);
402 case 0xE6: return KiCallVdmHandler(OUTBimm);
403 case 0xE7: return KiCallVdmHandler(OUTWimm);
404 case 0xEC: return KiCallVdmHandler(INB);
405 case 0xED: return KiCallVdmHandler(INW);
406 case 0xEE: return KiCallVdmHandler(OUTB);
407 case 0xEF: return KiCallVdmHandler(OUTW);
408 case 0xF4: return KiCallVdmHandler(HLT);
409 case 0xFA: return KiCallVdmHandler(CLI);
410 case 0xFB: return KiCallVdmHandler(STI);
411 default: return KiCallVdmHandler(INV);
412 }
413 }
414
415 /* PREFIX HANDLER *************************************************************/
416
417 BOOLEAN
418 FASTCALL
419 KiVdmOpcodePrefix(IN PKTRAP_FRAME TrapFrame,
420 IN ULONG Flags)
421 {
422 /* Increase instruction size */
423 DbgPrint("VDM: Handling PREFIX [%lx] Opcode @ 0x%p\n", KiVdmGetPrefixFlags(Flags), TrapFrame->Eip);
424 Flags++;
425
426 /* Handle the next opcode */
427 return KiVdmHandleOpcode(TrapFrame, Flags);
428 }
429
430 /* TRAP HANDLER ***************************************************************/
431
432 BOOLEAN
433 FASTCALL
434 Ki386HandleOpcodeV86(IN PKTRAP_FRAME TrapFrame)
435 {
436 /* Clean up */
437 TrapFrame->Eip &= 0xFFFF;
438 TrapFrame->HardwareEsp &= 0xFFFF;
439
440 /* We start with only 1 byte per instruction */
441 return KiVdmHandleOpcode(TrapFrame, 1);
442 }
443
444 ULONG_PTR
445 FASTCALL
446 KiExitV86Mode(IN PKTRAP_FRAME TrapFrame)
447 {
448 PKV8086_STACK_FRAME StackFrame;
449 PKGDTENTRY GdtEntry;
450 PKTHREAD Thread;
451 PKTRAP_FRAME PmTrapFrame;
452 PKV86_FRAME V86Frame;
453 PFX_SAVE_AREA NpxFrame;
454
455 /* Get the stack frame back */
456 StackFrame = CONTAINING_RECORD(TrapFrame->Esi, KV8086_STACK_FRAME, V86Frame);
457 PmTrapFrame = &StackFrame->TrapFrame;
458 V86Frame = &StackFrame->V86Frame;
459 NpxFrame = &StackFrame->NpxArea;
460
461 /* Copy the FPU frame back */
462 Thread = KeGetCurrentThread();
463 RtlCopyMemory(KiGetThreadNpxArea(Thread), NpxFrame, sizeof(FX_SAVE_AREA));
464
465 /* Set initial stack back */
466 Thread->InitialStack = (PVOID)((ULONG_PTR)V86Frame->ThreadStack + sizeof(FX_SAVE_AREA));
467
468 /* Set ESP0 back in the KTSS */
469 KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&PmTrapFrame->V86Es;
470
471 /* Restore TEB addresses */
472 Thread->Teb = V86Frame->ThreadTeb;
473 KeGetPcr()->Tib.Self = V86Frame->PcrTeb;
474
475 /* Setup real TEB descriptor */
476 GdtEntry = &((PKIPCR)KeGetPcr())->GDT[KGDT_R3_TEB / sizeof(KGDTENTRY)];
477 GdtEntry->BaseLow = (USHORT)((ULONG_PTR)Thread->Teb & 0xFFFF);
478 GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Thread->Teb >> 16);
479 GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Thread->Teb >> 24);
480
481 /* Enable interrupts and return a pointer to the trap frame */
482 _enable();
483 return (ULONG)PmTrapFrame;
484 }
485
486 VOID
487 FASTCALL
488 KiEnterV86Mode(IN PKV8086_STACK_FRAME StackFrame)
489 {
490 PKTHREAD Thread;
491 PKGDTENTRY GdtEntry;
492 PKTRAP_FRAME TrapFrame = &StackFrame->TrapFrame;
493 PKV86_FRAME V86Frame = &StackFrame->V86Frame;
494 PFX_SAVE_AREA NpxFrame = &StackFrame->NpxArea;
495
496 /* Build fake user-mode trap frame */
497 TrapFrame->SegCs = KGDT_R0_CODE | RPL_MASK;
498 TrapFrame->SegEs = TrapFrame->SegDs = TrapFrame->SegFs = TrapFrame->SegGs = 0;
499 TrapFrame->ErrCode = 0;
500
501 /* Get the current thread's initial stack */
502 Thread = KeGetCurrentThread();
503 V86Frame->ThreadStack = KiGetThreadNpxArea(Thread);
504
505 /* Save TEB addresses */
506 V86Frame->ThreadTeb = Thread->Teb;
507 V86Frame->PcrTeb = KeGetPcr()->Tib.Self;
508
509 /* Save return EIP */
510 TrapFrame->Eip = (ULONG_PTR)Ki386BiosCallReturnAddress;
511
512 /* Save our stack (after the frames) */
513 TrapFrame->Esi = (ULONG_PTR)V86Frame;
514 TrapFrame->Edi = (ULONG_PTR)_AddressOfReturnAddress() + 4;
515
516 /* Sanitize EFlags and enable interrupts */
517 TrapFrame->EFlags = __readeflags() & 0x60DD7;
518 TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
519
520 /* Fill out the rest of the frame */
521 TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK;
522 TrapFrame->HardwareEsp = 0x11FFE;
523 TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
524 TrapFrame->Dr7 = 0;
525 //TrapFrame->DbgArgMark = 0xBADB0D00;
526 TrapFrame->PreviousPreviousMode = -1;
527
528 /* Disable interrupts */
529 _disable();
530
531 /* Copy the thread's NPX frame */
532 RtlCopyMemory(NpxFrame, V86Frame->ThreadStack, sizeof(FX_SAVE_AREA));
533
534 /* Clear exception list */
535 KeGetPcr()->Tib.ExceptionList = EXCEPTION_CHAIN_END;
536
537 /* Set new ESP0 */
538 KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
539
540 /* Set new initial stack */
541 Thread->InitialStack = V86Frame;
542
543 /* Set VDM TEB */
544 Thread->Teb = (PTEB)TRAMPOLINE_TEB;
545 KeGetPcr()->Tib.Self = (PVOID)TRAMPOLINE_TEB;
546
547 /* Setup VDM TEB descriptor */
548 GdtEntry = &((PKIPCR)KeGetPcr())->GDT[KGDT_R3_TEB / sizeof(KGDTENTRY)];
549 GdtEntry->BaseLow = (USHORT)((ULONG_PTR)TRAMPOLINE_TEB & 0xFFFF);
550 GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)TRAMPOLINE_TEB >> 16);
551 GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)TRAMPOLINE_TEB >> 24);
552
553 /* Enable interrupts */
554 _enable();
555
556 /* Start VDM execution */
557 NtVdmControl(VdmStartExecution, NULL);
558
559 /* Exit to V86 mode */
560 KiEoiHelper(TrapFrame);
561 }
562
563 /* PUBLIC FUNCTIONS ***********************************************************/
564
565 /*
566 * @implemented
567 */
568 NTSTATUS
569 NTAPI
570 Ke386CallBios(IN ULONG Int,
571 OUT PCONTEXT Context)
572 {
573 PUCHAR Trampoline = (PUCHAR)TRAMPOLINE_BASE;
574 PTEB VdmTeb = (PTEB)TRAMPOLINE_TEB;
575 PVDM_TIB VdmTib = (PVDM_TIB)TRAMPOLINE_TIB;
576 ULONG ContextSize = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
577 PKTHREAD Thread = KeGetCurrentThread();
578 PKTSS Tss = KeGetPcr()->TSS;
579 PKPROCESS Process = Thread->ApcState.Process;
580 PVDM_PROCESS_OBJECTS VdmProcessObjects;
581 USHORT OldOffset, OldBase;
582
583 /* Start with a clean TEB */
584 RtlZeroMemory(VdmTeb, sizeof(TEB));
585
586 /* Write the interrupt and bop */
587 *Trampoline++ = 0xCD;
588 *Trampoline++ = (UCHAR)Int;
589 *(PULONG)Trampoline = TRAMPOLINE_BOP;
590
591 /* Setup the VDM TEB and TIB */
592 VdmTeb->Vdm = (PVOID)TRAMPOLINE_TIB;
593 RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
594 VdmTib->Size = sizeof(VDM_TIB);
595
596 /* Set a blank VDM state */
597 *VdmState = 0;
598
599 /* Copy the context */
600 RtlCopyMemory(&VdmTib->VdmContext, Context, ContextSize);
601 VdmTib->VdmContext.SegCs = (ULONG_PTR)Trampoline >> 4;
602 VdmTib->VdmContext.SegSs = (ULONG_PTR)Trampoline >> 4;
603 VdmTib->VdmContext.Eip = 0;
604 VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG_PTR);
605 VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
606 VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
607
608 /* This can't be a real VDM process */
609 ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
610
611 /* Allocate VDM structure */
612 VdmProcessObjects = ExAllocatePoolWithTag(NonPagedPool,
613 sizeof(VDM_PROCESS_OBJECTS),
614 ' eK');
615 if (!VdmProcessObjects) return STATUS_NO_MEMORY;
616
617 /* Set it up */
618 RtlZeroMemory(VdmProcessObjects, sizeof(VDM_PROCESS_OBJECTS));
619 VdmProcessObjects->VdmTib = VdmTib;
620 PsGetCurrentProcess()->VdmObjects = VdmProcessObjects;
621
622 /* Set the system affinity for the current thread */
623 KeSetSystemAffinityThread(1);
624
625 /* Make sure there's space for two IOPMs, then copy & clear the current */
626 ASSERT(((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / 8].LimitLow >=
627 (0x2000 + IOPM_OFFSET - 1));
628 RtlCopyMemory(Ki386IopmSaveArea, &Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
629 RtlZeroMemory(&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
630
631 /* Save the old offset and base, and set the new ones */
632 OldOffset = Process->IopmOffset;
633 OldBase = Tss->IoMapBase;
634 Process->IopmOffset = (USHORT)IOPM_OFFSET;
635 Tss->IoMapBase = (USHORT)IOPM_OFFSET;
636
637 /* Switch stacks and work the magic */
638 DbgPrint("VDM: Entering V8086 Mode\n");
639 Ki386SetupAndExitToV86Mode(VdmTeb);
640 DbgPrint("VDM: Exiting V8086 Mode\n");
641
642 /* Restore IOPM */
643 RtlCopyMemory(&Tss->IoMaps[0].IoMap, Ki386IopmSaveArea, PAGE_SIZE * 2);
644 Process->IopmOffset = OldOffset;
645 Tss->IoMapBase = OldBase;
646
647 /* Restore affinity */
648 KeRevertToUserAffinityThread();
649
650 /* Restore context */
651 RtlCopyMemory(Context, &VdmTib->VdmContext, ContextSize);
652 Context->ContextFlags = CONTEXT_FULL;
653
654 /* Free VDM objects */
655 ExFreePool(PsGetCurrentProcess()->VdmObjects);
656 PsGetCurrentProcess()->VdmObjects = NULL;
657
658 /* Return status */
659 return STATUS_SUCCESS;
660 }
661
662 /*
663 * @unimplemented
664 */
665 BOOLEAN
666 NTAPI
667 Ke386IoSetAccessProcess(IN PKPROCESS Process,
668 IN ULONG Flag)
669 {
670 UNIMPLEMENTED;
671 return FALSE;
672 }
673
674 /*
675 * @unimplemented
676 */
677 BOOLEAN
678 NTAPI
679 Ke386SetIoAccessMap(IN ULONG Flag,
680 IN PVOID IopmBuffer)
681 {
682 UNIMPLEMENTED;
683 return FALSE;
684 }
685
686 /*
687 * @unimplemented
688 */
689 BOOLEAN
690 NTAPI
691 Ke386QueryIoAccessMap(IN ULONG Flag,
692 IN PVOID IopmBuffer)
693 {
694 UNIMPLEMENTED;
695 return FALSE;
696 }