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