VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/TRPMAll.cpp@ 45685

Last change on this file since 45685 was 45534, checked in by vboxsync, 12 years ago

VMM: use of symbolic names and asserts.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 37.1 KB
Line 
1/* $Id: TRPMAll.cpp 45534 2013-04-13 16:16:43Z vboxsync $ */
2/** @file
3 * TRPM - Trap Monitor - Any Context.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_TRPM
23#include <VBox/vmm/trpm.h>
24#include <VBox/vmm/pgm.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/patm.h>
27#include <VBox/vmm/selm.h>
28#include <VBox/vmm/stam.h>
29#include <VBox/vmm/dbgf.h>
30#include "TRPMInternal.h"
31#include <VBox/vmm/vm.h>
32#include <VBox/err.h>
33#include <VBox/vmm/em.h>
34#include <VBox/log.h>
35#include <iprt/assert.h>
36#include <iprt/asm.h>
37#include <iprt/asm-amd64-x86.h>
38#include <iprt/param.h>
39#include <iprt/x86.h>
40#include "internal/pgm.h"
41
42
43
44/**
45 * Query info about the current active trap/interrupt.
46 * If no trap is active active an error code is returned.
47 *
48 * @returns VBox status code.
49 * @param pVCpu Pointer to the VMCPU.
50 * @param pu8TrapNo Where to store the trap number.
51 * @param pEnmType Where to store the trap type
52 */
53VMMDECL(int) TRPMQueryTrap(PVMCPU pVCpu, uint8_t *pu8TrapNo, TRPMEVENT *pEnmType)
54{
55 /*
56 * Check if we have a trap at present.
57 */
58 if (pVCpu->trpm.s.uActiveVector != ~0U)
59 {
60 if (pu8TrapNo)
61 *pu8TrapNo = (uint8_t)pVCpu->trpm.s.uActiveVector;
62 if (pEnmType)
63 *pEnmType = pVCpu->trpm.s.enmActiveType;
64 return VINF_SUCCESS;
65 }
66
67 return VERR_TRPM_NO_ACTIVE_TRAP;
68}
69
70
71/**
72 * Gets the trap number for the current trap.
73 *
74 * The caller is responsible for making sure there is an active trap which
75 * takes an error code when making this request.
76 *
77 * @returns The current trap number.
78 * @param pVCpu Pointer to the VMCPU.
79 */
80VMMDECL(uint8_t) TRPMGetTrapNo(PVMCPU pVCpu)
81{
82 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
83 return (uint8_t)pVCpu->trpm.s.uActiveVector;
84}
85
86
87/**
88 * Gets the error code for the current trap.
89 *
90 * The caller is responsible for making sure there is an active trap which
91 * takes an error code when making this request.
92 *
93 * @returns Error code.
94 * @param pVCpu Pointer to the VMCPU.
95 */
96VMMDECL(RTGCUINT) TRPMGetErrorCode(PVMCPU pVCpu)
97{
98 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
99#ifdef VBOX_STRICT
100 switch (pVCpu->trpm.s.uActiveVector)
101 {
102 case X86_XCPT_TS:
103 case X86_XCPT_NP:
104 case X86_XCPT_SS:
105 case X86_XCPT_GP:
106 case X86_XCPT_PF:
107 case X86_XCPT_AC:
108 case X86_XCPT_DF:
109 break;
110 default:
111 AssertMsgFailed(("This trap (%#x) doesn't have any error code\n", pVCpu->trpm.s.uActiveVector));
112 break;
113 }
114#endif
115 return pVCpu->trpm.s.uActiveErrorCode;
116}
117
118
119/**
120 * Gets the fault address for the current trap.
121 *
122 * The caller is responsible for making sure there is an active trap 0x0e when
123 * making this request.
124 *
125 * @returns Fault address associated with the trap.
126 * @param pVCpu Pointer to the VMCPU.
127 */
128VMMDECL(RTGCUINTPTR) TRPMGetFaultAddress(PVMCPU pVCpu)
129{
130 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
131 AssertMsg(pVCpu->trpm.s.uActiveVector == X86_XCPT_PF, ("Not page-fault trap!\n"));
132 return pVCpu->trpm.s.uActiveCR2;
133}
134
135
136/**
137 * Gets the instruction-length for the current trap (only relevant for software
138 * interrupts and software exceptions #BP and #OF).
139 *
140 * The caller is responsible for making sure there is an active trap 0x0e when
141 * making this request.
142 *
143 * @returns Fault address associated with the trap.
144 * @param pVCpu Pointer to the VMCPU.
145 */
146VMMDECL(uint8_t) TRPMGetInstrLength(PVMCPU pVCpu)
147{
148 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
149 return pVCpu->trpm.s.cbInstr;
150}
151
152
153/**
154 * Clears the current active trap/exception/interrupt.
155 *
156 * The caller is responsible for making sure there is an active trap
157 * when making this request.
158 *
159 * @returns VBox status code.
160 * @param pVCpu Pointer to the VMCPU.
161 */
162VMMDECL(int) TRPMResetTrap(PVMCPU pVCpu)
163{
164 /*
165 * Cannot reset non-existing trap!
166 */
167 if (pVCpu->trpm.s.uActiveVector == ~0U)
168 {
169 AssertMsgFailed(("No active trap!\n"));
170 return VERR_TRPM_NO_ACTIVE_TRAP;
171 }
172
173 /*
174 * Reset it.
175 */
176 pVCpu->trpm.s.uActiveVector = ~0U;
177 return VINF_SUCCESS;
178}
179
180
181/**
182 * Assert trap/exception/interrupt.
183 *
184 * The caller is responsible for making sure there is no active trap
185 * when making this request.
186 *
187 * @returns VBox status code.
188 * @param pVCpu Pointer to the VMCPU.
189 * @param u8TrapNo The trap vector to assert.
190 * @param enmType Trap type.
191 */
192VMMDECL(int) TRPMAssertTrap(PVMCPU pVCpu, uint8_t u8TrapNo, TRPMEVENT enmType)
193{
194 Log2(("TRPMAssertTrap: u8TrapNo=%02x type=%d\n", u8TrapNo, enmType));
195
196 /*
197 * Cannot assert a trap when one is already active.
198 */
199 if (pVCpu->trpm.s.uActiveVector != ~0U)
200 {
201 AssertMsgFailed(("CPU%d: Active trap %#x\n", pVCpu->idCpu, pVCpu->trpm.s.uActiveVector));
202 return VERR_TRPM_ACTIVE_TRAP;
203 }
204
205 pVCpu->trpm.s.uActiveVector = u8TrapNo;
206 pVCpu->trpm.s.enmActiveType = enmType;
207 pVCpu->trpm.s.uActiveErrorCode = ~(RTGCUINT)0;
208 pVCpu->trpm.s.uActiveCR2 = 0xdeadface;
209 pVCpu->trpm.s.cbInstr = UINT8_MAX;
210 return VINF_SUCCESS;
211}
212
213
214/**
215 * Assert a page-fault exception.
216 *
217 * The caller is responsible for making sure there is no active trap
218 * when making this request.
219 *
220 * @returns VBox status code.
221 * @param pVCpu Pointer to the VMCPU.
222 * @param uCR2 The new fault address.
223 * @param uErrorCode The error code for the page-fault.
224 */
225VMMDECL(int) TRPMAssertXcptPF(PVMCPU pVCpu, RTGCUINTPTR uCR2, RTGCUINT uErrorCode)
226{
227 Log2(("TRPMAssertXcptPF: uCR2=%RGv uErrorCode=%RGv\n", uCR2, uErrorCode)); /** @todo RTGCUINT to be fixed. */
228
229 /*
230 * Cannot assert a trap when one is already active.
231 */
232 if (pVCpu->trpm.s.uActiveVector != ~0U)
233 {
234 AssertMsgFailed(("CPU%d: Active trap %#x\n", pVCpu->idCpu, pVCpu->trpm.s.uActiveVector));
235 return VERR_TRPM_ACTIVE_TRAP;
236 }
237
238 pVCpu->trpm.s.uActiveVector = X86_XCPT_PF;
239 pVCpu->trpm.s.enmActiveType = TRPM_TRAP;
240 pVCpu->trpm.s.uActiveErrorCode = uErrorCode;
241 pVCpu->trpm.s.uActiveCR2 = uCR2;
242 pVCpu->trpm.s.cbInstr = UINT8_MAX;
243 return VINF_SUCCESS;
244}
245
246
247/**
248 * Sets the error code of the current trap.
249 * (This function is for use in trap handlers and such.)
250 *
251 * The caller is responsible for making sure there is an active trap
252 * which takes an errorcode when making this request.
253 *
254 * @param pVCpu Pointer to the VMCPU.
255 * @param uErrorCode The new error code.
256 */
257VMMDECL(void) TRPMSetErrorCode(PVMCPU pVCpu, RTGCUINT uErrorCode)
258{
259 Log2(("TRPMSetErrorCode: uErrorCode=%RGv\n", uErrorCode)); /** @todo RTGCUINT mess! */
260 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
261 pVCpu->trpm.s.uActiveErrorCode = uErrorCode;
262#ifdef VBOX_STRICT
263 switch (pVCpu->trpm.s.uActiveVector)
264 {
265 case X86_XCPT_TS: case X86_XCPT_NP: case X86_XCPT_SS: case X86_XCPT_GP: case X86_XCPT_PF:
266 AssertMsg(uErrorCode != ~(RTGCUINT)0, ("Invalid uErrorCode=%#x u8TrapNo=%d\n", uErrorCode, pVCpu->trpm.s.uActiveVector));
267 break;
268 case X86_XCPT_AC: case X86_XCPT_DF:
269 AssertMsg(uErrorCode == 0, ("Invalid uErrorCode=%#x u8TrapNo=%d\n", uErrorCode, pVCpu->trpm.s.uActiveVector));
270 break;
271 default:
272 AssertMsg(uErrorCode == ~(RTGCUINT)0, ("Invalid uErrorCode=%#x u8TrapNo=%d\n", uErrorCode, pVCpu->trpm.s.uActiveVector));
273 break;
274 }
275#endif
276}
277
278
279/**
280 * Sets the fault address of the current #PF trap. (This function is for use in
281 * trap handlers and such.)
282 *
283 * The caller is responsible for making sure there is an active trap 0e
284 * when making this request.
285 *
286 * @param pVCpu Pointer to the VMCPU.
287 * @param uCR2 The new fault address (cr2 register).
288 */
289VMMDECL(void) TRPMSetFaultAddress(PVMCPU pVCpu, RTGCUINTPTR uCR2)
290{
291 Log2(("TRPMSetFaultAddress: uCR2=%RGv\n", uCR2));
292 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
293 AssertMsg(pVCpu->trpm.s.uActiveVector == X86_XCPT_PF, ("Not trap 0e!\n"));
294 pVCpu->trpm.s.uActiveCR2 = uCR2;
295}
296
297
298/**
299 * Sets the instruction-length of the current trap (relevant for software
300 * interrupts and software exceptions like #BP, #OF).
301 *
302 * The caller is responsible for making sure there is an active trap 0e
303 * when making this request.
304 *
305 * @param pVCpu Pointer to the VMCPU.
306 * @param cbInstr The instruction length.
307 */
308VMMDECL(void) TRPMSetInstrLength(PVMCPU pVCpu, uint8_t cbInstr)
309{
310 Log2(("TRPMSetInstrLength: cbInstr=%u\n", cbInstr));
311 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
312 AssertMsg( pVCpu->trpm.s.enmActiveType == TRPM_SOFTWARE_INT
313 || ( pVCpu->trpm.s.enmActiveType == TRPM_TRAP
314 && ( pVCpu->trpm.s.uActiveVector == X86_XCPT_BP
315 || pVCpu->trpm.s.uActiveVector == X86_XCPT_OF)),
316 ("Invalid trap type %#x\n", pVCpu->trpm.s.enmActiveType));
317 pVCpu->trpm.s.cbInstr = cbInstr;
318}
319
320
321/**
322 * Checks if the current active trap/interrupt/exception/fault/whatever is a software
323 * interrupt or not.
324 *
325 * The caller is responsible for making sure there is an active trap
326 * when making this request.
327 *
328 * @returns true if software interrupt, false if not.
329 *
330 * @param pVCpu Pointer to the VMCPU.
331 */
332VMMDECL(bool) TRPMIsSoftwareInterrupt(PVMCPU pVCpu)
333{
334 AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n"));
335 return (pVCpu->trpm.s.enmActiveType == TRPM_SOFTWARE_INT);
336}
337
338
339/**
340 * Check if there is an active trap.
341 *
342 * @returns true if trap active, false if not.
343 * @param pVCpu Pointer to the VMCPU.
344 */
345VMMDECL(bool) TRPMHasTrap(PVMCPU pVCpu)
346{
347 return pVCpu->trpm.s.uActiveVector != ~0U;
348}
349
350
351/**
352 * Query all info about the current active trap/interrupt.
353 * If no trap is active active an error code is returned.
354 *
355 * @returns VBox status code.
356 * @param pVCpu Pointer to the VMCPU.
357 * @param pu8TrapNo Where to store the trap number.
358 * @param pEnmType Where to store the trap type
359 * @param puErrorCode Where to store the error code associated with some traps.
360 * ~0U is stored if the trap has no error code.
361 * @param puCR2 Where to store the CR2 associated with a trap 0E.
362 * @param pu8InstrLen Where to store the instruction-length
363 * associated with some traps.
364 */
365VMMDECL(int) TRPMQueryTrapAll(PVMCPU pVCpu, uint8_t *pu8TrapNo, TRPMEVENT *pEnmType, PRTGCUINT puErrorCode, PRTGCUINTPTR puCR2,
366 uint8_t *pu8InstrLen)
367{
368 /*
369 * Check if we have a trap at present.
370 */
371 if (pVCpu->trpm.s.uActiveVector == ~0U)
372 return VERR_TRPM_NO_ACTIVE_TRAP;
373
374 if (pu8TrapNo)
375 *pu8TrapNo = (uint8_t)pVCpu->trpm.s.uActiveVector;
376 if (pEnmType)
377 *pEnmType = pVCpu->trpm.s.enmActiveType;
378 if (puErrorCode)
379 *puErrorCode = pVCpu->trpm.s.uActiveErrorCode;
380 if (puCR2)
381 *puCR2 = pVCpu->trpm.s.uActiveCR2;
382 if (pu8InstrLen)
383 *pu8InstrLen = pVCpu->trpm.s.cbInstr;
384 return VINF_SUCCESS;
385}
386
387
388/**
389 * Save the active trap.
390 *
391 * This routine useful when doing try/catch in the hypervisor.
392 * Any function which uses temporary trap handlers should
393 * probably also use this facility to save the original trap.
394 *
395 * @param pVM Pointer to the VM.
396 */
397VMMDECL(void) TRPMSaveTrap(PVMCPU pVCpu)
398{
399 pVCpu->trpm.s.uSavedVector = pVCpu->trpm.s.uActiveVector;
400 pVCpu->trpm.s.enmSavedType = pVCpu->trpm.s.enmActiveType;
401 pVCpu->trpm.s.uSavedErrorCode = pVCpu->trpm.s.uActiveErrorCode;
402 pVCpu->trpm.s.uSavedCR2 = pVCpu->trpm.s.uActiveCR2;
403 pVCpu->trpm.s.cbSavedInstr = pVCpu->trpm.s.cbInstr;
404}
405
406
407/**
408 * Restore a saved trap.
409 *
410 * Multiple restores of a saved trap is possible.
411 *
412 * @param pVM Pointer to the VM.
413 */
414VMMDECL(void) TRPMRestoreTrap(PVMCPU pVCpu)
415{
416 pVCpu->trpm.s.uActiveVector = pVCpu->trpm.s.uSavedVector;
417 pVCpu->trpm.s.enmActiveType = pVCpu->trpm.s.enmSavedType;
418 pVCpu->trpm.s.uActiveErrorCode = pVCpu->trpm.s.uSavedErrorCode;
419 pVCpu->trpm.s.uActiveCR2 = pVCpu->trpm.s.uSavedCR2;
420 pVCpu->trpm.s.cbInstr = pVCpu->trpm.s.cbSavedInstr;
421}
422
423
424#ifdef VBOX_WITH_RAW_MODE_NOT_R0
425/**
426 * Forward trap or interrupt to the guest's handler
427 *
428 *
429 * @returns VBox status code.
430 * or does not return at all (when the trap is actually forwarded)
431 *
432 * @param pVM Pointer to the VM.
433 * @param pRegFrame Pointer to the register frame for the trap.
434 * @param iGate Trap or interrupt gate number
435 * @param cbInstr Instruction size (only relevant for software interrupts)
436 * @param enmError TRPM_TRAP_HAS_ERRORCODE or TRPM_TRAP_NO_ERRORCODE.
437 * @param enmType TRPM event type
438 * @param iOrgTrap The original trap.
439 * @internal
440 */
441VMMDECL(int) TRPMForwardTrap(PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, uint32_t iGate, uint32_t cbInstr,
442 TRPMERRORCODE enmError, TRPMEVENT enmType, int32_t iOrgTrap)
443{
444#ifdef TRPM_FORWARD_TRAPS_IN_GC
445 PVM pVM = pVCpu->CTX_SUFF(pVM);
446 X86EFLAGS eflags;
447 Assert(pVM->cCpus == 1);
448
449 STAM_PROFILE_ADV_START(&pVM->trpm.s.CTX_SUFF_Z(StatForwardProf), a);
450
451# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
452 if (pRegFrame->eflags.Bits.u1VM)
453 Log(("TRPMForwardTrap-VM: eip=%04X:%04X iGate=%d\n", pRegFrame->cs.Sel, pRegFrame->eip, iGate));
454 else
455 Log(("TRPMForwardTrap: eip=%04X:%08X iGate=%d\n", pRegFrame->cs.Sel, pRegFrame->eip, iGate));
456
457 switch (iGate) {
458 case X86_XCPT_PF:
459 if (pRegFrame->eip == pVCpu->trpm.s.uActiveCR2)
460 {
461 RTGCPTR pCallerGC;
462# ifdef IN_RC
463 int rc = MMGCRamRead(pVM, &pCallerGC, (void *)pRegFrame->esp, sizeof(pCallerGC));
464# else
465 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &pCallerGC, (RTGCPTR)pRegFrame->esp, sizeof(pCallerGC));
466# endif
467 if (RT_SUCCESS(rc))
468 Log(("TRPMForwardTrap: caller=%RGv\n", pCallerGC));
469 }
470 /* no break */
471 case X86_XCPT_DF:
472 case X86_XCPT_TS:
473 case X86_XCPT_NP:
474 case X86_XCPT_SS:
475 case X86_XCPT_GP:
476 case X86_XCPT_AC:
477 Assert(enmError == TRPM_TRAP_HAS_ERRORCODE || enmType == TRPM_SOFTWARE_INT);
478 break;
479
480 default:
481 Assert(enmError == TRPM_TRAP_NO_ERRORCODE);
482 break;
483 }
484# endif /* VBOX_STRICT || LOG_ENABLED */
485#ifdef IN_RC
486 AssertReturn(CPUMIsGuestInRawMode(pVCpu), VINF_EM_RESCHEDULE);
487#endif
488
489 /* Retrieve the eflags including the virtualized bits. */
490 /* Note: hackish as the cpumctxcore structure doesn't contain the right value */
491 eflags.u32 = CPUMRawGetEFlags(pVCpu);
492
493 /* VMCPU_FF_INHIBIT_INTERRUPTS should be cleared upfront or don't call this function at all for dispatching hardware interrupts. */
494 Assert(enmType != TRPM_HARDWARE_INT || !VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS));
495
496 /*
497 * If it's a real guest trap and the guest's page fault handler is marked as safe for GC execution, then we call it directly.
498 * Well, only if the IF flag is set.
499 */
500 /** @todo if the trap handler was modified and marked invalid, then we should *now* go back to the host context and install a new patch. */
501 if ( pVM->trpm.s.aGuestTrapHandler[iGate]
502 && (eflags.Bits.u1IF)
503#ifndef VBOX_RAW_V86
504 && !(eflags.Bits.u1VM) /** @todo implement when needed (illegal for same privilege level transfers). */
505#endif
506 && !PATMIsPatchGCAddr(pVM, pRegFrame->eip)
507 )
508 {
509 uint16_t cbIDT;
510 RTGCPTR GCPtrIDT = (RTGCPTR)CPUMGetGuestIDTR(pVCpu, &cbIDT);
511 uint32_t cpl;
512 VBOXIDTE GuestIdte;
513 RTGCPTR pIDTEntry;
514 int rc;
515
516 Assert(PATMAreInterruptsEnabledByCtxCore(pVM, pRegFrame));
517 Assert(!VMCPU_FF_ISPENDING(pVCpu, VMCPU_FF_SELM_SYNC_GDT | VMCPU_FF_SELM_SYNC_LDT | VMCPU_FF_TRPM_SYNC_IDT | VMCPU_FF_SELM_SYNC_TSS));
518
519 if (GCPtrIDT && iGate * sizeof(VBOXIDTE) >= cbIDT)
520 goto failure;
521
522 /* Get the current privilege level. */
523 cpl = CPUMGetGuestCPL(pVCpu);
524
525 /*
526 * BIG TODO: The checks are not complete. see trap and interrupt dispatching section in Intel docs for details
527 * All very obscure, but still necessary.
528 * Currently only some CS & TSS selector checks are missing.
529 *
530 */
531 pIDTEntry = (RTGCPTR)((RTGCUINTPTR)GCPtrIDT + sizeof(VBOXIDTE) * iGate);
532#ifdef IN_RC
533 rc = MMGCRamRead(pVM, &GuestIdte, (void *)(uintptr_t)pIDTEntry, sizeof(GuestIdte));
534#else
535 rc = PGMPhysSimpleReadGCPtr(pVCpu, &GuestIdte, pIDTEntry, sizeof(GuestIdte));
536#endif
537 if (RT_FAILURE(rc))
538 {
539 /* The page might be out of sync. */ /** @todo might cross a page boundary) */
540 Log(("Page %RGv out of sync -> prefetch and try again\n", pIDTEntry));
541 rc = PGMPrefetchPage(pVCpu, pIDTEntry); /** @todo r=bird: rainy day: this isn't entirely safe because of access bit virtualiziation and CSAM. */
542 if (rc != VINF_SUCCESS)
543 {
544 Log(("TRPMForwardTrap: PGMPrefetchPage failed with rc=%Rrc\n", rc));
545 goto failure;
546 }
547#ifdef IN_RC
548 rc = MMGCRamRead(pVM, &GuestIdte, (void *)(uintptr_t)pIDTEntry, sizeof(GuestIdte));
549#else
550 rc = PGMPhysSimpleReadGCPtr(pVCpu, &GuestIdte, pIDTEntry, sizeof(GuestIdte));
551#endif
552 }
553 if ( RT_SUCCESS(rc)
554 && GuestIdte.Gen.u1Present
555 && (GuestIdte.Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || GuestIdte.Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
556 && (GuestIdte.Gen.u2DPL == 3 || GuestIdte.Gen.u2DPL == 0)
557 && (GuestIdte.Gen.u16SegSel & 0xfffc) /* must not be zero */
558 && (enmType == TRPM_TRAP || enmType == TRPM_HARDWARE_INT || cpl <= GuestIdte.Gen.u2DPL) /* CPL <= DPL if software int */
559 )
560 {
561 RTGCPTR pHandler, dummy;
562 RTGCPTR pTrapStackGC;
563
564 pHandler = (RTGCPTR)VBOXIDTE_OFFSET(GuestIdte);
565
566 /* Note: SELMValidateAndConvertCSAddr checks for code type, memory type, selector validity. */
567 /** @todo dpl <= cpl else GPF */
568
569 /* Note: don't use current eflags as we might be in V86 mode and the IDT always contains protected mode selectors */
570 X86EFLAGS fakeflags;
571 fakeflags.u32 = 0;
572
573 rc = SELMValidateAndConvertCSAddr(pVCpu, fakeflags, 0, GuestIdte.Gen.u16SegSel, NULL, pHandler, &dummy);
574 if (rc == VINF_SUCCESS)
575 {
576 VBOXGDTR gdtr = {0, 0};
577 bool fConforming = false;
578 int idx = 0;
579 uint32_t dpl;
580 uint32_t ss_r0;
581 uint32_t esp_r0;
582 X86DESC Desc;
583 RTGCPTR pGdtEntry;
584
585 CPUMGetGuestGDTR(pVCpu, &gdtr);
586 Assert(gdtr.pGdt && gdtr.cbGdt > GuestIdte.Gen.u16SegSel);
587
588 if (!gdtr.pGdt)
589 goto failure;
590
591 pGdtEntry = gdtr.pGdt + (GuestIdte.Gen.u16SegSel >> X86_SEL_SHIFT) * sizeof(X86DESC);
592#ifdef IN_RC
593 rc = MMGCRamRead(pVM, &Desc, (void *)(uintptr_t)pGdtEntry, sizeof(Desc));
594#else
595 rc = PGMPhysSimpleReadGCPtr(pVCpu, &Desc, pGdtEntry, sizeof(Desc));
596#endif
597 if (RT_FAILURE(rc))
598 {
599 /* The page might be out of sync. */ /** @todo might cross a page boundary) */
600 Log(("Page %RGv out of sync -> prefetch and try again\n", pGdtEntry));
601 rc = PGMPrefetchPage(pVCpu, pGdtEntry); /** @todo r=bird: rainy day: this isn't entirely safe because of access bit virtualiziation and CSAM. */
602 if (rc != VINF_SUCCESS)
603 {
604 Log(("PGMPrefetchPage failed with rc=%Rrc\n", rc));
605 goto failure;
606 }
607#ifdef IN_RC
608 rc = MMGCRamRead(pVM, &Desc, (void *)(uintptr_t)pGdtEntry, sizeof(Desc));
609#else
610 rc = PGMPhysSimpleReadGCPtr(pVCpu, &Desc, pGdtEntry, sizeof(Desc));
611#endif
612 if (RT_FAILURE(rc))
613 {
614 Log(("MMGCRamRead failed with %Rrc\n", rc));
615 goto failure;
616 }
617 }
618
619 if (Desc.Gen.u4Type & X86_SEL_TYPE_CONF)
620 {
621 Log(("Conforming code selector\n"));
622 fConforming = true;
623 }
624 /** @todo check descriptor type!! */
625
626 dpl = Desc.Gen.u2Dpl;
627
628 if (!fConforming && dpl < cpl) /* to inner privilege level */
629 {
630 rc = SELMGetRing1Stack(pVM, &ss_r0, &esp_r0);
631 if (RT_FAILURE(rc))
632 goto failure;
633
634 Assert((ss_r0 & X86_SEL_RPL) == 1);
635
636 if ( !esp_r0
637 || !ss_r0
638 || (ss_r0 & X86_SEL_RPL) != ((dpl == 0) ? 1 : dpl)
639 || SELMToFlatBySelEx(pVCpu, fakeflags, ss_r0, (RTGCPTR)esp_r0, SELMTOFLAT_FLAGS_CPL1,
640 (PRTGCPTR)&pTrapStackGC, NULL) != VINF_SUCCESS
641 )
642 {
643 Log(("Invalid ring 0 stack %04X:%08RX32\n", ss_r0, esp_r0));
644 goto failure;
645 }
646 }
647 else
648 if (fConforming || dpl == cpl) /* to the same privilege level */
649 {
650 ss_r0 = pRegFrame->ss.Sel;
651 esp_r0 = pRegFrame->esp;
652
653 if ( eflags.Bits.u1VM /* illegal */
654 || SELMToFlatBySelEx(pVCpu, fakeflags, ss_r0, (RTGCPTR)esp_r0, SELMTOFLAT_FLAGS_CPL1,
655 (PRTGCPTR)&pTrapStackGC, NULL) != VINF_SUCCESS)
656 {
657 AssertMsgFailed(("Invalid stack %04X:%08RX32??? (VM=%d)\n", ss_r0, esp_r0, eflags.Bits.u1VM));
658 goto failure;
659 }
660 }
661 else
662 {
663 Log(("Invalid cpl-dpl combo %d vs %d\n", cpl, dpl));
664 goto failure;
665 }
666 /*
667 * Build trap stack frame on guest handler's stack
668 */
669 uint32_t *pTrapStack;
670#ifdef IN_RC
671 Assert(eflags.Bits.u1VM || (pRegFrame->ss.Sel & X86_SEL_RPL) != 0);
672 /* Check maximum amount we need (10 when executing in V86 mode) */
673 rc = PGMVerifyAccess(pVCpu, (RTGCUINTPTR)pTrapStackGC - 10*sizeof(uint32_t), 10 * sizeof(uint32_t), X86_PTE_RW);
674 pTrapStack = (uint32_t *)(uintptr_t)pTrapStackGC;
675#else
676 Assert(eflags.Bits.u1VM || (pRegFrame->ss.Sel & X86_SEL_RPL) == 0 || (pRegFrame->ss.Sel & X86_SEL_RPL) == 3 || (EMIsRawRing1Enabled(pVM) && (pRegFrame->ss.Sel & X86_SEL_RPL) == 1));
677 /* Check maximum amount we need (10 when executing in V86 mode) */
678 if ((pTrapStackGC >> PAGE_SHIFT) != ((pTrapStackGC - 10*sizeof(uint32_t)) >> PAGE_SHIFT)) /* fail if we cross a page boundary */
679 goto failure;
680 PGMPAGEMAPLOCK PageMappingLock;
681 rc = PGMPhysGCPtr2CCPtr(pVCpu, pTrapStackGC, (void **)&pTrapStack, &PageMappingLock);
682 if (RT_FAILURE(rc))
683 {
684 AssertRC(rc);
685 goto failure;
686 }
687#endif
688 if (rc == VINF_SUCCESS)
689 {
690 /** if eflags.Bits.u1VM then push gs, fs, ds, es */
691 if (eflags.Bits.u1VM)
692 {
693 Log(("TRAP%02X: (VM) Handler %04X:%RGv Stack %04X:%08X RPL=%d CR2=%08X\n", iGate, GuestIdte.Gen.u16SegSel, pHandler, ss_r0, esp_r0, (pRegFrame->ss.Sel & X86_SEL_RPL), pVCpu->trpm.s.uActiveCR2));
694 pTrapStack[--idx] = pRegFrame->gs.Sel;
695 pTrapStack[--idx] = pRegFrame->fs.Sel;
696 pTrapStack[--idx] = pRegFrame->ds.Sel;
697 pTrapStack[--idx] = pRegFrame->es.Sel;
698
699 /* clear ds, es, fs & gs in current context */
700 pRegFrame->ds.Sel = pRegFrame->es.Sel = pRegFrame->fs.Sel = pRegFrame->gs.Sel = 0;
701 }
702 else
703 Log(("TRAP%02X: Handler %04X:%RGv Stack %04X:%08X RPL=%d CR2=%08X\n", iGate, GuestIdte.Gen.u16SegSel, pHandler, ss_r0, esp_r0, (pRegFrame->ss.Sel & X86_SEL_RPL), pVCpu->trpm.s.uActiveCR2));
704
705 if (!fConforming && dpl < cpl)
706 {
707#ifdef IN_RC /* Only in RC we still see tracing of our ring modifications. */
708 if ( (pRegFrame->ss.Sel & X86_SEL_RPL) == 1
709 && !eflags.Bits.u1VM)
710 pTrapStack[--idx] = pRegFrame->ss.Sel & ~1; /* Mask away traces of raw ring 0 execution (ring 1). */
711 else if ( EMIsRawRing1Enabled(pVM)
712 && (pRegFrame->ss.Sel & X86_SEL_RPL) == 2)
713 pTrapStack[--idx] = (pRegFrame->ss.Sel & ~2) | 1; /* Mask away traces of raw ring 1 execution (ring 2). */
714 else
715#endif /* IN_RC */
716 pTrapStack[--idx] = pRegFrame->ss.Sel;
717
718 pTrapStack[--idx] = pRegFrame->esp;
719 }
720
721 /* Note: We use the eflags copy, that includes the virtualized bits! */
722 /* Note: Not really necessary as we grab include those bits in the trap/irq handler trampoline */
723 pTrapStack[--idx] = eflags.u32;
724
725#ifdef IN_RC /* Only in RC mode we still see tracing of our ring modifications */
726 if ( (pRegFrame->cs.Sel & X86_SEL_RPL) == 1
727 && !eflags.Bits.u1VM)
728 pTrapStack[--idx] = pRegFrame->cs.Sel & ~1; /* Mask away traces of raw ring execution (ring 1). */
729 else if ( EMIsRawRing1Enabled(pVM)
730 && (pRegFrame->cs.Sel & X86_SEL_RPL) == 2)
731 pTrapStack[--idx] = (pRegFrame->cs.Sel & ~2) | 1; /* Mask away traces of raw ring 1 execution (ring 2). */
732 else
733#endif /* IN_RC */
734 pTrapStack[--idx] = pRegFrame->cs.Sel;
735
736 if (enmType == TRPM_SOFTWARE_INT)
737 {
738 Assert(cbInstr);
739 pTrapStack[--idx] = pRegFrame->eip + cbInstr; /* return address = next instruction */
740 }
741 else
742 pTrapStack[--idx] = pRegFrame->eip;
743
744 if (enmError == TRPM_TRAP_HAS_ERRORCODE)
745 {
746 pTrapStack[--idx] = pVCpu->trpm.s.uActiveErrorCode;
747 }
748
749 Assert(esp_r0 > -idx*sizeof(uint32_t));
750 /* Adjust ESP accordingly */
751 esp_r0 += idx*sizeof(uint32_t);
752
753 /* Mask away dangerous flags for the trap/interrupt handler. */
754 eflags.u32 &= ~(X86_EFL_TF | X86_EFL_VM | X86_EFL_RF | X86_EFL_NT);
755#ifdef DEBUG
756 if (DBGFIsStepping(pVCpu))
757 eflags.u32 |= X86_EFL_TF;
758#endif
759
760 /* Turn off interrupts for interrupt gates. */
761 if (GuestIdte.Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
762 eflags.Bits.u1IF = 0;
763
764 CPUMRawSetEFlags(pVCpu, eflags.u32);
765
766#ifdef DEBUG
767 for (int j = idx; j < 0; j++)
768 LogFlow(("Stack %RRv pos %02d: %08x\n", &pTrapStack[j], j, pTrapStack[j]));
769
770 Log4(("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n"
771 "eip=%08x esp=%08x ebp=%08x iopl=%d\n"
772 "cs=%04x ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n",
773 pRegFrame->eax, pRegFrame->ebx, pRegFrame->ecx, pRegFrame->edx, pRegFrame->esi, pRegFrame->edi,
774 pRegFrame->eip, pRegFrame->esp, pRegFrame->ebp, eflags.Bits.u2IOPL,
775 pRegFrame->cs.Sel, pRegFrame->ds.Sel, pRegFrame->es.Sel,
776 pRegFrame->fs.Sel, pRegFrame->gs.Sel, eflags.u32));
777#endif
778
779 Log(("TRPM: PATM Handler %RRv Adjusted stack %08X new EFLAGS=%08X/%08x idx=%d dpl=%d cpl=%d\n",
780 pVM->trpm.s.aGuestTrapHandler[iGate], esp_r0, eflags.u32, CPUMRawGetEFlags(pVCpu), idx, dpl, cpl));
781
782 /* Make sure the internal guest context structure is up-to-date. */
783 if (iGate == X86_XCPT_PF)
784 CPUMSetGuestCR2(pVCpu, pVCpu->trpm.s.uActiveCR2);
785
786#ifdef IN_RC
787 /* paranoia */
788 Assert(pRegFrame->eflags.Bits.u1IF == 1);
789 eflags.Bits.u1IF = 1;
790 Assert(pRegFrame->eflags.Bits.u2IOPL == 0);
791 eflags.Bits.u2IOPL = 0;
792
793 Assert(eflags.Bits.u1IF);
794 Assert(eflags.Bits.u2IOPL == 0);
795 STAM_COUNTER_INC(&pVM->trpm.s.CTX_SUFF(paStatForwardedIRQ)[iGate]);
796 STAM_PROFILE_ADV_STOP(&pVM->trpm.s.CTX_SUFF_Z(StatForwardProf), a);
797 if (iOrgTrap >= 0 && iOrgTrap < (int)RT_ELEMENTS(pVM->trpm.s.aStatGCTraps))
798 STAM_PROFILE_ADV_STOP(&pVM->trpm.s.aStatGCTraps[iOrgTrap], o);
799
800 PGMRZDynMapReleaseAutoSet(pVCpu);
801 CPUMGCCallGuestTrapHandler(pRegFrame, GuestIdte.Gen.u16SegSel | 1, pVM->trpm.s.aGuestTrapHandler[iGate],
802 eflags.u32, ss_r0, (RTRCPTR)esp_r0);
803 /* does not return */
804#else
805
806 Assert(!CPUMIsGuestInRawMode(pVCpu));
807 pRegFrame->eflags.u32 = eflags.u32;
808 pRegFrame->eip = pVM->trpm.s.aGuestTrapHandler[iGate];
809 pRegFrame->cs.Sel = GuestIdte.Gen.u16SegSel;
810 pRegFrame->esp = esp_r0;
811 pRegFrame->ss.Sel = ss_r0 & ~X86_SEL_RPL; /* set rpl to ring 0 */
812 STAM_PROFILE_ADV_STOP(&pVM->trpm.s.CTX_SUFF_Z(StatForwardProf), a);
813 PGMPhysReleasePageMappingLock(pVM, &PageMappingLock);
814 NOREF(iOrgTrap);
815 return VINF_SUCCESS;
816#endif
817 }
818 else
819 Log(("TRAP%02X: PGMVerifyAccess %RGv failed with %Rrc -> forward to REM\n", iGate, pTrapStackGC, rc));
820 }
821 else
822 Log(("SELMValidateAndConvertCSAddr failed with %Rrc\n", rc));
823 }
824 else
825 Log(("MMRamRead %RGv size %d failed with %Rrc\n", (RTGCUINTPTR)GCPtrIDT + sizeof(VBOXIDTE) * iGate, sizeof(GuestIdte), rc));
826 }
827 else
828 {
829 Log(("Refused to forward trap: eflags=%08x IF=%d\n", eflags.u32, eflags.Bits.u1IF));
830#ifdef VBOX_WITH_STATISTICS
831 if (pVM->trpm.s.aGuestTrapHandler[iGate] == TRPM_INVALID_HANDLER)
832 STAM_COUNTER_INC(&pVM->trpm.s.StatForwardFailNoHandler);
833 else if (PATMIsPatchGCAddr(pVM, pRegFrame->eip))
834 STAM_COUNTER_INC(&pVM->trpm.s.StatForwardFailPatchAddr);
835#endif
836 }
837failure:
838 STAM_COUNTER_INC(&pVM->trpm.s.CTX_SUFF_Z(StatForwardFail));
839 STAM_PROFILE_ADV_STOP(&pVM->trpm.s.CTX_SUFF_Z(StatForwardProf), a);
840
841 Log(("TRAP%02X: forwarding to REM (ss rpl=%d eflags=%08X VMIF=%d handler=%08X\n", iGate, pRegFrame->ss.Sel & X86_SEL_RPL, pRegFrame->eflags.u32, PATMAreInterruptsEnabledByCtxCore(pVM, pRegFrame), pVM->trpm.s.aGuestTrapHandler[iGate]));
842#endif
843 return VINF_EM_RAW_GUEST_TRAP;
844}
845#endif /* VBOX_WITH_RAW_MODE_NOT_R0 */
846
847
848/**
849 * Raises a cpu exception which doesn't take an error code.
850 *
851 * This function may or may not dispatch the exception before returning.
852 *
853 * @returns VBox status code fit for scheduling.
854 * @retval VINF_EM_RAW_GUEST_TRAP if the exception was left pending.
855 * @retval VINF_TRPM_XCPT_DISPATCHED if the exception was raised and dispatched for raw-mode execution.
856 * @retval VINF_EM_RESCHEDULE_REM if the exception was dispatched and cannot be executed in raw-mode.
857 *
858 * @param pVM Pointer to the VM.
859 * @param pCtxCore The CPU context core.
860 * @param enmXcpt The exception.
861 */
862VMMDECL(int) TRPMRaiseXcpt(PVMCPU pVCpu, PCPUMCTXCORE pCtxCore, X86XCPT enmXcpt)
863{
864 LogFlow(("TRPMRaiseXcptErr: cs:eip=%RTsel:%RX32 enmXcpt=%#x\n", pCtxCore->cs.Sel, pCtxCore->eip, enmXcpt));
865/** @todo dispatch the trap. */
866 pVCpu->trpm.s.uActiveVector = enmXcpt;
867 pVCpu->trpm.s.enmActiveType = TRPM_TRAP;
868 pVCpu->trpm.s.uActiveErrorCode = 0xdeadbeef;
869 pVCpu->trpm.s.uActiveCR2 = 0xdeadface;
870 pVCpu->trpm.s.cbInstr = UINT8_MAX;
871 return VINF_EM_RAW_GUEST_TRAP;
872}
873
874
875/**
876 * Raises a cpu exception with an errorcode.
877 *
878 * This function may or may not dispatch the exception before returning.
879 *
880 * @returns VBox status code fit for scheduling.
881 * @retval VINF_EM_RAW_GUEST_TRAP if the exception was left pending.
882 * @retval VINF_TRPM_XCPT_DISPATCHED if the exception was raised and dispatched for raw-mode execution.
883 * @retval VINF_EM_RESCHEDULE_REM if the exception was dispatched and cannot be executed in raw-mode.
884 *
885 * @param pVM Pointer to the VM.
886 * @param pCtxCore The CPU context core.
887 * @param enmXcpt The exception.
888 * @param uErr The error code.
889 */
890VMMDECL(int) TRPMRaiseXcptErr(PVMCPU pVCpu, PCPUMCTXCORE pCtxCore, X86XCPT enmXcpt, uint32_t uErr)
891{
892 LogFlow(("TRPMRaiseXcptErr: cs:eip=%RTsel:%RX32 enmXcpt=%#x uErr=%RX32\n", pCtxCore->cs.Sel, pCtxCore->eip, enmXcpt, uErr));
893/** @todo dispatch the trap. */
894 pVCpu->trpm.s.uActiveVector = enmXcpt;
895 pVCpu->trpm.s.enmActiveType = TRPM_TRAP;
896 pVCpu->trpm.s.uActiveErrorCode = uErr;
897 pVCpu->trpm.s.uActiveCR2 = 0xdeadface;
898 pVCpu->trpm.s.cbInstr = UINT8_MAX;
899 return VINF_EM_RAW_GUEST_TRAP;
900}
901
902
903/**
904 * Raises a cpu exception with an errorcode and CR2.
905 *
906 * This function may or may not dispatch the exception before returning.
907 *
908 * @returns VBox status code fit for scheduling.
909 * @retval VINF_EM_RAW_GUEST_TRAP if the exception was left pending.
910 * @retval VINF_TRPM_XCPT_DISPATCHED if the exception was raised and dispatched for raw-mode execution.
911 * @retval VINF_EM_RESCHEDULE_REM if the exception was dispatched and cannot be executed in raw-mode.
912 *
913 * @param pVM Pointer to the VM.
914 * @param pCtxCore The CPU context core.
915 * @param enmXcpt The exception.
916 * @param uErr The error code.
917 * @param uCR2 The CR2 value.
918 */
919VMMDECL(int) TRPMRaiseXcptErrCR2(PVMCPU pVCpu, PCPUMCTXCORE pCtxCore, X86XCPT enmXcpt, uint32_t uErr, RTGCUINTPTR uCR2)
920{
921 LogFlow(("TRPMRaiseXcptErr: cs:eip=%RTsel:%RX32 enmXcpt=%#x uErr=%RX32 uCR2=%RGv\n", pCtxCore->cs.Sel, pCtxCore->eip, enmXcpt, uErr, uCR2));
922/** @todo dispatch the trap. */
923 pVCpu->trpm.s.uActiveVector = enmXcpt;
924 pVCpu->trpm.s.enmActiveType = TRPM_TRAP;
925 pVCpu->trpm.s.uActiveErrorCode = uErr;
926 pVCpu->trpm.s.uActiveCR2 = uCR2;
927 pVCpu->trpm.s.cbInstr = UINT8_MAX;
928 return VINF_EM_RAW_GUEST_TRAP;
929}
930
931
932#ifdef VBOX_WITH_RAW_MODE
933/**
934 * Clear guest trap/interrupt gate handler
935 *
936 * @returns VBox status code.
937 * @param pVM Pointer to the VM.
938 * @param iTrap Interrupt/trap number.
939 */
940VMMDECL(int) trpmClearGuestTrapHandler(PVM pVM, unsigned iTrap)
941{
942 /*
943 * Validate.
944 */
945 if (iTrap >= RT_ELEMENTS(pVM->trpm.s.aIdt))
946 {
947 AssertMsg(iTrap < TRPM_HANDLER_INT_BASE, ("Illegal gate number %d!\n", iTrap));
948 return VERR_INVALID_PARAMETER;
949 }
950
951 if (ASMBitTest(&pVM->trpm.s.au32IdtPatched[0], iTrap))
952# ifdef IN_RING3
953 trpmR3ClearPassThroughHandler(pVM, iTrap);
954# else
955 AssertFailed();
956# endif
957
958 pVM->trpm.s.aGuestTrapHandler[iTrap] = TRPM_INVALID_HANDLER;
959 return VINF_SUCCESS;
960}
961#endif /* VBOX_WITH_RAW_MODE */
962
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette