VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PDMAll.cpp@ 60632

Last change on this file since 60632 was 60573, checked in by vboxsync, 9 years ago

VMM/APIC: Fix INIT IPI handling, and handle callers of PDMGetInterrupt() expecting a valid
vector which may not be the case with the new APIC code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.1 KB
Line 
1/* $Id: PDMAll.cpp 60573 2016-04-19 13:27:45Z vboxsync $ */
2/** @file
3 * PDM Critical Sections
4 */
5
6/*
7 * Copyright (C) 2006-2015 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_PDM
23#include "PDMInternal.h"
24#include <VBox/vmm/pdm.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/vm.h>
27#include <VBox/err.h>
28
29#include <VBox/log.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32
33#include "PDMInline.h"
34#include "dtrace/VBoxVMM.h"
35
36
37
38/**
39 * Gets the pending interrupt.
40 *
41 * @returns VBox status code.
42 * @param pVCpu The cross context virtual CPU structure.
43 * @param pu8Interrupt Where to store the interrupt on success.
44 */
45VMMDECL(int) PDMGetInterrupt(PVMCPU pVCpu, uint8_t *pu8Interrupt)
46{
47 PVM pVM = pVCpu->CTX_SUFF(pVM);
48
49#ifndef VBOX_WITH_NEW_APIC
50 pdmLock(pVM);
51#endif
52
53 /*
54 * The local APIC has a higher priority than the PIC.
55 */
56 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC))
57 {
58 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC);
59 Assert(pVM->pdm.s.Apic.CTX_SUFF(pDevIns));
60 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnGetInterrupt));
61 uint32_t uTagSrc;
62 int i = pVM->pdm.s.Apic.CTX_SUFF(pfnGetInterrupt)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu, &uTagSrc);
63#ifndef VBOX_WITH_NEW_APIC
64 AssertMsg(i <= 255 && i >= 0, ("i=%d\n", i));
65#endif
66 if (i >= 0)
67 {
68#ifndef VBOX_WITH_NEW_APIC
69 pdmUnlock(pVM);
70#endif
71 *pu8Interrupt = (uint8_t)i;
72 VBOXVMM_PDM_IRQ_GET(pVCpu, RT_LOWORD(uTagSrc), RT_HIWORD(uTagSrc), i);
73 return VINF_SUCCESS;
74 }
75 }
76
77#ifdef VBOX_WITH_NEW_APIC
78 pdmLock(pVM);
79#endif
80
81 /*
82 * Check the PIC.
83 */
84 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC))
85 {
86 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_PIC);
87 Assert(pVM->pdm.s.Pic.CTX_SUFF(pDevIns));
88 Assert(pVM->pdm.s.Pic.CTX_SUFF(pfnGetInterrupt));
89 uint32_t uTagSrc;
90 int i = pVM->pdm.s.Pic.CTX_SUFF(pfnGetInterrupt)(pVM->pdm.s.Pic.CTX_SUFF(pDevIns), &uTagSrc);
91 AssertMsg(i <= 255 && i >= 0, ("i=%d\n", i));
92 if (i >= 0)
93 {
94 pdmUnlock(pVM);
95 *pu8Interrupt = (uint8_t)i;
96 VBOXVMM_PDM_IRQ_GET(pVCpu, RT_LOWORD(uTagSrc), RT_HIWORD(uTagSrc), i);
97 return VINF_SUCCESS;
98 }
99 }
100
101 /** @todo Figure out exactly why we can get here without anything being set. (REM) */
102
103 pdmUnlock(pVM);
104 return VERR_NO_DATA;
105}
106
107
108/**
109 * Sets the pending interrupt coming from ISA source or HPET.
110 *
111 * @returns VBox status code.
112 * @param pVM The cross context VM structure.
113 * @param u8Irq The IRQ line.
114 * @param u8Level The new level.
115 * @param uTagSrc The IRQ tag and source tracer ID.
116 */
117VMMDECL(int) PDMIsaSetIrq(PVM pVM, uint8_t u8Irq, uint8_t u8Level, uint32_t uTagSrc)
118{
119 pdmLock(pVM);
120
121 /** @todo put the IRQ13 code elsewhere to avoid this unnecessary bloat. */
122 if (!uTagSrc && (u8Level & PDM_IRQ_LEVEL_HIGH)) /* FPU IRQ */
123 {
124 if (u8Level == PDM_IRQ_LEVEL_HIGH)
125 VBOXVMM_PDM_IRQ_HIGH(VMMGetCpu(pVM), 0, 0);
126 else
127 VBOXVMM_PDM_IRQ_HILO(VMMGetCpu(pVM), 0, 0);
128 }
129
130 int rc = VERR_PDM_NO_PIC_INSTANCE;
131 if (pVM->pdm.s.Pic.CTX_SUFF(pDevIns))
132 {
133 Assert(pVM->pdm.s.Pic.CTX_SUFF(pfnSetIrq));
134 pVM->pdm.s.Pic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.Pic.CTX_SUFF(pDevIns), u8Irq, u8Level, uTagSrc);
135 rc = VINF_SUCCESS;
136 }
137
138 if (pVM->pdm.s.IoApic.CTX_SUFF(pDevIns))
139 {
140 Assert(pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq));
141
142 /*
143 * Apply Interrupt Source Override rules.
144 * See ACPI 4.0 specification 5.2.12.4 and 5.2.12.5 for details on
145 * interrupt source override.
146 * Shortly, ISA IRQ0 is electically connected to pin 2 on IO-APIC, and some OSes,
147 * notably recent OS X rely upon this configuration.
148 * If changing, also update override rules in MADT and MPS.
149 */
150 /* ISA IRQ0 routed to pin 2, all others ISA sources are identity mapped */
151 if (u8Irq == 0)
152 u8Irq = 2;
153
154 pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.IoApic.CTX_SUFF(pDevIns), u8Irq, u8Level, uTagSrc);
155 rc = VINF_SUCCESS;
156 }
157
158 if (!uTagSrc && u8Level == PDM_IRQ_LEVEL_LOW)
159 VBOXVMM_PDM_IRQ_LOW(VMMGetCpu(pVM), 0, 0);
160 pdmUnlock(pVM);
161 return rc;
162}
163
164
165/**
166 * Sets the pending I/O APIC interrupt.
167 *
168 * @returns VBox status code.
169 * @param pVM The cross context VM structure.
170 * @param u8Irq The IRQ line.
171 * @param u8Level The new level.
172 * @param uTagSrc The IRQ tag and source tracer ID.
173 */
174VMM_INT_DECL(int) PDMIoApicSetIrq(PVM pVM, uint8_t u8Irq, uint8_t u8Level, uint32_t uTagSrc)
175{
176 if (pVM->pdm.s.IoApic.CTX_SUFF(pDevIns))
177 {
178 Assert(pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq));
179 pdmLock(pVM);
180 pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.IoApic.CTX_SUFF(pDevIns), u8Irq, u8Level, uTagSrc);
181 pdmUnlock(pVM);
182 return VINF_SUCCESS;
183 }
184 return VERR_PDM_NO_PIC_INSTANCE;
185}
186
187/**
188 * Send a MSI to an I/O APIC.
189 *
190 * @returns VBox status code.
191 * @param pVM The cross context VM structure.
192 * @param GCAddr Request address.
193 * @param uValue Request value.
194 * @param uTagSrc The IRQ tag and source tracer ID.
195 */
196VMM_INT_DECL(int) PDMIoApicSendMsi(PVM pVM, RTGCPHYS GCAddr, uint32_t uValue, uint32_t uTagSrc)
197{
198 if (pVM->pdm.s.IoApic.CTX_SUFF(pDevIns))
199 {
200 Assert(pVM->pdm.s.IoApic.CTX_SUFF(pfnSendMsi));
201 pdmLock(pVM);
202 pVM->pdm.s.IoApic.CTX_SUFF(pfnSendMsi)(pVM->pdm.s.IoApic.CTX_SUFF(pDevIns), GCAddr, uValue, uTagSrc);
203 pdmUnlock(pVM);
204 return VINF_SUCCESS;
205 }
206 return VERR_PDM_NO_PIC_INSTANCE;
207}
208
209
210
211/**
212 * Returns the presence of an IO-APIC.
213 *
214 * @returns VBox true if an IO-APIC is present.
215 * @param pVM The cross context VM structure.
216 */
217VMM_INT_DECL(bool) PDMHasIoApic(PVM pVM)
218{
219 return pVM->pdm.s.IoApic.CTX_SUFF(pDevIns) != NULL;
220}
221
222
223/**
224 * Returns the presence of a Local APIC.
225 *
226 * @returns VBox true if a Local APIC is present.
227 * @param pVM The cross context VM structure.
228 */
229VMM_INT_DECL(bool) PDMHasApic(PVM pVM)
230{
231 return pVM->pdm.s.Apic.CTX_SUFF(pDevIns) != NULL;
232}
233
234
235/**
236 * Set the APIC base.
237 *
238 * @returns Strict VBox status code.
239 * @param pVCpu The cross context virtual CPU structure.
240 * @param u64Base The new base.
241 */
242VMMDECL(VBOXSTRICTRC) PDMApicSetBaseMsr(PVMCPU pVCpu, uint64_t u64Base)
243{
244 PVM pVM = pVCpu->CTX_SUFF(pVM);
245 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
246 {
247 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnSetBaseMsr));
248#ifndef VBOX_WITH_NEW_APIC
249 pdmLock(pVM);
250#endif
251 VBOXSTRICTRC rcStrict = pVM->pdm.s.Apic.CTX_SUFF(pfnSetBaseMsr)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu, u64Base);
252
253 /* Update CPUM's copy of the APIC base. */
254 PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
255 Assert(pCtx);
256 pCtx->msrApicBase = pVM->pdm.s.Apic.CTX_SUFF(pfnGetBaseMsr)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu);
257
258#ifndef VBOX_WITH_NEW_APIC
259 pdmUnlock(pVM);
260#endif
261 return rcStrict;
262 }
263
264#ifdef IN_RING3
265 LogRelMax(5, ("PDM: APIC%U: Writing APIC base MSR (%#x) invalid since there isn't an APIC -> #GP(0)\n", pVCpu->idCpu,
266 MSR_IA32_APICBASE));
267 return VERR_CPUM_RAISE_GP_0;
268#else
269 return VINF_CPUM_R3_MSR_WRITE;
270#endif
271}
272
273
274/**
275 * Get the APIC base MSR from the APIC device.
276 *
277 * @returns Strict VBox status code.
278 * @param pVCpu The cross context virtual CPU structure.
279 * @param pu64Base Where to store the APIC base.
280 * @param fIgnoreErrors Whether to ignore errors (i.e. not a real guest MSR
281 * access).
282 */
283VMMDECL(VBOXSTRICTRC) PDMApicGetBaseMsr(PVMCPU pVCpu, uint64_t *pu64Base, bool fIgnoreErrors)
284{
285 PVM pVM = pVCpu->CTX_SUFF(pVM);
286 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
287 {
288 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnGetBaseMsr));
289#ifdef VBOX_WITH_NEW_APIC
290 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
291 *pu64Base = pVM->pdm.s.Apic.CTX_SUFF(pfnGetBaseMsr)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu);
292#else
293 pdmLock(pVM);
294 *pu64Base = pVM->pdm.s.Apic.CTX_SUFF(pfnGetBaseMsr)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu);
295 pdmUnlock(pVM);
296#endif
297 return VINF_SUCCESS;
298 }
299
300 *pu64Base = 0;
301 if (fIgnoreErrors)
302 return VINF_SUCCESS;
303
304#ifdef IN_RING3
305 LogRelMax(5, ("PDM: APIC%u: Reading APIC base MSR (%#x) invalid without an APIC instance -> #GP(0)\n", pVCpu->idCpu,
306 MSR_IA32_APICBASE));
307 return VERR_CPUM_RAISE_GP_0;
308#else
309 return VINF_CPUM_R3_MSR_WRITE;
310#endif
311}
312
313
314/**
315 * Check if the APIC has a pending interrupt/if a TPR change would active one.
316 *
317 * @returns VINF_SUCCESS or VERR_PDM_NO_APIC_INSTANCE.
318 * @param pVCpu The cross context virtual CPU structure.
319 * @param pfPending Pending state (out).
320 */
321VMM_INT_DECL(int) PDMApicHasPendingIrq(PVMCPU pVCpu, bool *pfPending)
322{
323 PVM pVM = pVCpu->CTX_SUFF(pVM);
324 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
325 {
326 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnHasPendingIrq));
327 pdmLock(pVM);
328 *pfPending = pVM->pdm.s.Apic.CTX_SUFF(pfnHasPendingIrq)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu, NULL /*pu8PendingIrq*/);
329 pdmUnlock(pVM);
330 return VINF_SUCCESS;
331 }
332 return VERR_PDM_NO_APIC_INSTANCE;
333}
334
335
336/**
337 * Set the TPR (task priority register).
338 *
339 * @returns VBox status code.
340 * @param pVCpu The cross context virtual CPU structure.
341 * @param u8TPR The new TPR.
342 */
343VMMDECL(int) PDMApicSetTPR(PVMCPU pVCpu, uint8_t u8TPR)
344{
345 PVM pVM = pVCpu->CTX_SUFF(pVM);
346 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
347 {
348 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnSetTpr));
349 pdmLock(pVM);
350 pVM->pdm.s.Apic.CTX_SUFF(pfnSetTpr)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu, u8TPR);
351 pdmUnlock(pVM);
352 return VINF_SUCCESS;
353 }
354 return VERR_PDM_NO_APIC_INSTANCE;
355}
356
357
358/**
359 * Get the TPR (task priority register).
360 *
361 * @returns VINF_SUCCESS or VERR_PDM_NO_APIC_INSTANCE.
362 * @param pVCpu The cross context virtual CPU structure.
363 * @param pu8TPR Where to store the TRP.
364 * @param pfPending Pending interrupt state (out, optional).
365 * @param pu8PendingIrq Where to store the highest-priority pending IRQ
366 * (out, optional).
367 *
368 * @remarks No-long-jump zone!!!
369 */
370VMMDECL(int) PDMApicGetTPR(PVMCPU pVCpu, uint8_t *pu8TPR, bool *pfPending, uint8_t *pu8PendingIrq)
371{
372 PVM pVM = pVCpu->CTX_SUFF(pVM);
373 PPDMDEVINS pApicIns = pVM->pdm.s.Apic.CTX_SUFF(pDevIns);
374 if (pApicIns)
375 {
376 /*
377 * Note! We don't acquire the PDM lock here as we're just reading
378 * information. Doing so causes massive contention as this
379 * function is called very often by each and every VCPU.
380 */
381 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnGetTpr));
382 *pu8TPR = pVM->pdm.s.Apic.CTX_SUFF(pfnGetTpr)(pApicIns, pVCpu);
383 if (pfPending)
384 *pfPending = pVM->pdm.s.Apic.CTX_SUFF(pfnHasPendingIrq)(pApicIns, pVCpu, pu8PendingIrq);
385 return VINF_SUCCESS;
386 }
387 *pu8TPR = 0;
388 return VERR_PDM_NO_APIC_INSTANCE;
389}
390
391
392/**
393 * Write a MSR in APIC range.
394 *
395 * @returns Strict VBox status code.
396 * @param pVCpu The cross context virtual CPU structure.
397 * @param u32Reg MSR to write.
398 * @param u64Value Value to write.
399 */
400VMM_INT_DECL(VBOXSTRICTRC) PDMApicWriteMsr(PVMCPU pVCpu, uint32_t u32Reg, uint64_t u64Value)
401{
402 PVM pVM = pVCpu->CTX_SUFF(pVM);
403 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
404 {
405 AssertPtr(pVM->pdm.s.Apic.CTX_SUFF(pfnWriteMsr));
406 return pVM->pdm.s.Apic.CTX_SUFF(pfnWriteMsr)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu, u32Reg, u64Value);
407 }
408 return VERR_CPUM_RAISE_GP_0;
409}
410
411
412/**
413 * Read a MSR in APIC range.
414 *
415 * @returns Strict VBox status code.
416 * @param pVCpu The cross context virtual CPU structure.
417 * @param u32Reg MSR to read.
418 * @param pu64Value Where to store the value read.
419 */
420VMM_INT_DECL(VBOXSTRICTRC) PDMApicReadMsr(PVMCPU pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
421{
422 PVM pVM = pVCpu->CTX_SUFF(pVM);
423 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
424 {
425 AssertPtr(pVM->pdm.s.Apic.CTX_SUFF(pfnReadMsr));
426 return pVM->pdm.s.Apic.CTX_SUFF(pfnReadMsr)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu, u32Reg, pu64Value);
427 }
428 return VERR_CPUM_RAISE_GP_0;
429}
430
431
432/**
433 * Gets the frequency of the APIC timer.
434 *
435 * @returns VBox status code.
436 * @param pVM The cross context VM structure.
437 * @param pu64Value Where to store the frequency.
438 */
439VMM_INT_DECL(int) PDMApicGetTimerFreq(PVM pVM, uint64_t *pu64Value)
440{
441 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
442 {
443 AssertPtr(pVM->pdm.s.Apic.CTX_SUFF(pfnGetTimerFreq));
444 *pu64Value = pVM->pdm.s.Apic.CTX_SUFF(pfnGetTimerFreq)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns));
445 return VINF_SUCCESS;
446 }
447 return VERR_PDM_NO_APIC_INSTANCE;
448}
449
450
451/**
452 * Locks PDM.
453 * This might call back to Ring-3 in order to deal with lock contention in GC and R3.
454 *
455 * @param pVM The cross context VM structure.
456 */
457void pdmLock(PVM pVM)
458{
459#ifdef IN_RING3
460 int rc = PDMCritSectEnter(&pVM->pdm.s.CritSect, VERR_IGNORED);
461#else
462 int rc = PDMCritSectEnter(&pVM->pdm.s.CritSect, VERR_GENERAL_FAILURE);
463 if (rc == VERR_GENERAL_FAILURE)
464 rc = VMMRZCallRing3NoCpu(pVM, VMMCALLRING3_PDM_LOCK, 0);
465#endif
466 AssertRC(rc);
467}
468
469
470/**
471 * Locks PDM but don't go to ring-3 if it's owned by someone.
472 *
473 * @returns VINF_SUCCESS on success.
474 * @returns rc if we're in GC or R0 and can't get the lock.
475 * @param pVM The cross context VM structure.
476 * @param rc The RC to return in GC or R0 when we can't get the lock.
477 */
478int pdmLockEx(PVM pVM, int rc)
479{
480 return PDMCritSectEnter(&pVM->pdm.s.CritSect, rc);
481}
482
483
484/**
485 * Unlocks PDM.
486 *
487 * @param pVM The cross context VM structure.
488 */
489void pdmUnlock(PVM pVM)
490{
491 PDMCritSectLeave(&pVM->pdm.s.CritSect);
492}
493
494
495/**
496 * Converts ring 3 VMM heap pointer to a guest physical address
497 *
498 * @returns VBox status code.
499 * @param pVM The cross context VM structure.
500 * @param pv Ring-3 pointer.
501 * @param pGCPhys GC phys address (out).
502 */
503VMM_INT_DECL(int) PDMVmmDevHeapR3ToGCPhys(PVM pVM, RTR3PTR pv, RTGCPHYS *pGCPhys)
504{
505 if (RT_LIKELY(pVM->pdm.s.GCPhysVMMDevHeap != NIL_RTGCPHYS))
506 {
507 RTR3UINTPTR const offHeap = (RTR3UINTPTR)pv - (RTR3UINTPTR)pVM->pdm.s.pvVMMDevHeap;
508 if (RT_LIKELY(offHeap < pVM->pdm.s.cbVMMDevHeap))
509 {
510 *pGCPhys = pVM->pdm.s.GCPhysVMMDevHeap + offHeap;
511 return VINF_SUCCESS;
512 }
513
514 /* Don't assert here as this is called before we can catch ring-0 assertions. */
515 Log(("PDMVmmDevHeapR3ToGCPhys: pv=%p pvVMMDevHeap=%p cbVMMDevHeap=%#x\n",
516 pv, pVM->pdm.s.pvVMMDevHeap, pVM->pdm.s.cbVMMDevHeap));
517 }
518 else
519 Log(("PDMVmmDevHeapR3ToGCPhys: GCPhysVMMDevHeap=%RGp (pv=%p)\n", pVM->pdm.s.GCPhysVMMDevHeap, pv));
520 return VERR_PDM_DEV_HEAP_R3_TO_GCPHYS;
521}
522
523
524/**
525 * Checks if the vmm device heap is enabled (== vmm device's pci region mapped)
526 *
527 * @returns dev heap enabled status (true/false)
528 * @param pVM The cross context VM structure.
529 */
530VMM_INT_DECL(bool) PDMVmmDevHeapIsEnabled(PVM pVM)
531{
532 return pVM->pdm.s.GCPhysVMMDevHeap != NIL_RTGCPHYS;
533}
Note: See TracBrowser for help on using the repository browser.

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