VirtualBox

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

Last change on this file since 99654 was 99576, checked in by vboxsync, 20 months ago

VMM: Preparations for getting interrupts injected into the guest. With ARMv8 there are two types of interrupts (normal interrupts and fast interrupts) which need to be mapped to forced action flags. Because the PIC and APIC flags are not needed those are mapped to IRQs and FIQs on ARM respectively, bugref:10389

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.9 KB
Line 
1/* $Id: PDMAll.cpp 99576 2023-05-03 10:24:27Z vboxsync $ */
2/** @file
3 * PDM Critical Sections
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_PDM
33#include "PDMInternal.h"
34#include <VBox/vmm/pdm.h>
35#include <VBox/vmm/mm.h>
36#include <VBox/vmm/vmcc.h>
37#include <VBox/err.h>
38#include <VBox/vmm/apic.h>
39
40#include <VBox/log.h>
41#include <iprt/asm.h>
42#include <iprt/assert.h>
43
44#include "PDMInline.h"
45#include "dtrace/VBoxVMM.h"
46
47
48#if !defined(VBOX_VMM_TARGET_ARMV8)
49/**
50 * Gets the pending interrupt.
51 *
52 * @returns VBox status code.
53 * @retval VINF_SUCCESS on success.
54 * @retval VERR_APIC_INTR_MASKED_BY_TPR when an APIC interrupt is pending but
55 * can't be delivered due to TPR priority.
56 * @retval VERR_NO_DATA if there is no interrupt to be delivered (either APIC
57 * has been software-disabled since it flagged something was pending,
58 * or other reasons).
59 *
60 * @param pVCpu The cross context virtual CPU structure.
61 * @param pu8Interrupt Where to store the interrupt.
62 */
63VMMDECL(int) PDMGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Interrupt)
64{
65 /*
66 * The local APIC has a higher priority than the PIC.
67 */
68 int rc = VERR_NO_DATA;
69 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC))
70 {
71 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC);
72
73 uint32_t uTagSrc;
74 rc = APICGetInterrupt(pVCpu, pu8Interrupt, &uTagSrc);
75 if (RT_SUCCESS(rc))
76 {
77 VBOXVMM_PDM_IRQ_GET(pVCpu, RT_LOWORD(uTagSrc), RT_HIWORD(uTagSrc), *pu8Interrupt);
78 Log8(("PDMGetInterrupt: irq=%#x tag=%#x (apic)\n", *pu8Interrupt, uTagSrc));
79 return VINF_SUCCESS;
80 }
81 /* else if it's masked by TPR/PPR/whatever, go ahead checking the PIC. Such masked
82 interrupts shouldn't prevent ExtINT from being delivered. */
83 }
84
85 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
86 pdmLock(pVM);
87
88 /*
89 * Check the PIC.
90 */
91 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC))
92 {
93 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_PIC);
94 Assert(pVM->pdm.s.Pic.CTX_SUFF(pDevIns));
95 Assert(pVM->pdm.s.Pic.CTX_SUFF(pfnGetInterrupt));
96 uint32_t uTagSrc;
97 int i = pVM->pdm.s.Pic.CTX_SUFF(pfnGetInterrupt)(pVM->pdm.s.Pic.CTX_SUFF(pDevIns), &uTagSrc);
98 AssertMsg(i <= 255 && i >= 0, ("i=%d\n", i));
99 if (i >= 0)
100 {
101 pdmUnlock(pVM);
102 *pu8Interrupt = (uint8_t)i;
103 VBOXVMM_PDM_IRQ_GET(pVCpu, RT_LOWORD(uTagSrc), RT_HIWORD(uTagSrc), i);
104 Log8(("PDMGetInterrupt: irq=%#x tag=%#x (pic)\n", i, uTagSrc));
105 return VINF_SUCCESS;
106 }
107 }
108
109 /*
110 * One scenario where we may possibly get here is if the APIC signaled a pending interrupt,
111 * got an APIC MMIO/MSR VM-exit which disabled the APIC. We could, in theory, clear the APIC
112 * force-flag from all the places which disables the APIC but letting PDMGetInterrupt() fail
113 * without returning a valid interrupt still needs to be handled for the TPR masked case,
114 * so we shall just handle it here regardless if we choose to update the APIC code in the future.
115 */
116
117 pdmUnlock(pVM);
118 return rc;
119}
120#endif
121
122
123/**
124 * Sets the pending interrupt coming from ISA source or HPET.
125 *
126 * @returns VBox status code.
127 * @param pVM The cross context VM structure.
128 * @param u8Irq The IRQ line.
129 * @param u8Level The new level.
130 * @param uTagSrc The IRQ tag and source tracer ID.
131 */
132VMMDECL(int) PDMIsaSetIrq(PVMCC pVM, uint8_t u8Irq, uint8_t u8Level, uint32_t uTagSrc)
133{
134 pdmLock(pVM);
135
136 /** @todo put the IRQ13 code elsewhere to avoid this unnecessary bloat. */
137 if (!uTagSrc && (u8Level & PDM_IRQ_LEVEL_HIGH)) /* FPU IRQ */
138 {
139 if (u8Level == PDM_IRQ_LEVEL_HIGH)
140 VBOXVMM_PDM_IRQ_HIGH(VMMGetCpu(pVM), 0, 0);
141 else
142 VBOXVMM_PDM_IRQ_HILO(VMMGetCpu(pVM), 0, 0);
143 }
144 Log9(("PDMIsaSetIrq: irq=%#x lvl=%u tag=%#x\n", u8Irq, u8Level, uTagSrc));
145
146 int rc = VERR_PDM_NO_PIC_INSTANCE;
147/** @todo r=bird: This code is incorrect, as it ASSUMES the PIC and I/O APIC
148 * are always ring-0 enabled! */
149 if (pVM->pdm.s.Pic.CTX_SUFF(pDevIns))
150 {
151 Assert(pVM->pdm.s.Pic.CTX_SUFF(pfnSetIrq));
152 pVM->pdm.s.Pic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.Pic.CTX_SUFF(pDevIns), u8Irq, u8Level, uTagSrc);
153 rc = VINF_SUCCESS;
154 }
155
156 if (pVM->pdm.s.IoApic.CTX_SUFF(pDevIns))
157 {
158 Assert(pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq));
159
160 /*
161 * Apply Interrupt Source Override rules.
162 * See ACPI 4.0 specification 5.2.12.4 and 5.2.12.5 for details on
163 * interrupt source override.
164 * Shortly, ISA IRQ0 is electically connected to pin 2 on IO-APIC, and some OSes,
165 * notably recent OS X rely upon this configuration.
166 * If changing, also update override rules in MADT and MPS.
167 */
168 /* ISA IRQ0 routed to pin 2, all others ISA sources are identity mapped */
169 if (u8Irq == 0)
170 u8Irq = 2;
171
172 pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.IoApic.CTX_SUFF(pDevIns), NIL_PCIBDF, u8Irq, u8Level, uTagSrc);
173 rc = VINF_SUCCESS;
174 }
175
176 if (!uTagSrc && u8Level == PDM_IRQ_LEVEL_LOW)
177 VBOXVMM_PDM_IRQ_LOW(VMMGetCpu(pVM), 0, 0);
178 pdmUnlock(pVM);
179 return rc;
180}
181
182
183/**
184 * Sets the pending I/O APIC interrupt.
185 *
186 * @returns VBox status code.
187 * @param pVM The cross context VM structure.
188 * @param u8Irq The IRQ line.
189 * @param uBusDevFn The bus:device:function of the device initiating the IRQ.
190 * Pass NIL_PCIBDF when it's not a PCI device or interrupt.
191 * @param u8Level The new level.
192 * @param uTagSrc The IRQ tag and source tracer ID.
193 */
194VMM_INT_DECL(int) PDMIoApicSetIrq(PVM pVM, PCIBDF uBusDevFn, uint8_t u8Irq, uint8_t u8Level, uint32_t uTagSrc)
195{
196 Log9(("PDMIoApicSetIrq: irq=%#x lvl=%u tag=%#x src=%#x\n", u8Irq, u8Level, uTagSrc, uBusDevFn));
197 if (pVM->pdm.s.IoApic.CTX_SUFF(pDevIns))
198 {
199 Assert(pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq));
200 pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.IoApic.CTX_SUFF(pDevIns), uBusDevFn, u8Irq, u8Level, uTagSrc);
201 return VINF_SUCCESS;
202 }
203 return VERR_PDM_NO_PIC_INSTANCE;
204}
205
206
207/**
208 * Broadcasts an EOI to the I/O APIC(s).
209 *
210 * @param pVM The cross context VM structure.
211 * @param uVector The interrupt vector corresponding to the EOI.
212 */
213VMM_INT_DECL(void) PDMIoApicBroadcastEoi(PVMCC pVM, uint8_t uVector)
214{
215 /*
216 * At present, we support only a maximum of one I/O APIC per-VM. If we ever implement having
217 * multiple I/O APICs per-VM, we'll have to broadcast this EOI to all of the I/O APICs.
218 */
219 PCPDMIOAPIC pIoApic = &pVM->pdm.s.IoApic;
220#ifdef IN_RING0
221 if (pIoApic->pDevInsR0)
222 {
223 Assert(pIoApic->pfnSetEoiR0);
224 pIoApic->pfnSetEoiR0(pIoApic->pDevInsR0, uVector);
225 }
226 else if (pIoApic->pDevInsR3)
227 {
228 /* Queue for ring-3 execution. */
229 PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pVM, pVM->pdm.s.hDevHlpQueue, pVM);
230 if (pTask)
231 {
232 pTask->enmOp = PDMDEVHLPTASKOP_IOAPIC_SET_EOI;
233 pTask->pDevInsR3 = NIL_RTR3PTR; /* not required */
234 pTask->u.IoApicSetEoi.uVector = uVector;
235 PDMQueueInsert(pVM, pVM->pdm.s.hDevHlpQueue, pVM, &pTask->Core);
236 }
237 else
238 AssertMsgFailed(("We're out of devhlp queue items!!!\n"));
239 }
240#else
241 if (pIoApic->pDevInsR3)
242 {
243 Assert(pIoApic->pfnSetEoiR3);
244 pIoApic->pfnSetEoiR3(pIoApic->pDevInsR3, uVector);
245 }
246#endif
247}
248
249
250/**
251 * Send a MSI to an I/O APIC.
252 *
253 * @param pVM The cross context VM structure.
254 * @param uBusDevFn The bus:device:function of the device initiating the MSI.
255 * @param pMsi The MSI to send.
256 * @param uTagSrc The IRQ tag and source tracer ID.
257 */
258VMM_INT_DECL(void) PDMIoApicSendMsi(PVMCC pVM, PCIBDF uBusDevFn, PCMSIMSG pMsi, uint32_t uTagSrc)
259{
260 Log9(("PDMIoApicSendMsi: addr=%#RX64 data=%#RX32 tag=%#x src=%#x\n", pMsi->Addr.u64, pMsi->Data.u32, uTagSrc, uBusDevFn));
261 PCPDMIOAPIC pIoApic = &pVM->pdm.s.IoApic;
262#ifdef IN_RING0
263 if (pIoApic->pDevInsR0)
264 pIoApic->pfnSendMsiR0(pIoApic->pDevInsR0, uBusDevFn, pMsi, uTagSrc);
265 else if (pIoApic->pDevInsR3)
266 {
267 /* Queue for ring-3 execution. */
268 PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pVM, pVM->pdm.s.hDevHlpQueue, pVM);
269 if (pTask)
270 {
271 pTask->enmOp = PDMDEVHLPTASKOP_IOAPIC_SEND_MSI;
272 pTask->pDevInsR3 = NIL_RTR3PTR; /* not required */
273 pTask->u.IoApicSendMsi.uBusDevFn = uBusDevFn;
274 pTask->u.IoApicSendMsi.Msi = *pMsi;
275 pTask->u.IoApicSendMsi.uTagSrc = uTagSrc;
276 PDMQueueInsert(pVM, pVM->pdm.s.hDevHlpQueue, pVM, &pTask->Core);
277 }
278 else
279 AssertMsgFailed(("We're out of devhlp queue items!!!\n"));
280 }
281#else
282 if (pIoApic->pDevInsR3)
283 {
284 Assert(pIoApic->pfnSendMsiR3);
285 pIoApic->pfnSendMsiR3(pIoApic->pDevInsR3, uBusDevFn, pMsi, uTagSrc);
286 }
287#endif
288}
289
290
291
292/**
293 * Returns the presence of an IO-APIC.
294 *
295 * @returns true if an IO-APIC is present.
296 * @param pVM The cross context VM structure.
297 */
298VMM_INT_DECL(bool) PDMHasIoApic(PVM pVM)
299{
300 return pVM->pdm.s.IoApic.pDevInsR3 != NULL;
301}
302
303
304/**
305 * Returns the presence of an APIC.
306 *
307 * @returns true if an APIC is present.
308 * @param pVM The cross context VM structure.
309 */
310VMM_INT_DECL(bool) PDMHasApic(PVM pVM)
311{
312 return pVM->pdm.s.Apic.pDevInsR3 != NIL_RTR3PTR;
313}
314
315
316/**
317 * Translates a ring-0 device instance index to a pointer.
318 *
319 * This is used by PGM for device access handlers.
320 *
321 * @returns Device instance pointer if valid index, otherwise NULL (asserted).
322 * @param pVM The cross context VM structure.
323 * @param idxR0Device The ring-0 device instance index.
324 */
325VMM_INT_DECL(PPDMDEVINS) PDMDeviceRing0IdxToInstance(PVMCC pVM, uint64_t idxR0Device)
326{
327#ifdef IN_RING0
328 AssertMsgReturn(idxR0Device < RT_ELEMENTS(pVM->pdmr0.s.apDevInstances), ("%#RX64\n", idxR0Device), NULL);
329 PPDMDEVINS pDevIns = pVM->pdmr0.s.apDevInstances[idxR0Device];
330#elif defined(IN_RING3)
331 AssertMsgReturn(idxR0Device < RT_ELEMENTS(pVM->pdm.s.apDevRing0Instances), ("%#RX64\n", idxR0Device), NULL);
332 PPDMDEVINS pDevIns = pVM->pdm.s.apDevRing0Instances[idxR0Device];
333#else
334# error "Unsupported context"
335#endif
336 AssertMsg(pDevIns, ("%#RX64\n", idxR0Device));
337 return pDevIns;
338}
339
340
341/**
342 * Locks PDM.
343 *
344 * This might block.
345 *
346 * @param pVM The cross context VM structure.
347 */
348void pdmLock(PVMCC pVM)
349{
350 int rc = PDMCritSectEnter(pVM, &pVM->pdm.s.CritSect, VINF_SUCCESS);
351 PDM_CRITSECT_RELEASE_ASSERT_RC(pVM, &pVM->pdm.s.CritSect, rc);
352}
353
354
355/**
356 * Locks PDM but don't go to ring-3 if it's owned by someone.
357 *
358 * @returns VINF_SUCCESS on success.
359 * @returns rc if we're in GC or R0 and can't get the lock.
360 * @param pVM The cross context VM structure.
361 * @param rcBusy The RC to return in GC or R0 when we can't get the lock.
362 */
363int pdmLockEx(PVMCC pVM, int rcBusy)
364{
365 return PDMCritSectEnter(pVM, &pVM->pdm.s.CritSect, rcBusy);
366}
367
368
369/**
370 * Unlocks PDM.
371 *
372 * @param pVM The cross context VM structure.
373 */
374void pdmUnlock(PVMCC pVM)
375{
376 PDMCritSectLeave(pVM, &pVM->pdm.s.CritSect);
377}
378
379
380/**
381 * Checks if this thread is owning the PDM lock.
382 *
383 * @returns @c true if the lock is taken, @c false otherwise.
384 * @param pVM The cross context VM structure.
385 */
386bool pdmLockIsOwner(PVMCC pVM)
387{
388 return PDMCritSectIsOwner(pVM, &pVM->pdm.s.CritSect);
389}
390
391
392/**
393 * Converts ring 3 VMM heap pointer to a guest physical address
394 *
395 * @returns VBox status code.
396 * @param pVM The cross context VM structure.
397 * @param pv Ring-3 pointer.
398 * @param pGCPhys GC phys address (out).
399 */
400VMM_INT_DECL(int) PDMVmmDevHeapR3ToGCPhys(PVM pVM, RTR3PTR pv, RTGCPHYS *pGCPhys)
401{
402 if (RT_LIKELY(pVM->pdm.s.GCPhysVMMDevHeap != NIL_RTGCPHYS))
403 {
404 RTR3UINTPTR const offHeap = (RTR3UINTPTR)pv - (RTR3UINTPTR)pVM->pdm.s.pvVMMDevHeap;
405 if (RT_LIKELY(offHeap < pVM->pdm.s.cbVMMDevHeap))
406 {
407 *pGCPhys = pVM->pdm.s.GCPhysVMMDevHeap + offHeap;
408 return VINF_SUCCESS;
409 }
410
411 /* Don't assert here as this is called before we can catch ring-0 assertions. */
412 Log(("PDMVmmDevHeapR3ToGCPhys: pv=%p pvVMMDevHeap=%p cbVMMDevHeap=%#x\n",
413 pv, pVM->pdm.s.pvVMMDevHeap, pVM->pdm.s.cbVMMDevHeap));
414 }
415 else
416 Log(("PDMVmmDevHeapR3ToGCPhys: GCPhysVMMDevHeap=%RGp (pv=%p)\n", pVM->pdm.s.GCPhysVMMDevHeap, pv));
417 return VERR_PDM_DEV_HEAP_R3_TO_GCPHYS;
418}
419
420
421/**
422 * Checks if the vmm device heap is enabled (== vmm device's pci region mapped)
423 *
424 * @returns dev heap enabled status (true/false)
425 * @param pVM The cross context VM structure.
426 */
427VMM_INT_DECL(bool) PDMVmmDevHeapIsEnabled(PVM pVM)
428{
429 return pVM->pdm.s.GCPhysVMMDevHeap != NIL_RTGCPHYS;
430}
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