summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/PC/DevAPIC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/PC/DevAPIC.cpp')
-rw-r--r--src/VBox/Devices/PC/DevAPIC.cpp83
1 files changed, 81 insertions, 2 deletions
diff --git a/src/VBox/Devices/PC/DevAPIC.cpp b/src/VBox/Devices/PC/DevAPIC.cpp
index 8e427ca0c..8317f5098 100644
--- a/src/VBox/Devices/PC/DevAPIC.cpp
+++ b/src/VBox/Devices/PC/DevAPIC.cpp
@@ -365,6 +365,7 @@ PDMBOTHCBDECL(uint8_t) apicGetTPR(PPDMDEVINS pDevIns, VMCPUID idCpu);
PDMBOTHCBDECL(int) apicBusDeliverCallback(PPDMDEVINS pDevIns, uint8_t u8Dest, uint8_t u8DestMode,
uint8_t u8DeliveryMode, uint8_t iVector, uint8_t u8Polarity,
uint8_t u8TriggerMode);
+PDMBOTHCBDECL(int) apicLocalInterrupt(PPDMDEVINS pDevIns, uint8_t u8Pin, uint8_t u8Level);
PDMBOTHCBDECL(int) apicWriteMSR(PPDMDEVINS pDevIns, VMCPUID iCpu, uint32_t u32Reg, uint64_t u64Value);
PDMBOTHCBDECL(int) apicReadMSR(PPDMDEVINS pDevIns, VMCPUID iCpu, uint32_t u32Reg, uint64_t *pu64Value);
PDMBOTHCBDECL(int) ioapicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
@@ -421,10 +422,10 @@ DECLINLINE(void) cpuSetInterrupt(APICDeviceInfo* dev, APICState *s, PDMAPICIRQ e
getCpuFromLapic(dev, s));
}
-DECLINLINE(void) cpuClearInterrupt(APICDeviceInfo* dev, APICState *s)
+DECLINLINE(void) cpuClearInterrupt(APICDeviceInfo* dev, APICState *s, PDMAPICIRQ enmType = PDMAPICIRQ_HARDWARE)
{
LogFlow(("apic: clear interrupt flag\n"));
- dev->CTX_SUFF(pApicHlp)->pfnClearInterruptFF(dev->CTX_SUFF(pDevIns),
+ dev->CTX_SUFF(pApicHlp)->pfnClearInterruptFF(dev->CTX_SUFF(pDevIns), enmType,
getCpuFromLapic(dev, s));
}
@@ -938,6 +939,79 @@ PDMBOTHCBDECL(int) apicBusDeliverCallback(PPDMDEVINS pDevIns, uint8_t u8Dest, ui
u8DeliveryMode, iVector, u8Polarity, u8TriggerMode);
}
+/**
+ * Local interrupt delivery, for devices attached to the CPU's LINT0/LINT1 pin.
+ * Normally used for 8259A PIC and NMI.
+ */
+PDMBOTHCBDECL(int) apicLocalInterrupt(PPDMDEVINS pDevIns, uint8_t u8Pin, uint8_t u8Level)
+{
+ APICDeviceInfo *dev = PDMINS_2_DATA(pDevIns, APICDeviceInfo *);
+ APICState *s = getLapicById(dev, 0);
+
+ Assert(PDMCritSectIsOwner(dev->CTX_SUFF(pCritSect)));
+ LogFlow(("apicLocalInterrupt: pDevIns=%p u8Pin=%x\n", pDevIns, u8Pin));
+
+ /* If LAPIC is disabled, go straight to the CPU. */
+ if (!(s->spurious_vec & APIC_SV_ENABLE))
+ {
+ LogFlow(("apicLocalInterrupt: LAPIC disabled, delivering directly to CPU core.\n"));
+ if (u8Level)
+ cpuSetInterrupt(dev, s, PDMAPICIRQ_EXTINT);
+ else
+ cpuClearInterrupt(dev, s, PDMAPICIRQ_EXTINT);
+
+ return VINF_SUCCESS;
+ }
+
+ /* If LAPIC is enabled, interrupts are subject to LVT programming. */
+
+ /* There are only two local interrupt pins. */
+ AssertMsgReturn(u8Pin <= 1, ("Invalid LAPIC pin %d\n", u8Pin), VERR_INVALID_PARAMETER);
+
+ /* NB: We currently only deliver local interrupts to the first CPU. In theory they
+ * should be delivered to all CPUs and it is the guest's responsibility to ensure
+ * no more than one CPU has the interrupt unmasked.
+ */
+ uint32_t u32Lvec;
+
+ u32Lvec = s->lvt[APIC_LVT_LINT0 + u8Pin]; /* Fetch corresponding LVT entry. */
+ /* Drop int if entry is masked. May not be correct for level-triggered interrupts. */
+ if (!(u32Lvec & APIC_LVT_MASKED))
+ { uint8_t u8Delivery;
+ PDMAPICIRQ enmType;
+
+ u8Delivery = (u32Lvec >> 8) & 7;
+ switch (u8Delivery)
+ {
+ case APIC_DM_EXTINT:
+ Assert(u8Pin == 0); /* PIC should be wired to LINT0. */
+ enmType = PDMAPICIRQ_EXTINT;
+ break;
+ case APIC_DM_NMI:
+ Assert(u8Pin == 1); /* NMI should be wired to LINT1. */
+ enmType = PDMAPICIRQ_NMI;
+ break;
+ case APIC_DM_SMI:
+ enmType = PDMAPICIRQ_SMI;
+ break;
+ case APIC_DM_FIXED:
+ /** @todo implement APIC_DM_FIXED! */
+ case APIC_DM_INIT:
+ /** @todo implement APIC_DM_INIT? */
+ default:
+ {
+ static unsigned s_c = 0;
+ if (s_c++ < 100)
+ AssertLogRelMsgFailed(("delivery type %d not implemented. u8Pin=%d u8Level=%d", u8Delivery, u8Pin, u8Level));
+ return VERR_INTERNAL_ERROR_4;
+ }
+ }
+ LogFlow(("apicLocalInterrupt: setting local interrupt type %d\n", enmType));
+ cpuSetInterrupt(dev, s, enmType);
+ }
+ return VINF_SUCCESS;
+}
+
#endif /* VBOX */
/* return -1 if no bit is set */
@@ -2526,6 +2600,7 @@ static DECLCALLBACK(int) apicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
ApicReg.pfnWriteMSRR3 = apicWriteMSR;
ApicReg.pfnReadMSRR3 = apicReadMSR;
ApicReg.pfnBusDeliverR3 = apicBusDeliverCallback;
+ ApicReg.pfnLocalInterruptR3 = apicLocalInterrupt;
if (fGCEnabled) {
ApicReg.pszGetInterruptRC = "apicGetInterrupt";
ApicReg.pszHasPendingIrqRC = "apicHasPendingIrq";
@@ -2536,6 +2611,7 @@ static DECLCALLBACK(int) apicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
ApicReg.pszWriteMSRRC = "apicWriteMSR";
ApicReg.pszReadMSRRC = "apicReadMSR";
ApicReg.pszBusDeliverRC = "apicBusDeliverCallback";
+ ApicReg.pszLocalInterruptRC = "apicLocalInterrupt";
} else {
ApicReg.pszGetInterruptRC = NULL;
ApicReg.pszHasPendingIrqRC = NULL;
@@ -2546,6 +2622,7 @@ static DECLCALLBACK(int) apicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
ApicReg.pszWriteMSRRC = NULL;
ApicReg.pszReadMSRRC = NULL;
ApicReg.pszBusDeliverRC = NULL;
+ ApicReg.pszLocalInterruptRC = NULL;
}
if (fR0Enabled) {
ApicReg.pszGetInterruptR0 = "apicGetInterrupt";
@@ -2557,6 +2634,7 @@ static DECLCALLBACK(int) apicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
ApicReg.pszWriteMSRR0 = "apicWriteMSR";
ApicReg.pszReadMSRR0 = "apicReadMSR";
ApicReg.pszBusDeliverR0 = "apicBusDeliverCallback";
+ ApicReg.pszLocalInterruptR0 = "apicLocalInterrupt";
} else {
ApicReg.pszGetInterruptR0 = NULL;
ApicReg.pszHasPendingIrqR0 = NULL;
@@ -2567,6 +2645,7 @@ static DECLCALLBACK(int) apicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
ApicReg.pszWriteMSRR0 = NULL;
ApicReg.pszReadMSRR0 = NULL;
ApicReg.pszBusDeliverR0 = NULL;
+ ApicReg.pszLocalInterruptR0 = NULL;
}
Assert(pDevIns->pDevHlpR3->pfnAPICRegister);