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
KiVdmOpcodePUSHF(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
KiVdmOpcodePOPF(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
KiVdmOpcodeINTnn(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
KiVdmOpcodeIRET(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
KiVdmOpcodeCLI(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
KiVdmOpcodeSTI(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
KiVdmHandleOpcode(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
KiVdmOpcodePrefix(IN PKTRAP_FRAME TrapFrame,IN ULONG Flags)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
Ki386HandleOpcodeV86(IN PKTRAP_FRAME TrapFrame)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
KiExitV86Mode(IN PKTRAP_FRAME TrapFrame)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
KiEnterV86Mode(IN ULONG_PTR StackFrameUnaligned)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
Ke386SetIOPL(VOID)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
Ke386CallBios(IN ULONG Int,OUT PCONTEXT Context)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 TAG_KERNEL);
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, TAG_KERNEL);
698 PsGetCurrentProcess()->VdmObjects = NULL;
699
700 /* Return status */
701 return STATUS_SUCCESS;
702 }
703
704 /*
705 * @implemented
706 */
707 BOOLEAN
708 NTAPI
Ke386IoSetAccessProcess(IN PKPROCESS Process,IN ULONG MapNumber)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
Ke386SetIoAccessMap(IN ULONG MapNumber,IN PKIO_ACCESS_MAP IopmBuffer)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
Ke386QueryIoAccessMap(IN ULONG MapNumber,IN PKIO_ACCESS_MAP IopmBuffer)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