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