| /* | |
| * Copyright (c) 2012-2013 ARM Limited | |
| * All rights reserved | |
| * | |
| * The license below extends only to copyright in the software and shall | |
| * not be construed as granting a license to any other intellectual | |
| * property including but not limited to intellectual property relating | |
| * to a hardware implementation of the functionality of the software | |
| * licensed hereunder. You may use the software subject to the license | |
| * terms below provided that you ensure that this notice is replicated | |
| * unmodified and in its entirety in all distributions of the software, | |
| * modified or unmodified, in source code or in binary form. | |
| * | |
| * Copyright (c) 2008 The Hewlett-Packard Development Company | |
| * All rights reserved. | |
| * | |
| * The license below extends only to copyright in the software and shall | |
| * not be construed as granting a license to any other intellectual | |
| * property including but not limited to intellectual property relating | |
| * to a hardware implementation of the functionality of the software | |
| * licensed hereunder. You may use the software subject to the license | |
| * terms below provided that you ensure that this notice is replicated | |
| * unmodified and in its entirety in all distributions of the software, | |
| * modified or unmodified, in source code or in binary form. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer; | |
| * redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution; | |
| * neither the name of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * Authors: Gabe Black | |
| */ | |
| #include "arch/x86/interrupts.hh" | |
| #include <memory> | |
| #include "arch/x86/intmessage.hh" | |
| #include "arch/x86/regs/apic.hh" | |
| #include "cpu/base.hh" | |
| #include "debug/LocalApic.hh" | |
| #include "dev/x86/i82094aa.hh" | |
| #include "dev/x86/pc.hh" | |
| #include "dev/x86/south_bridge.hh" | |
| #include "mem/packet_access.hh" | |
| #include "sim/full_system.hh" | |
| #include "sim/system.hh" | |
| int | |
| divideFromConf(uint32_t conf) | |
| { | |
| // This figures out what division we want from the division configuration | |
| // register in the local APIC. The encoding is a little odd but it can | |
| // be deciphered fairly easily. | |
| int shift = ((conf & 0x8) >> 1) | (conf & 0x3); | |
| shift = (shift + 1) % 8; | |
| return 1 << shift; | |
| } | |
| namespace X86ISA | |
| { | |
| ApicRegIndex | |
| decodeAddr(Addr paddr) | |
| { | |
| ApicRegIndex regNum; | |
| paddr &= ~mask(3); | |
| switch (paddr) | |
| { | |
| case 0x20: | |
| regNum = APIC_ID; | |
| break; | |
| case 0x30: | |
| regNum = APIC_VERSION; | |
| break; | |
| case 0x80: | |
| regNum = APIC_TASK_PRIORITY; | |
| break; | |
| case 0x90: | |
| regNum = APIC_ARBITRATION_PRIORITY; | |
| break; | |
| case 0xA0: | |
| regNum = APIC_PROCESSOR_PRIORITY; | |
| break; | |
| case 0xB0: | |
| regNum = APIC_EOI; | |
| break; | |
| case 0xD0: | |
| regNum = APIC_LOGICAL_DESTINATION; | |
| break; | |
| case 0xE0: | |
| regNum = APIC_DESTINATION_FORMAT; | |
| break; | |
| case 0xF0: | |
| regNum = APIC_SPURIOUS_INTERRUPT_VECTOR; | |
| break; | |
| case 0x100: | |
| case 0x110: | |
| case 0x120: | |
| case 0x130: | |
| case 0x140: | |
| case 0x150: | |
| case 0x160: | |
| case 0x170: | |
| regNum = APIC_IN_SERVICE((paddr - 0x100) / 0x10); | |
| break; | |
| case 0x180: | |
| case 0x190: | |
| case 0x1A0: | |
| case 0x1B0: | |
| case 0x1C0: | |
| case 0x1D0: | |
| case 0x1E0: | |
| case 0x1F0: | |
| regNum = APIC_TRIGGER_MODE((paddr - 0x180) / 0x10); | |
| break; | |
| case 0x200: | |
| case 0x210: | |
| case 0x220: | |
| case 0x230: | |
| case 0x240: | |
| case 0x250: | |
| case 0x260: | |
| case 0x270: | |
| regNum = APIC_INTERRUPT_REQUEST((paddr - 0x200) / 0x10); | |
| break; | |
| case 0x280: | |
| regNum = APIC_ERROR_STATUS; | |
| break; | |
| case 0x300: | |
| regNum = APIC_INTERRUPT_COMMAND_LOW; | |
| break; | |
| case 0x310: | |
| regNum = APIC_INTERRUPT_COMMAND_HIGH; | |
| break; | |
| case 0x320: | |
| regNum = APIC_LVT_TIMER; | |
| break; | |
| case 0x330: | |
| regNum = APIC_LVT_THERMAL_SENSOR; | |
| break; | |
| case 0x340: | |
| regNum = APIC_LVT_PERFORMANCE_MONITORING_COUNTERS; | |
| break; | |
| case 0x350: | |
| regNum = APIC_LVT_LINT0; | |
| break; | |
| case 0x360: | |
| regNum = APIC_LVT_LINT1; | |
| break; | |
| case 0x370: | |
| regNum = APIC_LVT_ERROR; | |
| break; | |
| case 0x380: | |
| regNum = APIC_INITIAL_COUNT; | |
| break; | |
| case 0x390: | |
| regNum = APIC_CURRENT_COUNT; | |
| break; | |
| case 0x3E0: | |
| regNum = APIC_DIVIDE_CONFIGURATION; | |
| break; | |
| default: | |
| // A reserved register field. | |
| panic("Accessed reserved register field %#x.\n", paddr); | |
| break; | |
| } | |
| return regNum; | |
| } | |
| } | |
| Tick | |
| X86ISA::Interrupts::read(PacketPtr pkt) | |
| { | |
| Addr offset = pkt->getAddr() - pioAddr; | |
| // Make sure we're at least only accessing one register. | |
| if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3))) | |
| panic("Accessed more than one register at a time in the APIC!\n"); | |
| ApicRegIndex reg = decodeAddr(offset); | |
| uint32_t val = htog(readReg(reg)); | |
| DPRINTF(LocalApic, | |
| "Reading Local APIC register %d at offset %#x as %#x.\n", | |
| reg, offset, val); | |
| pkt->setData(((uint8_t *)&val) + (offset & mask(3))); | |
| pkt->makeAtomicResponse(); | |
| return pioDelay; | |
| } | |
| Tick | |
| X86ISA::Interrupts::write(PacketPtr pkt) | |
| { | |
| Addr offset = pkt->getAddr() - pioAddr; | |
| // Make sure we're at least only accessing one register. | |
| if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3))) | |
| panic("Accessed more than one register at a time in the APIC!\n"); | |
| ApicRegIndex reg = decodeAddr(offset); | |
| uint32_t val = regs[reg]; | |
| pkt->writeData(((uint8_t *)&val) + (offset & mask(3))); | |
| DPRINTF(LocalApic, | |
| "Writing Local APIC register %d at offset %#x as %#x.\n", | |
| reg, offset, gtoh(val)); | |
| setReg(reg, gtoh(val)); | |
| pkt->makeAtomicResponse(); | |
| return pioDelay; | |
| } | |
| void | |
| X86ISA::Interrupts::requestInterrupt(uint8_t vector, | |
| uint8_t deliveryMode, bool level) | |
| { | |
| /* | |
| * Fixed and lowest-priority delivery mode interrupts are handled | |
| * using the IRR/ISR registers, checking against the TPR, etc. | |
| * The SMI, NMI, ExtInt, INIT, etc interrupts go straight through. | |
| */ | |
| if (deliveryMode == DeliveryMode::Fixed || | |
| deliveryMode == DeliveryMode::LowestPriority) { | |
| DPRINTF(LocalApic, "Interrupt is an %s.\n", | |
| DeliveryMode::names[deliveryMode]); | |
| // Queue up the interrupt in the IRR. | |
| if (vector > IRRV) | |
| IRRV = vector; | |
| if (!getRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, vector)) { | |
| setRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, vector); | |
| if (level) { | |
| setRegArrayBit(APIC_TRIGGER_MODE_BASE, vector); | |
| } else { | |
| clearRegArrayBit(APIC_TRIGGER_MODE_BASE, vector); | |
| } | |
| } | |
| } else if (!DeliveryMode::isReserved(deliveryMode)) { | |
| DPRINTF(LocalApic, "Interrupt is an %s.\n", | |
| DeliveryMode::names[deliveryMode]); | |
| if (deliveryMode == DeliveryMode::SMI && !pendingSmi) { | |
| pendingUnmaskableInt = pendingSmi = true; | |
| smiVector = vector; | |
| } else if (deliveryMode == DeliveryMode::NMI && !pendingNmi) { | |
| pendingUnmaskableInt = pendingNmi = true; | |
| nmiVector = vector; | |
| } else if (deliveryMode == DeliveryMode::ExtInt && !pendingExtInt) { | |
| pendingExtInt = true; | |
| extIntVector = vector; | |
| } else if (deliveryMode == DeliveryMode::INIT && !pendingInit) { | |
| pendingUnmaskableInt = pendingInit = true; | |
| initVector = vector; | |
| } else if (deliveryMode == DeliveryMode::SIPI && | |
| !pendingStartup && !startedUp) { | |
| pendingUnmaskableInt = pendingStartup = true; | |
| startupVector = vector; | |
| } | |
| } | |
| if (FullSystem) | |
| cpu->wakeup(0); | |
| } | |
| void | |
| X86ISA::Interrupts::setCPU(BaseCPU * newCPU) | |
| { | |
| assert(newCPU); | |
| if (cpu != NULL && cpu->cpuId() != newCPU->cpuId()) { | |
| panic("Local APICs can't be moved between CPUs" | |
| " with different IDs.\n"); | |
| } | |
| cpu = newCPU; | |
| initialApicId = cpu->cpuId(); | |
| regs[APIC_ID] = (initialApicId << 24); | |
| pioAddr = x86LocalAPICAddress(initialApicId, 0); | |
| } | |
| void | |
| X86ISA::Interrupts::init() | |
| { | |
| // | |
| // The local apic must register its address ranges on its pio | |
| // port via the basicpiodevice(piodevice) init() function. | |
| PioDevice::init(); | |
| // The slave port has a range, so inform the connected master. | |
| intSlavePort.sendRangeChange(); | |
| // If the master port isn't connected, we can't send interrupts anywhere. | |
| panic_if(!intMasterPort.isConnected(), | |
| "Int port not connected to anything!"); | |
| } | |
| Tick | |
| X86ISA::Interrupts::recvMessage(PacketPtr pkt) | |
| { | |
| Addr offset = pkt->getAddr() - x86InterruptAddress(initialApicId, 0); | |
| assert(pkt->cmd == MemCmd::WriteReq); | |
| switch(offset) | |
| { | |
| case 0: | |
| { | |
| TriggerIntMessage message = pkt->getRaw<TriggerIntMessage>(); | |
| DPRINTF(LocalApic, | |
| "Got Trigger Interrupt message with vector %#x.\n", | |
| message.vector); | |
| requestInterrupt(message.vector, | |
| message.deliveryMode, message.trigger); | |
| } | |
| break; | |
| default: | |
| panic("Local apic got unknown interrupt message at offset %#x.\n", | |
| offset); | |
| break; | |
| } | |
| pkt->makeAtomicResponse(); | |
| return pioDelay; | |
| } | |
| bool | |
| X86ISA::Interrupts::recvResponse(PacketPtr pkt) | |
| { | |
| assert(!pkt->isError()); | |
| assert(pkt->cmd == MemCmd::WriteResp); | |
| if (--pendingIPIs == 0) { | |
| InterruptCommandRegLow low = regs[APIC_INTERRUPT_COMMAND_LOW]; | |
| // Record that the ICR is now idle. | |
| low.deliveryStatus = 0; | |
| regs[APIC_INTERRUPT_COMMAND_LOW] = low; | |
| } | |
| DPRINTF(LocalApic, "ICR is now idle.\n"); | |
| return true; | |
| } | |
| AddrRangeList | |
| X86ISA::Interrupts::getAddrRanges() const | |
| { | |
| assert(cpu); | |
| AddrRangeList ranges; | |
| ranges.push_back(RangeSize(pioAddr, PageBytes)); | |
| return ranges; | |
| } | |
| AddrRangeList | |
| X86ISA::Interrupts::getIntAddrRange() const | |
| { | |
| AddrRangeList ranges; | |
| ranges.push_back(RangeEx(x86InterruptAddress(initialApicId, 0), | |
| x86InterruptAddress(initialApicId, 0) + | |
| PhysAddrAPICRangeSize)); | |
| return ranges; | |
| } | |
| uint32_t | |
| X86ISA::Interrupts::readReg(ApicRegIndex reg) | |
| { | |
| if (reg >= APIC_TRIGGER_MODE(0) && | |
| reg <= APIC_TRIGGER_MODE(15)) { | |
| panic("Local APIC Trigger Mode registers are unimplemented.\n"); | |
| } | |
| switch (reg) { | |
| case APIC_ARBITRATION_PRIORITY: | |
| panic("Local APIC Arbitration Priority register unimplemented.\n"); | |
| break; | |
| case APIC_PROCESSOR_PRIORITY: | |
| panic("Local APIC Processor Priority register unimplemented.\n"); | |
| break; | |
| case APIC_ERROR_STATUS: | |
| regs[APIC_INTERNAL_STATE] &= ~ULL(0x1); | |
| break; | |
| case APIC_CURRENT_COUNT: | |
| { | |
| if (apicTimerEvent.scheduled()) { | |
| // Compute how many m5 ticks happen per count. | |
| uint64_t ticksPerCount = clockPeriod() * | |
| divideFromConf(regs[APIC_DIVIDE_CONFIGURATION]); | |
| // Compute how many m5 ticks are left. | |
| uint64_t val = apicTimerEvent.when() - curTick(); | |
| // Turn that into a count. | |
| val = (val + ticksPerCount - 1) / ticksPerCount; | |
| return val; | |
| } else { | |
| return 0; | |
| } | |
| } | |
| default: | |
| break; | |
| } | |
| return regs[reg]; | |
| } | |
| void | |
| X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val) | |
| { | |
| uint32_t newVal = val; | |
| if (reg >= APIC_IN_SERVICE(0) && | |
| reg <= APIC_IN_SERVICE(15)) { | |
| panic("Local APIC In-Service registers are unimplemented.\n"); | |
| } | |
| if (reg >= APIC_TRIGGER_MODE(0) && | |
| reg <= APIC_TRIGGER_MODE(15)) { | |
| panic("Local APIC Trigger Mode registers are unimplemented.\n"); | |
| } | |
| if (reg >= APIC_INTERRUPT_REQUEST(0) && | |
| reg <= APIC_INTERRUPT_REQUEST(15)) { | |
| panic("Local APIC Interrupt Request registers " | |
| "are unimplemented.\n"); | |
| } | |
| switch (reg) { | |
| case APIC_ID: | |
| newVal = val & 0xFF; | |
| break; | |
| case APIC_VERSION: | |
| // The Local APIC Version register is read only. | |
| return; | |
| case APIC_TASK_PRIORITY: | |
| newVal = val & 0xFF; | |
| break; | |
| case APIC_ARBITRATION_PRIORITY: | |
| panic("Local APIC Arbitration Priority register unimplemented.\n"); | |
| break; | |
| case APIC_PROCESSOR_PRIORITY: | |
| panic("Local APIC Processor Priority register unimplemented.\n"); | |
| break; | |
| case APIC_EOI: | |
| // Remove the interrupt that just completed from the local apic state. | |
| clearRegArrayBit(APIC_IN_SERVICE_BASE, ISRV); | |
| updateISRV(); | |
| return; | |
| case APIC_LOGICAL_DESTINATION: | |
| newVal = val & 0xFF000000; | |
| break; | |
| case APIC_DESTINATION_FORMAT: | |
| newVal = val | 0x0FFFFFFF; | |
| break; | |
| case APIC_SPURIOUS_INTERRUPT_VECTOR: | |
| regs[APIC_INTERNAL_STATE] &= ~ULL(1 << 1); | |
| regs[APIC_INTERNAL_STATE] |= val & (1 << 8); | |
| if (val & (1 << 9)) | |
| warn("Focus processor checking not implemented.\n"); | |
| break; | |
| case APIC_ERROR_STATUS: | |
| { | |
| if (regs[APIC_INTERNAL_STATE] & 0x1) { | |
| regs[APIC_INTERNAL_STATE] &= ~ULL(0x1); | |
| newVal = 0; | |
| } else { | |
| regs[APIC_INTERNAL_STATE] |= ULL(0x1); | |
| return; | |
| } | |
| } | |
| break; | |
| case APIC_INTERRUPT_COMMAND_LOW: | |
| { | |
| InterruptCommandRegLow low = regs[APIC_INTERRUPT_COMMAND_LOW]; | |
| // Check if we're already sending an IPI. | |
| if (low.deliveryStatus) { | |
| newVal = low; | |
| break; | |
| } | |
| low = val; | |
| InterruptCommandRegHigh high = regs[APIC_INTERRUPT_COMMAND_HIGH]; | |
| TriggerIntMessage message = 0; | |
| message.destination = high.destination; | |
| message.vector = low.vector; | |
| message.deliveryMode = low.deliveryMode; | |
| message.destMode = low.destMode; | |
| message.level = low.level; | |
| message.trigger = low.trigger; | |
| ApicList apics; | |
| int numContexts = sys->numContexts(); | |
| switch (low.destShorthand) { | |
| case 0: | |
| if (message.deliveryMode == DeliveryMode::LowestPriority) { | |
| panic("Lowest priority delivery mode " | |
| "IPIs aren't implemented.\n"); | |
| } | |
| if (message.destMode == 1) { | |
| int dest = message.destination; | |
| hack_once("Assuming logical destinations are 1 << id.\n"); | |
| for (int i = 0; i < numContexts; i++) { | |
| if (dest & 0x1) | |
| apics.push_back(i); | |
| dest = dest >> 1; | |
| } | |
| } else { | |
| if (message.destination == 0xFF) { | |
| for (int i = 0; i < numContexts; i++) { | |
| if (i == initialApicId) { | |
| requestInterrupt(message.vector, | |
| message.deliveryMode, message.trigger); | |
| } else { | |
| apics.push_back(i); | |
| } | |
| } | |
| } else { | |
| if (message.destination == initialApicId) { | |
| requestInterrupt(message.vector, | |
| message.deliveryMode, message.trigger); | |
| } else { | |
| apics.push_back(message.destination); | |
| } | |
| } | |
| } | |
| break; | |
| case 1: | |
| newVal = val; | |
| requestInterrupt(message.vector, | |
| message.deliveryMode, message.trigger); | |
| break; | |
| case 2: | |
| requestInterrupt(message.vector, | |
| message.deliveryMode, message.trigger); | |
| // Fall through | |
| case 3: | |
| { | |
| for (int i = 0; i < numContexts; i++) { | |
| if (i != initialApicId) { | |
| apics.push_back(i); | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| // Record that an IPI is being sent if one actually is. | |
| if (apics.size()) { | |
| low.deliveryStatus = 1; | |
| pendingIPIs += apics.size(); | |
| } | |
| regs[APIC_INTERRUPT_COMMAND_LOW] = low; | |
| intMasterPort.sendMessage(apics, message, sys->isTimingMode()); | |
| newVal = regs[APIC_INTERRUPT_COMMAND_LOW]; | |
| } | |
| break; | |
| case APIC_LVT_TIMER: | |
| case APIC_LVT_THERMAL_SENSOR: | |
| case APIC_LVT_PERFORMANCE_MONITORING_COUNTERS: | |
| case APIC_LVT_LINT0: | |
| case APIC_LVT_LINT1: | |
| case APIC_LVT_ERROR: | |
| { | |
| uint64_t readOnlyMask = (1 << 12) | (1 << 14); | |
| newVal = (val & ~readOnlyMask) | | |
| (regs[reg] & readOnlyMask); | |
| } | |
| break; | |
| case APIC_INITIAL_COUNT: | |
| { | |
| newVal = bits(val, 31, 0); | |
| // Compute how many timer ticks we're being programmed for. | |
| uint64_t newCount = newVal * | |
| (divideFromConf(regs[APIC_DIVIDE_CONFIGURATION])); | |
| // Schedule on the edge of the next tick plus the new count. | |
| Tick offset = curTick() % clockPeriod(); | |
| if (offset) { | |
| reschedule(apicTimerEvent, | |
| curTick() + (newCount + 1) * | |
| clockPeriod() - offset, true); | |
| } else { | |
| if (newCount) | |
| reschedule(apicTimerEvent, | |
| curTick() + newCount * | |
| clockPeriod(), true); | |
| } | |
| } | |
| break; | |
| case APIC_CURRENT_COUNT: | |
| //Local APIC Current Count register is read only. | |
| return; | |
| case APIC_DIVIDE_CONFIGURATION: | |
| newVal = val & 0xB; | |
| break; | |
| default: | |
| break; | |
| } | |
| regs[reg] = newVal; | |
| return; | |
| } | |
| X86ISA::Interrupts::Interrupts(Params * p) | |
| : PioDevice(p), | |
| apicTimerEvent([this]{ processApicTimerEvent(); }, name()), | |
| pendingSmi(false), smiVector(0), | |
| pendingNmi(false), nmiVector(0), | |
| pendingExtInt(false), extIntVector(0), | |
| pendingInit(false), initVector(0), | |
| pendingStartup(false), startupVector(0), | |
| startedUp(false), pendingUnmaskableInt(false), | |
| pendingIPIs(0), cpu(NULL), | |
| intSlavePort(name() + ".int_slave", this, this), | |
| intMasterPort(name() + ".int_master", this, this, p->int_latency), | |
| pioDelay(p->pio_latency) | |
| { | |
| memset(regs, 0, sizeof(regs)); | |
| //Set the local apic DFR to the flat model. | |
| regs[APIC_DESTINATION_FORMAT] = (uint32_t)(-1); | |
| ISRV = 0; | |
| IRRV = 0; | |
| } | |
| bool | |
| X86ISA::Interrupts::checkInterrupts(ThreadContext *tc) const | |
| { | |
| RFLAGS rflags = tc->readMiscRegNoEffect(MISCREG_RFLAGS); | |
| if (pendingUnmaskableInt) { | |
| DPRINTF(LocalApic, "Reported pending unmaskable interrupt.\n"); | |
| return true; | |
| } | |
| if (rflags.intf) { | |
| if (pendingExtInt) { | |
| DPRINTF(LocalApic, "Reported pending external interrupt.\n"); | |
| return true; | |
| } | |
| if (IRRV > ISRV && bits(IRRV, 7, 4) > | |
| bits(regs[APIC_TASK_PRIORITY], 7, 4)) { | |
| DPRINTF(LocalApic, "Reported pending regular interrupt.\n"); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool | |
| X86ISA::Interrupts::checkInterruptsRaw() const | |
| { | |
| return pendingUnmaskableInt || pendingExtInt || | |
| (IRRV > ISRV && bits(IRRV, 7, 4) > | |
| bits(regs[APIC_TASK_PRIORITY], 7, 4)); | |
| } | |
| Fault | |
| X86ISA::Interrupts::getInterrupt(ThreadContext *tc) | |
| { | |
| assert(checkInterrupts(tc)); | |
| // These are all probably fairly uncommon, so we'll make them easier to | |
| // check for. | |
| if (pendingUnmaskableInt) { | |
| if (pendingSmi) { | |
| DPRINTF(LocalApic, "Generated SMI fault object.\n"); | |
| return std::make_shared<SystemManagementInterrupt>(); | |
| } else if (pendingNmi) { | |
| DPRINTF(LocalApic, "Generated NMI fault object.\n"); | |
| return std::make_shared<NonMaskableInterrupt>(nmiVector); | |
| } else if (pendingInit) { | |
| DPRINTF(LocalApic, "Generated INIT fault object.\n"); | |
| return std::make_shared<InitInterrupt>(initVector); | |
| } else if (pendingStartup) { | |
| DPRINTF(LocalApic, "Generating SIPI fault object.\n"); | |
| return std::make_shared<StartupInterrupt>(startupVector); | |
| } else { | |
| panic("pendingUnmaskableInt set, but no unmaskable " | |
| "ints were pending.\n"); | |
| return NoFault; | |
| } | |
| } else if (pendingExtInt) { | |
| DPRINTF(LocalApic, "Generated external interrupt fault object.\n"); | |
| return std::make_shared<ExternalInterrupt>(extIntVector); | |
| } else { | |
| DPRINTF(LocalApic, "Generated regular interrupt fault object.\n"); | |
| // The only thing left are fixed and lowest priority interrupts. | |
| return std::make_shared<ExternalInterrupt>(IRRV); | |
| } | |
| } | |
| void | |
| X86ISA::Interrupts::updateIntrInfo(ThreadContext *tc) | |
| { | |
| assert(checkInterrupts(tc)); | |
| if (pendingUnmaskableInt) { | |
| if (pendingSmi) { | |
| DPRINTF(LocalApic, "SMI sent to core.\n"); | |
| pendingSmi = false; | |
| } else if (pendingNmi) { | |
| DPRINTF(LocalApic, "NMI sent to core.\n"); | |
| pendingNmi = false; | |
| } else if (pendingInit) { | |
| DPRINTF(LocalApic, "Init sent to core.\n"); | |
| pendingInit = false; | |
| startedUp = false; | |
| } else if (pendingStartup) { | |
| DPRINTF(LocalApic, "SIPI sent to core.\n"); | |
| pendingStartup = false; | |
| startedUp = true; | |
| } | |
| if (!(pendingSmi || pendingNmi || pendingInit || pendingStartup)) | |
| pendingUnmaskableInt = false; | |
| } else if (pendingExtInt) { | |
| pendingExtInt = false; | |
| } else { | |
| DPRINTF(LocalApic, "Interrupt %d sent to core.\n", IRRV); | |
| // Mark the interrupt as "in service". | |
| ISRV = IRRV; | |
| setRegArrayBit(APIC_IN_SERVICE_BASE, ISRV); | |
| // Clear it out of the IRR. | |
| clearRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, IRRV); | |
| updateIRRV(); | |
| } | |
| } | |
| void | |
| X86ISA::Interrupts::serialize(CheckpointOut &cp) const | |
| { | |
| SERIALIZE_ARRAY(regs, NUM_APIC_REGS); | |
| SERIALIZE_SCALAR(pendingSmi); | |
| SERIALIZE_SCALAR(smiVector); | |
| SERIALIZE_SCALAR(pendingNmi); | |
| SERIALIZE_SCALAR(nmiVector); | |
| SERIALIZE_SCALAR(pendingExtInt); | |
| SERIALIZE_SCALAR(extIntVector); | |
| SERIALIZE_SCALAR(pendingInit); | |
| SERIALIZE_SCALAR(initVector); | |
| SERIALIZE_SCALAR(pendingStartup); | |
| SERIALIZE_SCALAR(startupVector); | |
| SERIALIZE_SCALAR(startedUp); | |
| SERIALIZE_SCALAR(pendingUnmaskableInt); | |
| SERIALIZE_SCALAR(pendingIPIs); | |
| SERIALIZE_SCALAR(IRRV); | |
| SERIALIZE_SCALAR(ISRV); | |
| bool apicTimerEventScheduled = apicTimerEvent.scheduled(); | |
| SERIALIZE_SCALAR(apicTimerEventScheduled); | |
| Tick apicTimerEventTick = apicTimerEvent.when(); | |
| SERIALIZE_SCALAR(apicTimerEventTick); | |
| } | |
| void | |
| X86ISA::Interrupts::unserialize(CheckpointIn &cp) | |
| { | |
| UNSERIALIZE_ARRAY(regs, NUM_APIC_REGS); | |
| UNSERIALIZE_SCALAR(pendingSmi); | |
| UNSERIALIZE_SCALAR(smiVector); | |
| UNSERIALIZE_SCALAR(pendingNmi); | |
| UNSERIALIZE_SCALAR(nmiVector); | |
| UNSERIALIZE_SCALAR(pendingExtInt); | |
| UNSERIALIZE_SCALAR(extIntVector); | |
| UNSERIALIZE_SCALAR(pendingInit); | |
| UNSERIALIZE_SCALAR(initVector); | |
| UNSERIALIZE_SCALAR(pendingStartup); | |
| UNSERIALIZE_SCALAR(startupVector); | |
| UNSERIALIZE_SCALAR(startedUp); | |
| UNSERIALIZE_SCALAR(pendingUnmaskableInt); | |
| UNSERIALIZE_SCALAR(pendingIPIs); | |
| UNSERIALIZE_SCALAR(IRRV); | |
| UNSERIALIZE_SCALAR(ISRV); | |
| bool apicTimerEventScheduled; | |
| UNSERIALIZE_SCALAR(apicTimerEventScheduled); | |
| if (apicTimerEventScheduled) { | |
| Tick apicTimerEventTick; | |
| UNSERIALIZE_SCALAR(apicTimerEventTick); | |
| if (apicTimerEvent.scheduled()) { | |
| reschedule(apicTimerEvent, apicTimerEventTick, true); | |
| } else { | |
| schedule(apicTimerEvent, apicTimerEventTick); | |
| } | |
| } | |
| } | |
| X86ISA::Interrupts * | |
| X86LocalApicParams::create() | |
| { | |
| return new X86ISA::Interrupts(this); | |
| } | |
| void | |
| X86ISA::Interrupts::processApicTimerEvent() { | |
| if (triggerTimerInterrupt()) | |
| setReg(APIC_INITIAL_COUNT, readReg(APIC_INITIAL_COUNT)); | |
| } |