diff options
Diffstat (limited to 'src/VBox/Devices/PC/DevAPIC.cpp')
-rw-r--r-- | src/VBox/Devices/PC/DevAPIC.cpp | 83 |
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); |