- KdDebuggerNotPresent should be FALSE by default.
[reactos.git] / reactos / ntoskrnl / vdm / vdmexec.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/vdm/vdmexec.c
5 * PURPOSE: Support for executing VDM code and context swapping.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 ULONG VdmBopCount;
18
19 /* FUNCTIONS *****************************************************************/
20
21 NTSTATUS
22 NTAPI
23 VdmpGetVdmTib(OUT PVDM_TIB *VdmTib)
24 {
25 PVDM_TIB Tib;
26 PAGED_CODE();
27
28 /* Assume vailure */
29 *VdmTib = NULL;
30
31 /* Get the current TIB */
32 Tib = NtCurrentTeb()->Vdm;
33 if (!Tib) return STATUS_INVALID_SYSTEM_SERVICE;
34
35 /* Validate the size */
36 if (Tib->Size != sizeof(VDM_TIB)) return STATUS_INVALID_SYSTEM_SERVICE;
37
38 /* Return it */
39 *VdmTib = Tib;
40 return STATUS_SUCCESS;
41 }
42
43 VOID
44 NTAPI
45 VdmSwapContext(IN PKTRAP_FRAME TrapFrame,
46 IN PCONTEXT OutContext,
47 IN PCONTEXT InContext)
48 {
49 ULONG EFlags, OldEFlags;
50
51 /* Make sure that we're at APC_LEVEL and that this is a valid frame */
52 ASSERT(KeGetCurrentIrql() == APC_LEVEL);
53 ASSERT(TrapFrame->DbgArgMark == 0xBADB0D00);
54
55 /* Check if this is a V86 frame */
56 if (TrapFrame->EFlags & EFLAGS_V86_MASK)
57 {
58 /* Copy segment registers */
59 OutContext->SegGs = TrapFrame->V86Gs;
60 OutContext->SegFs = TrapFrame->V86Fs;
61 OutContext->SegEs = TrapFrame->V86Es;
62 OutContext->SegDs = TrapFrame->V86Ds;
63 }
64 else if (TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK))
65 {
66 /* This was kernel mode, copy segment registers */
67 OutContext->SegGs = TrapFrame->SegGs;
68 OutContext->SegFs = TrapFrame->SegFs;
69 OutContext->SegEs = TrapFrame->SegEs;
70 OutContext->SegDs = TrapFrame->SegDs;
71 }
72
73 /* Copy CS and SS */
74 OutContext->SegCs = TrapFrame->SegCs;
75 OutContext->SegSs = TrapFrame->HardwareSegSs;
76
77 /* Copy general purpose registers */
78 OutContext->Eax = TrapFrame->Eax;
79 OutContext->Ebx = TrapFrame->Ebx;
80 OutContext->Ecx = TrapFrame->Ecx;
81 OutContext->Edx = TrapFrame->Edx;
82 OutContext->Esi = TrapFrame->Esi;
83 OutContext->Edi = TrapFrame->Edi;
84
85 /* Copy stack and counter */
86 OutContext->Ebp = TrapFrame->Ebp;
87 OutContext->Esp = TrapFrame->HardwareEsp;
88 OutContext->Eip = TrapFrame->Eip;
89
90 /* Finally the flags */
91 OutContext->EFlags = TrapFrame->EFlags;
92
93 /* Now copy from the in frame to the trap frame */
94 TrapFrame->SegCs = InContext->SegCs;
95 TrapFrame->HardwareSegSs = InContext->SegSs;
96
97 /* Copy the general purpose registers */
98 TrapFrame->Eax = InContext->Eax;
99 TrapFrame->Ebx = InContext->Ebx;
100 TrapFrame->Ecx = InContext->Ecx;
101 TrapFrame->Edx = InContext->Edx;
102 TrapFrame->Esi = InContext->Esi;
103 TrapFrame->Edi = InContext->Edi;
104
105 /* Copy the stack and counter */
106 TrapFrame->Ebp = InContext->Ebp;
107 TrapFrame->HardwareEsp = InContext->Esp;
108 TrapFrame->Eip = InContext->Eip;
109
110 /* Check if the context is from V86 */
111 EFlags = InContext->EFlags;
112 if (EFlags & EFLAGS_V86_MASK)
113 {
114 /* Sanitize the flags for V86 */
115 EFlags &= KeI386EFlagsAndMaskV86;
116 EFlags |= KeI386EFlagsOrMaskV86;
117 }
118 else
119 {
120 /* Add RPL_MASK to segments */
121 TrapFrame->SegCs |= RPL_MASK;
122 TrapFrame->HardwareSegSs |= RPL_MASK;
123
124 /* Check for bogus CS */
125 if (TrapFrame->SegCs < KGDT_R0_CODE)
126 {
127 /* Set user-mode */
128 TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
129 }
130
131 /* Sanitize flags and add interrupt mask */
132 EFlags &= EFLAGS_USER_SANITIZE;
133 EFlags |=EFLAGS_INTERRUPT_MASK;
134 }
135
136 /* Save the new eflags */
137 OldEFlags = TrapFrame->EFlags;
138 TrapFrame->EFlags = EFlags;
139
140 /* Check if we need to fixup ESP0 */
141 if ((OldEFlags ^ EFlags) & EFLAGS_V86_MASK)
142 {
143 /* Fix it up */
144 Ki386AdjustEsp0(TrapFrame);
145 }
146
147 /* Check if this is a V86 context */
148 if (InContext->EFlags & EFLAGS_V86_MASK)
149 {
150 /* Copy VDM segments */
151 TrapFrame->V86Gs = InContext->SegGs;
152 TrapFrame->V86Fs = InContext->SegFs;
153 TrapFrame->V86Es = InContext->SegEs;
154 TrapFrame->V86Ds = InContext->SegDs;
155 }
156 else
157 {
158 /* Copy monitor segments */
159 TrapFrame->SegGs = InContext->SegGs;
160 TrapFrame->SegFs = InContext->SegFs;
161 TrapFrame->SegEs = InContext->SegEs;
162 TrapFrame->SegDs = InContext->SegDs;
163 }
164
165 /* Clear the exception list and return */
166 TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
167 }
168
169 NTSTATUS
170 NTAPI
171 VdmpStartExecution(VOID)
172 {
173 PETHREAD Thread = PsGetCurrentThread();
174 PKTRAP_FRAME VdmFrame;
175 NTSTATUS Status;
176 PVDM_TIB VdmTib;
177 BOOLEAN Interrupts;
178 KIRQL OldIrql;
179 CONTEXT VdmContext;
180 PAGED_CODE();
181
182 /* Get the thread's VDM frame and TIB */
183 VdmFrame = (PVOID)((ULONG_PTR)Thread->Tcb.InitialStack -
184 sizeof(FX_SAVE_AREA) -
185 sizeof(KTRAP_FRAME));
186 Status = VdmpGetVdmTib(&VdmTib);
187 if (!NT_SUCCESS(Status)) return STATUS_INVALID_SYSTEM_SERVICE;
188
189 /* Go to APC level */
190 KeRaiseIrql(APC_LEVEL, &OldIrql);
191
192 /* Check if interrupts are enabled */
193 Interrupts = (BOOLEAN)(VdmTib->VdmContext.EFlags & EFLAGS_INTERRUPT_MASK);
194
195 /* We don't support full VDM yet, this shouldn't happen */
196 ASSERT(*VdmState == 0);
197 ASSERT(VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK);
198
199 /* Get the VDM context and make sure it's an edited frame */
200 VdmContext = VdmTib->VdmContext;
201 if (!(VdmContext.SegCs & FRAME_EDITED))
202 {
203 /* Fail */
204 KeLowerIrql(OldIrql);
205 return STATUS_INVALID_SYSTEM_SERVICE;
206 }
207
208 /* FIXME: Support VME */
209 ASSERT(VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK);
210
211 /* Set interrupt state in the VDM State */
212 if (VdmTib->VdmContext.EFlags & EFLAGS_INTERRUPT_MASK)
213 {
214 /* Enable them as well */
215 InterlockedOr((PLONG)VdmState, EFLAGS_INTERRUPT_MASK);
216 }
217 else
218 {
219 /* Disable them */
220 InterlockedAnd((PLONG)VdmState, ~EFLAGS_INTERRUPT_MASK);
221 }
222
223 /* Enable the interrupt flag */
224 VdmTib->VdmContext.EFlags |= EFLAGS_INTERRUPT_MASK;
225
226 /* Now do the VDM Swap */
227 VdmSwapContext(VdmFrame, &VdmTib->MonitorContext, &VdmContext);
228
229 /* Lower the IRQL and return EAX */
230 KeLowerIrql(OldIrql);
231 return VdmFrame->Eax;
232 }
233
234 VOID
235 NTAPI
236 VdmEndExecution(IN PKTRAP_FRAME TrapFrame,
237 IN PVDM_TIB VdmTib)
238 {
239 KIRQL OldIrql;
240 CONTEXT Context;
241 PAGED_CODE();
242
243 /* Sanity check */
244 ASSERT((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
245 (TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK)));
246
247 /* Raise to APC_LEVEL */
248 KeRaiseIrql(APC_LEVEL, &OldIrql);
249
250 /* Set success */
251 VdmTib->MonitorContext.Eax = STATUS_SUCCESS;
252
253 /* Make a copy of the monitor context */
254 RtlCopyMemory(&Context, &VdmTib->MonitorContext, sizeof(CONTEXT));
255
256 /* Switch contexts */
257 VdmSwapContext(TrapFrame, &VdmTib->VdmContext, &Context);
258
259 /* FIXME: Support VME */
260
261 /* Set the EFLAGS */
262 VdmTib->VdmContext.EFlags = (VdmTib->VdmContext.EFlags &
263 ~EFLAGS_INTERRUPT_MASK) |
264 (*VdmState & EFLAGS_INTERRUPT_MASK);
265
266 /* Lower IRQL and reutrn */
267 KeLowerIrql(OldIrql);
268 }
269
270 BOOLEAN
271 NTAPI
272 VdmDispatchBop(IN PKTRAP_FRAME TrapFrame)
273 {
274 PUCHAR Eip;
275 PVDM_TIB VdmTib;
276
277 /* Check if this is from V86 mode */
278 if (TrapFrame->EFlags & EFLAGS_V86_MASK)
279 {
280 /* Calculate flat EIP */
281 Eip = (PUCHAR)((TrapFrame->Eip & 0xFFFF) +
282 ((TrapFrame->SegCs & 0xFFFF) << 4));
283
284 /* Check if this is a BOP */
285 if (*(PUSHORT)Eip == 0xC4C4)
286 {
287 /* Check sure its the DOS Bop */
288 if (Eip[2] == 0x50)
289 {
290 /* FIXME: No VDM Support */
291 ASSERT(FALSE);
292 }
293
294 /* Increase the number of BOP operations */
295 VdmBopCount++;
296
297 /* Get the TIB */
298 VdmTib = NtCurrentTeb()->Vdm;
299
300 /* Fill out a VDM Event */
301 VdmTib->EventInfo.InstructionSize = 3;
302 VdmTib->EventInfo.BopNumber = Eip[2];
303 VdmTib->EventInfo.Event = VdmBop;
304
305 /* End VDM Execution */
306 VdmEndExecution(TrapFrame, VdmTib);
307 }
308 else
309 {
310 /* Not a BOP */
311 return FALSE;
312 }
313 }
314 else
315 {
316 /* FIXME: Shouldn't happen on ROS */
317 ASSERT(FALSE);
318 }
319
320 /* Return success */
321 return TRUE;
322 }
323
324