| /* |
| * 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 both its pio |
| // port via the basicpiodevice(piodevice) init() function and its |
| // int port that it inherited from IntDevice. Note IntDevice is |
| // not a SimObject itself. |
| // |
| PioDevice::init(); |
| IntDevice::init(); |
| |
| // the slave port has a range so inform the connected master |
| intSlavePort.sendRangeChange(); |
| } |
| |
| |
| Tick |
| X86ISA::Interrupts::recvMessage(PacketPtr pkt) |
| { |
| Addr offset = pkt->getAddr() - x86InterruptAddress(initialApicId, 0); |
| assert(pkt->cmd == MemCmd::MessageReq); |
| 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::MessageResp); |
| 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), IntDevice(this, p->int_latency), |
| 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), |
| 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)); |
| } |