[TCPIP]
[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 //TrapFrame->DbgArgMark = 0xBADB0D00;
530 TrapFrame->PreviousPreviousMode = -1;
531
532 /* Disable interrupts */
533 _disable();
534
535 /* Copy the thread's NPX frame */
536 RtlCopyMemory(NpxFrame, V86Frame->ThreadStack, sizeof(FX_SAVE_AREA));
537
538 /* Clear exception list */
539 KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
540
541 /* Set new ESP0 */
542 KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
543
544 /* Set new initial stack */
545 Thread->InitialStack = V86Frame;
546
547 /* Set VDM TEB */
548 Thread->Teb = (PTEB)TRAMPOLINE_TEB;
549 KeGetPcr()->NtTib.Self = (PVOID)TRAMPOLINE_TEB;
550
551 /* Setup VDM TEB descriptor */
552 GdtEntry = &((PKIPCR)KeGetPcr())->GDT[KGDT_R3_TEB / sizeof(KGDTENTRY)];
553 GdtEntry->BaseLow = (USHORT)((ULONG_PTR)TRAMPOLINE_TEB & 0xFFFF);
554 GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)TRAMPOLINE_TEB >> 16);
555 GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)TRAMPOLINE_TEB >> 24);
556
557 /* Enable interrupts */
558 _enable();
559
560 /* Start VDM execution */
561 NtVdmControl(VdmStartExecution, NULL);
562
563 /* Exit to V86 mode */
564 KiEoiHelper(TrapFrame);
565 }
566
567 /* PUBLIC FUNCTIONS ***********************************************************/
568
569 /*
570 * @implemented
571 */
572 NTSTATUS
573 NTAPI
574 Ke386CallBios(IN ULONG Int,
575 OUT PCONTEXT Context)
576 {
577 PUCHAR Trampoline = (PUCHAR)TRAMPOLINE_BASE;
578 PTEB VdmTeb = (PTEB)TRAMPOLINE_TEB;
579 PVDM_TIB VdmTib = (PVDM_TIB)TRAMPOLINE_TIB;
580 ULONG ContextSize = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
581 PKTHREAD Thread = KeGetCurrentThread();
582 PKTSS Tss = KeGetPcr()->TSS;
583 PKPROCESS Process = Thread->ApcState.Process;
584 PVDM_PROCESS_OBJECTS VdmProcessObjects;
585 USHORT OldOffset, OldBase;
586
587 /* Start with a clean TEB */
588 RtlZeroMemory(VdmTeb, sizeof(TEB));
589
590 /* Write the interrupt and bop */
591 *Trampoline++ = 0xCD;
592 *Trampoline++ = (UCHAR)Int;
593 *(PULONG)Trampoline = TRAMPOLINE_BOP;
594
595 /* Setup the VDM TEB and TIB */
596 VdmTeb->Vdm = (PVOID)TRAMPOLINE_TIB;
597 RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
598 VdmTib->Size = sizeof(VDM_TIB);
599
600 /* Set a blank VDM state */
601 *VdmState = 0;
602
603 /* Copy the context */
604 RtlCopyMemory(&VdmTib->VdmContext, Context, ContextSize);
605 VdmTib->VdmContext.SegCs = (ULONG_PTR)Trampoline >> 4;
606 VdmTib->VdmContext.SegSs = (ULONG_PTR)Trampoline >> 4;
607 VdmTib->VdmContext.Eip = 0;
608 VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG_PTR);
609 VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
610 VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
611
612 /* This can't be a real VDM process */
613 ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
614
615 /* Allocate VDM structure */
616 VdmProcessObjects = ExAllocatePoolWithTag(NonPagedPool,
617 sizeof(VDM_PROCESS_OBJECTS),
618 ' eK');
619 if (!VdmProcessObjects) return STATUS_NO_MEMORY;
620
621 /* Set it up */
622 RtlZeroMemory(VdmProcessObjects, sizeof(VDM_PROCESS_OBJECTS));
623 VdmProcessObjects->VdmTib = VdmTib;
624 PsGetCurrentProcess()->VdmObjects = VdmProcessObjects;
625
626 /* Set the system affinity for the current thread */
627 KeSetSystemAffinityThread(1);
628
629 /* Make sure there's space for two IOPMs, then copy & clear the current */
630 ASSERT(((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / 8].LimitLow >=
631 (0x2000 + IOPM_OFFSET - 1));
632 RtlCopyMemory(Ki386IopmSaveArea, &Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
633 RtlZeroMemory(&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
634
635 /* Save the old offset and base, and set the new ones */
636 OldOffset = Process->IopmOffset;
637 OldBase = Tss->IoMapBase;
638 Process->IopmOffset = (USHORT)IOPM_OFFSET;
639 Tss->IoMapBase = (USHORT)IOPM_OFFSET;
640
641 /* Switch stacks and work the magic */
642 Ki386SetupAndExitToV86Mode(VdmTeb);
643
644 /* Restore IOPM */
645 RtlCopyMemory(&Tss->IoMaps[0].IoMap, Ki386IopmSaveArea, PAGE_SIZE * 2);
646 Process->IopmOffset = OldOffset;
647 Tss->IoMapBase = OldBase;
648
649 /* Restore affinity */
650 KeRevertToUserAffinityThread();
651
652 /* Restore context */
653 RtlCopyMemory(Context, &VdmTib->VdmContext, ContextSize);
654 Context->ContextFlags = CONTEXT_FULL;
655
656 /* Free VDM objects */
657 ExFreePool(PsGetCurrentProcess()->VdmObjects);
658 PsGetCurrentProcess()->VdmObjects = NULL;
659
660 /* Return status */
661 return STATUS_SUCCESS;
662 }
663
664 /*
665 * @implemented
666 */
667 BOOLEAN
668 NTAPI
669 Ke386IoSetAccessProcess(IN PKPROCESS Process,
670 IN ULONG MapNumber)
671 {
672 USHORT MapOffset;
673 PKPRCB Prcb;
674 KAFFINITY TargetProcessors;
675
676 if(MapNumber > IOPM_COUNT)
677 return FALSE;
678
679 MapOffset = KiComputeIopmOffset(MapNumber);
680
681 Process->IopmOffset = MapOffset;
682
683 TargetProcessors = Process->ActiveProcessors;
684 Prcb = KeGetCurrentPrcb();
685 if (TargetProcessors & Prcb->SetMember)
686 KeGetPcr()->TSS->IoMapBase = MapOffset;
687
688 return TRUE;
689 }
690
691 /*
692 * @implemented
693 */
694 BOOLEAN
695 NTAPI
696 Ke386SetIoAccessMap(IN ULONG MapNumber,
697 IN PKIO_ACCESS_MAP IopmBuffer)
698 {
699 PKPROCESS CurrentProcess;
700 PKPRCB Prcb;
701 PVOID pt;
702
703 if ((MapNumber > IOPM_COUNT) || (MapNumber == IO_ACCESS_MAP_NONE))
704 return FALSE;
705
706 Prcb = KeGetCurrentPrcb();
707
708 // Copy the IOP map and load the map for the current process.
709 pt = &(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
710 RtlMoveMemory(pt, (PVOID)IopmBuffer, IOPM_SIZE);
711 CurrentProcess = Prcb->CurrentThread->ApcState.Process;
712 KeGetPcr()->TSS->IoMapBase = CurrentProcess->IopmOffset;
713
714 return TRUE;
715 }
716
717 /*
718 * @implemented
719 */
720 BOOLEAN
721 NTAPI
722 Ke386QueryIoAccessMap(IN ULONG MapNumber,
723 IN PKIO_ACCESS_MAP IopmBuffer)
724 {
725 ULONG i;
726 PVOID Map;
727 PUCHAR p;
728
729 if (MapNumber > IOPM_COUNT)
730 return FALSE;
731
732 if (MapNumber == IO_ACCESS_MAP_NONE)
733 {
734 // no access, simply return a map of all 1s
735 p = (PUCHAR)IopmBuffer;
736 for (i = 0; i < IOPM_SIZE; i++) {
737 p[i] = (UCHAR)-1;
738 }
739 }
740 else
741 {
742 // copy the bits
743 Map = (PVOID)&(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
744 RtlMoveMemory((PVOID)IopmBuffer, Map, IOPM_SIZE);
745 }
746
747 return TRUE;
748 }