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