xref: /reactos/ntoskrnl/vdm/vdmexec.c (revision 1734f297)
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     /* Check if VME is supported and V86 mode was enabled */
200     if ((KeI386VirtualIntExtensions) &&
201         (VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK))
202     {
203         /* Check if interrupts are enabled */
204         if (Interrupts)
205         {
206             /* Set fake IF flag */
207             VdmTib->VdmContext.EFlags |= EFLAGS_VIF;
208         }
209         else
210         {
211             /* Remove fake IF flag, turn on real IF flag */
212             VdmTib->VdmContext.EFlags &= ~EFLAGS_VIF;
213             VdmTib->VdmContext.EFlags |= EFLAGS_INTERRUPT_MASK;
214         }
215     }
216     else
217     {
218         /* Set interrupt state in the VDM State */
219         if (VdmTib->VdmContext.EFlags & EFLAGS_INTERRUPT_MASK)
220         {
221             /* Enable them as well */
222             InterlockedOr((PLONG)VdmState, EFLAGS_INTERRUPT_MASK);
223         }
224         else
225         {
226             /* Disable them */
227             InterlockedAnd((PLONG)VdmState, ~EFLAGS_INTERRUPT_MASK);
228         }
229 
230         /* Enable the interrupt flag */
231         VdmTib->VdmContext.EFlags |= EFLAGS_INTERRUPT_MASK;
232     }
233 
234     /*  Get the VDM context and make sure it's not an edited frame */
235     VdmContext = VdmTib->VdmContext;
236     if (!(VdmContext.SegCs & FRAME_EDITED))
237     {
238         /* Fail */
239         KeLowerIrql(OldIrql);
240         return STATUS_INVALID_SYSTEM_SERVICE;
241     }
242 
243     /* Now do the VDM Swap */
244     VdmSwapContext(VdmFrame, &VdmTib->MonitorContext, &VdmContext);
245 
246     /* Lower the IRQL and return EAX */
247     KeLowerIrql(OldIrql);
248     return VdmFrame->Eax;
249 }
250 
251 VOID
252 NTAPI
253 VdmEndExecution(IN PKTRAP_FRAME TrapFrame,
254                 IN PVDM_TIB VdmTib)
255 {
256     KIRQL OldIrql;
257     CONTEXT Context;
258     PAGED_CODE();
259 
260     /* Sanity check */
261     ASSERT((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
262            (TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK)));
263 
264     /* Raise to APC_LEVEL */
265     KeRaiseIrql(APC_LEVEL, &OldIrql);
266 
267     /* Set success */
268     VdmTib->MonitorContext.Eax = STATUS_SUCCESS;
269 
270     /* Make a copy of the monitor context */
271     Context = VdmTib->MonitorContext;
272 
273     /* Check if V86 mode was enabled or the trap was edited */
274     if ((Context.EFlags & EFLAGS_V86_MASK) || (Context.SegCs & FRAME_EDITED))
275     {
276         /* Switch contexts */
277         VdmSwapContext(TrapFrame, &VdmTib->VdmContext, &Context);
278 
279         /* Check if VME is supported and V86 mode was enabled */
280         if ((KeI386VirtualIntExtensions) &&
281             (VdmTib->VdmContext.EFlags & EFLAGS_V86_MASK))
282         {
283             /* Check for VIF (virtual interrupt) flag state */
284             if (VdmTib->VdmContext.EFlags & EFLAGS_VIF)
285             {
286                 /* Set real IF flag */
287                 VdmTib->VdmContext.EFlags |= EFLAGS_INTERRUPT_MASK;
288             }
289             else
290             {
291                 /* Remove real IF flag */
292                 VdmTib->VdmContext.EFlags &= ~EFLAGS_INTERRUPT_MASK;
293             }
294 
295             /* Turn off VIP and VIF */
296             TrapFrame->EFlags &= ~(EFLAGS_VIP | EFLAGS_VIF);
297             VdmTib->VdmContext.EFlags &= ~(EFLAGS_VIP | EFLAGS_VIF);
298         }
299         else
300         {
301             /* Set the EFLAGS based on our software copy of EFLAGS */
302             VdmTib->VdmContext.EFlags = (VdmTib->VdmContext.EFlags & ~EFLAGS_INTERRUPT_MASK) |
303                                         (*VdmState & EFLAGS_INTERRUPT_MASK);
304         }
305     }
306 
307     /* Lower IRQL and reutrn */
308     KeLowerIrql(OldIrql);
309 }
310 
311 BOOLEAN
312 NTAPI
313 VdmDispatchBop(IN PKTRAP_FRAME TrapFrame)
314 {
315     PUCHAR Eip;
316     PVDM_TIB VdmTib;
317 
318     /* Check if this is from V86 mode */
319     if (TrapFrame->EFlags & EFLAGS_V86_MASK)
320     {
321         /* Calculate flat EIP */
322         Eip = (PUCHAR)((TrapFrame->Eip & 0xFFFF) +
323                       ((TrapFrame->SegCs & 0xFFFF) << 4));
324 
325         /* Check if this is a BOP */
326         if (*(PUSHORT)Eip == 0xC4C4)
327         {
328             /* Check sure its the DOS Bop */
329             if (Eip[2] == 0x50)
330             {
331                 /* FIXME: No VDM Support */
332                 ASSERT(FALSE);
333             }
334 
335             /* Increase the number of BOP operations */
336             VdmBopCount++;
337 
338             /* Get the TIB */
339             VdmTib = NtCurrentTeb()->Vdm;
340 
341             /* Fill out a VDM Event */
342             VdmTib->EventInfo.InstructionSize = 3;
343             VdmTib->EventInfo.BopNumber = Eip[2];
344             VdmTib->EventInfo.Event = VdmBop;
345 
346             /* End VDM Execution */
347             VdmEndExecution(TrapFrame, VdmTib);
348         }
349         else
350         {
351             /* Not a BOP */
352             return FALSE;
353         }
354     }
355     else
356     {
357         /* FIXME: Shouldn't happen on ROS */
358         ASSERT(FALSE);
359     }
360 
361     /* Return success */
362     return TRUE;
363 }
364 
365 BOOLEAN
366 NTAPI
367 VdmDispatchPageFault(
368     _In_ PKTRAP_FRAME TrapFrame)
369 {
370     NTSTATUS Status;
371     PVDM_TIB VdmTib;
372 
373     PAGED_CODE();
374 
375     /* Get the VDM TIB so we can terminate V86 execution */
376     Status = VdmpGetVdmTib(&VdmTib);
377     if (!NT_SUCCESS(Status))
378     {
379         /* Not a proper VDM fault, keep looking */
380         DPRINT1("VdmDispatchPageFault: no VDM TIB, Vdm=%p\n", NtCurrentTeb()->Vdm);
381         return FALSE;
382     }
383 
384     /* Must be coming from V86 code */
385     ASSERT(TrapFrame->EFlags & EFLAGS_V86_MASK);
386 
387     _SEH2_TRY
388     {
389         /* Fill out a VDM Event */
390         VdmTib->EventInfo.Event = VdmMemAccess;
391         VdmTib->EventInfo.InstructionSize = 0;
392 
393         /* End VDM Execution */
394         VdmEndExecution(TrapFrame, VdmTib);
395     }
396     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
397     {
398         Status = _SEH2_GetExceptionCode();
399     }
400     _SEH2_END;
401 
402     /* Consider the exception handled if we succeeded */
403     DPRINT1("VdmDispatchPageFault EFlags %lx exit with 0x%lx\n", TrapFrame->EFlags, Status);
404     return NT_SUCCESS(Status);
405 }
406 
407