/* * 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 #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. // BasicPioDevice::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->get(); 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; } Tick 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 0; } 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) : BasicPioDevice(p, PageBytes), 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) { 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(); } else if (pendingNmi) { DPRINTF(LocalApic, "Generated NMI fault object.\n"); return std::make_shared(nmiVector); } else if (pendingInit) { DPRINTF(LocalApic, "Generated INIT fault object.\n"); return std::make_shared(initVector); } else if (pendingStartup) { DPRINTF(LocalApic, "Generating SIPI fault object.\n"); return std::make_shared(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(extIntVector); } else { DPRINTF(LocalApic, "Generated regular interrupt fault object.\n"); // The only thing left are fixed and lowest priority interrupts. return std::make_shared(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)); }