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