X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Farch%2Fx86%2Finterrupts.cc;h=b418a7489bd4e8d5c9a9ad2a074d69a3cbd9de68;hb=52540b1b785aac9b307dfcc976527d94899deb94;hp=4f77537030f17b16af59165c8588ad7ed4b800a7;hpb=389fbfdab19332ad191e9abed1c12a673fb7eda9;p=gem5.git diff --git a/src/arch/x86/interrupts.cc b/src/arch/x86/interrupts.cc index 4f7753703..b418a7489 100644 --- a/src/arch/x86/interrupts.cc +++ b/src/arch/x86/interrupts.cc @@ -1,44 +1,38 @@ /* - * Copyright (c) 2008 The Hewlett-Packard Development Company - * All rights reserved. + * Copyright (c) 2012 ARM Limited + * All rights reserved * - * Redistribution and use of this software in source and binary forms, - * with or without modification, are permitted provided that the - * following conditions are met: + * 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. * - * The software must be used only for Non-Commercial Use which means any - * use which is NOT directed to receiving any direct monetary - * compensation for, or commercial advantage from such use. Illustrative - * examples of non-commercial use are academic research, personal study, - * teaching, education and corporate research & development. - * Illustrative examples of commercial use are distributing products for - * commercial advantage and providing services using the software for - * commercial advantage. + * Copyright (c) 2008 The Hewlett-Packard Development Company + * All rights reserved. * - * If you wish to use this software or functionality therein that may be - * covered by patents for commercial use, please contact: - * Director of Intellectual Property Licensing - * Office of Strategy and Technology - * Hewlett-Packard Company - * 1501 Page Mill Road - * Palo Alto, California 94304 + * 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. * - * 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 HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its + * 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. No right of - * sublicense is granted herewith. Derivatives of the software and - * output created using the software may be prepared, but only for - * Non-Commercial Uses. Derivatives of the software may be shared with - * others provided: (i) the others agree to abide by the list of - * conditions herein which includes the Non-Commercial Use restrictions; - * and (ii) such Derivatives of the software include the above copyright - * notice to acknowledge the contribution from this software where - * applicable, this list of conditions and the disclaimer below. + * 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 @@ -55,11 +49,17 @@ * Authors: Gabe Black */ -#include "arch/x86/apicregs.hh" +#include "arch/x86/regs/apic.hh" #include "arch/x86/interrupts.hh" #include "arch/x86/intmessage.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/system.hh" +#include "sim/full_system.hh" int divideFromConf(uint32_t conf) @@ -221,6 +221,7 @@ X86ISA::Interrupts::read(PacketPtr pkt) "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 latency; } @@ -238,6 +239,7 @@ X86ISA::Interrupts::write(PacketPtr pkt) "Writing Local APIC register %d at offset %#x as %#x.\n", reg, offset, gtoh(val)); setReg(reg, gtoh(val)); + pkt->makeAtomicResponse(); return latency; } void @@ -279,16 +281,52 @@ X86ISA::Interrupts::requestInterrupt(uint8_t vector, } else if (deliveryMode == DeliveryMode::INIT && !pendingInit) { pendingUnmaskableInt = pendingInit = true; initVector = vector; + } else if (deliveryMode == DeliveryMode::SIPI && + !pendingStartup && !startedUp) { + pendingUnmaskableInt = pendingStartup = true; + startupVector = vector; } - } - cpu->wakeup(); + } + if (FullSystem) + cpu->wakeup(); +} + + +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 IntDev. Note IntDev is not a SimObject itself. + // + BasicPioDevice::init(); + IntDev::init(); + + // the slave port has a range so inform the connected master + intSlavePort.sendRangeChange(); } + Tick X86ISA::Interrupts::recvMessage(PacketPtr pkt) { - uint8_t id = 0; - Addr offset = pkt->getAddr() - x86InterruptAddress(id, 0); + Addr offset = pkt->getAddr() - x86InterruptAddress(initialApicId, 0); assert(pkt->cmd == MemCmd::MessageReq); switch(offset) { @@ -298,9 +336,6 @@ X86ISA::Interrupts::recvMessage(PacketPtr pkt) DPRINTF(LocalApic, "Got Trigger Interrupt message with vector %#x.\n", message.vector); - // Make sure we're really supposed to get this. - assert((message.destMode == 0 && message.destination == id) || - (bits((int)message.destination, id))); requestInterrupt(message.vector, message.deliveryMode, message.trigger); @@ -311,12 +346,50 @@ X86ISA::Interrupts::recvMessage(PacketPtr pkt) offset); break; } - delete pkt->req; - delete pkt; + pkt->makeAtomicResponse(); return latency; } +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::getAddrRanges() const +{ + AddrRangeList ranges; + Range range = RangeEx(x86LocalAPICAddress(initialApicId, 0), + x86LocalAPICAddress(initialApicId, 0) + + PageBytes); + ranges.push_back(range); + 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) { @@ -334,20 +407,21 @@ X86ISA::Interrupts::readReg(ApicRegIndex reg) case APIC_ERROR_STATUS: regs[APIC_INTERNAL_STATE] &= ~ULL(0x1); break; - case APIC_INTERRUPT_COMMAND_LOW: - panic("Local APIC Interrupt Command low" - " register unimplemented.\n"); - break; - case APIC_INTERRUPT_COMMAND_HIGH: - panic("Local APIC Interrupt Command high" - " register unimplemented.\n"); - break; case APIC_CURRENT_COUNT: { - assert(clock); - uint32_t val = regs[reg] - curTick / clock; - val /= (16 * divideFromConf(regs[APIC_DIVIDE_CONFIGURATION])); - return val; + if (apicTimerEvent.scheduled()) { + assert(clock); + // Compute how many m5 ticks happen per count. + uint64_t ticksPerCount = clock * + 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; @@ -418,12 +492,86 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val) } break; case APIC_INTERRUPT_COMMAND_LOW: - panic("Local APIC Interrupt Command low" - " register unimplemented.\n"); - break; - case APIC_INTERRUPT_COMMAND_HIGH: - panic("Local APIC Interrupt Command high" - " register unimplemented.\n"); + { + 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]; + // Record that an IPI is being sent. + low.deliveryStatus = 1; + 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; + bool timing = sys->getMemoryMode() == Enums::timing; + // Be careful no updates of the delivery status bit get lost. + regs[APIC_INTERRUPT_COMMAND_LOW] = low; + 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; + } + pendingIPIs += apics.size(); + intMasterPort.sendMessage(apics, message, timing); + newVal = regs[APIC_INTERRUPT_COMMAND_LOW]; + } break; case APIC_LVT_TIMER: case APIC_LVT_THERMAL_SENSOR: @@ -441,19 +589,17 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val) { assert(clock); newVal = bits(val, 31, 0); - uint32_t newCount = newVal * - (divideFromConf(regs[APIC_DIVIDE_CONFIGURATION]) * 16); - regs[APIC_CURRENT_COUNT] = newCount + curTick / clock; - // Find out how long a "tick" of the timer should take. - Tick timerTick = 16 * clock; + // 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 % timerTick; + Tick offset = curTick() % clock; if (offset) { reschedule(apicTimerEvent, - curTick + (newCount + 1) * timerTick - offset, true); + curTick() + (newCount + 1) * clock - offset, true); } else { reschedule(apicTimerEvent, - curTick + newCount * timerTick, true); + curTick() + newCount * clock, true); } } break; @@ -470,6 +616,29 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val) return; } + +X86ISA::Interrupts::Interrupts(Params * p) : + BasicPioDevice(p), IntDev(this, p->int_latency), latency(p->pio_latency), + clock(0), + apicTimerEvent(this), + 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, latency) +{ + pioSize = PageBytes; + 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 { @@ -508,6 +677,9 @@ X86ISA::Interrupts::getInterrupt(ThreadContext *tc) } else if (pendingInit) { DPRINTF(LocalApic, "Generated INIT fault object.\n"); return new InitInterrupt(initVector); + } else if (pendingStartup) { + DPRINTF(LocalApic, "Generating SIPI fault object.\n"); + return new StartupInterrupt(startupVector); } else { panic("pendingUnmaskableInt set, but no unmaskable " "ints were pending.\n"); @@ -537,8 +709,13 @@ X86ISA::Interrupts::updateIntrInfo(ThreadContext *tc) } 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)) + if (!(pendingSmi || pendingNmi || pendingInit || pendingStartup)) pendingUnmaskableInt = false; } else if (pendingExtInt) { pendingExtInt = false; @@ -553,6 +730,65 @@ X86ISA::Interrupts::updateIntrInfo(ThreadContext *tc) } } +void +X86ISA::Interrupts::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(regs, NUM_APIC_REGS); + SERIALIZE_SCALAR(clock); + 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(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(regs, NUM_APIC_REGS); + UNSERIALIZE_SCALAR(clock); + 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() {