Merging r37048, r37051, r37052, r37055 from the-real-msvc branch
[reactos.git] / reactos / ntoskrnl / ke / i386 / v86vdm.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/v86vdm.c
5 * PURPOSE: Manages the Kernel's support for Virtual-8086 Mode (V86)
6 * used by Video Drivers to access ROM BIOS functions, as well
7 * as the kernel architecture part of generic VDM support.
8 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 ULONG KeI386EFlagsAndMaskV86 = EFLAGS_USER_SANITIZE;
20 ULONG KeI386EFlagsOrMaskV86 = EFLAGS_INTERRUPT_MASK;
21 PVOID Ki386IopmSaveArea;
22 BOOLEAN KeI386VirtualIntExtensions = FALSE;
23
24 /* PRIVATE FUNCTIONS *********************************************************/
25
26 /* PUBLIC FUNCTIONS **********************************************************/
27
28 /*
29 * @implemented
30 */
31 NTSTATUS
32 NTAPI
33 Ke386CallBios(IN ULONG Int,
34 OUT PCONTEXT Context)
35 {
36 PUCHAR Trampoline = (PUCHAR)TRAMPOLINE_BASE;
37 PTEB VdmTeb = (PTEB)TRAMPOLINE_TEB;
38 PVDM_TIB VdmTib = (PVDM_TIB)TRAMPOLINE_TIB;
39 ULONG ContextSize = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
40 PKTHREAD Thread = KeGetCurrentThread();
41 PKTSS Tss = KeGetPcr()->TSS;
42 PKPROCESS Process = Thread->ApcState.Process;
43 PVDM_PROCESS_OBJECTS VdmProcessObjects;
44 USHORT OldOffset, OldBase;
45
46 /* Start with a clean TEB */
47 RtlZeroMemory(VdmTeb, sizeof(TEB));
48
49 /* Write the interrupt and bop */
50 *Trampoline++ = 0xCD;
51 *Trampoline++ = (UCHAR)Int;
52 *(PULONG)Trampoline = TRAMPOLINE_BOP;
53
54 /* Setup the VDM TEB and TIB */
55 VdmTeb->Vdm = (PVOID)TRAMPOLINE_TIB;
56 RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
57 VdmTib->Size = sizeof(VDM_TIB);
58
59 /* Set a blank VDM state */
60 *VdmState = 0;
61
62 /* Copy the context */
63 RtlCopyMemory(&VdmTib->VdmContext, Context, ContextSize);
64 VdmTib->VdmContext.SegCs = (ULONG_PTR)Trampoline >> 4;
65 VdmTib->VdmContext.SegSs = (ULONG_PTR)Trampoline >> 4;
66 VdmTib->VdmContext.Eip = 0;
67 VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG_PTR);
68 VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
69 VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
70
71 /* This can't be a real VDM process */
72 ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
73
74 /* Allocate VDM structure */
75 VdmProcessObjects = ExAllocatePoolWithTag(NonPagedPool,
76 sizeof(VDM_PROCESS_OBJECTS),
77 TAG('K', 'e', ' ', ' '));
78 if (!VdmProcessObjects) return STATUS_NO_MEMORY;
79
80 /* Set it up */
81 RtlZeroMemory(VdmProcessObjects, sizeof(VDM_PROCESS_OBJECTS));
82 VdmProcessObjects->VdmTib = VdmTib;
83 PsGetCurrentProcess()->VdmObjects = VdmProcessObjects;
84
85 /* Set the system affinity for the current thread */
86 KeSetSystemAffinityThread(1);
87
88 /* Make sure there's space for two IOPMs, then copy & clear the current */
89 ASSERT(((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / 8].LimitLow >=
90 (0x2000 + IOPM_OFFSET - 1));
91 RtlCopyMemory(Ki386IopmSaveArea, &Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
92 RtlZeroMemory(&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
93
94 /* Save the old offset and base, and set the new ones */
95 OldOffset = Process->IopmOffset;
96 OldBase = Tss->IoMapBase;
97 Process->IopmOffset = (USHORT)IOPM_OFFSET;
98 Tss->IoMapBase = (USHORT)IOPM_OFFSET;
99
100 /* Switch stacks and work the magic */
101 Ki386SetupAndExitToV86Mode(VdmTeb);
102
103 /* Restore IOPM */
104 RtlCopyMemory(&Tss->IoMaps[0].IoMap, Ki386IopmSaveArea, PAGE_SIZE * 2);
105 Process->IopmOffset = OldOffset;
106 Tss->IoMapBase = OldBase;
107
108 /* Restore affinity */
109 KeRevertToUserAffinityThread();
110
111 /* Restore context */
112 RtlCopyMemory(Context, &VdmTib->VdmContext, ContextSize);
113 Context->ContextFlags = CONTEXT_FULL;
114
115 /* Free VDM objects */
116 ExFreePool(PsGetCurrentProcess()->VdmObjects);
117 PsGetCurrentProcess()->VdmObjects = NULL;
118
119 /* Return status */
120 return STATUS_SUCCESS;
121 }
122
123 /* EOF */