VirtualBox

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

Last change on this file since 45852 was 45728, checked in by vboxsync, 12 years ago

TRPM: Sprinkled HMIsEnabled around the place.

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