interrupts.cc revision 9544
16899SN/A/*
26899SN/A * Copyright (c) 2012-2013 ARM Limited
36899SN/A * All rights reserved
46899SN/A *
56899SN/A * The license below extends only to copyright in the software and shall
66899SN/A * not be construed as granting a license to any other intellectual
76899SN/A * property including but not limited to intellectual property relating
86899SN/A * to a hardware implementation of the functionality of the software
96899SN/A * licensed hereunder.  You may use the software subject to the license
106899SN/A * terms below provided that you ensure that this notice is replicated
116899SN/A * unmodified and in its entirety in all distributions of the software,
126899SN/A * modified or unmodified, in source code or in binary form.
136899SN/A *
146899SN/A * Copyright (c) 2008 The Hewlett-Packard Development Company
156899SN/A * All rights reserved.
166899SN/A *
176899SN/A * The license below extends only to copyright in the software and shall
186899SN/A * not be construed as granting a license to any other intellectual
196899SN/A * property including but not limited to intellectual property relating
206899SN/A * to a hardware implementation of the functionality of the software
216899SN/A * licensed hereunder.  You may use the software subject to the license
226899SN/A * terms below provided that you ensure that this notice is replicated
236899SN/A * unmodified and in its entirety in all distributions of the software,
246899SN/A * modified or unmodified, in source code or in binary form.
256899SN/A *
266899SN/A * Redistribution and use in source and binary forms, with or without
276899SN/A * modification, are permitted provided that the following conditions are
286899SN/A * met: redistributions of source code must retain the above copyright
296899SN/A * notice, this list of conditions and the following disclaimer;
3011793Sbrandon.potter@amd.com * redistributions in binary form must reproduce the above copyright
3111793Sbrandon.potter@amd.com * notice, this list of conditions and the following disclaimer in the
3210348Sandreas.hansson@arm.com * documentation and/or other materials provided with the distribution;
3311800Sbrandon.potter@amd.com * neither the name of the copyright holders nor the names of its
348232Snate@binkert.org * contributors may be used to endorse or promote products derived from
357053SN/A * this software without specific prior written permission.
366899SN/A *
377053SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
387053SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3911025Snilay@cs.wisc.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
4011025Snilay@cs.wisc.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
418932SBrad.Beckmann@amd.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
428932SBrad.Beckmann@amd.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
436899SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
447053SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
456899SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
467053SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
477053SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
487053SN/A *
497053SN/A * Authors: Gabe Black
5010348Sandreas.hansson@arm.com */
5110348Sandreas.hansson@arm.com
527053SN/A#include "arch/x86/regs/apic.hh"
536899SN/A#include "arch/x86/interrupts.hh"
546899SN/A#include "arch/x86/intmessage.hh"
557053SN/A#include "cpu/base.hh"
567053SN/A#include "debug/LocalApic.hh"
576899SN/A#include "dev/x86/i82094aa.hh"
587053SN/A#include "dev/x86/pc.hh"
597053SN/A#include "dev/x86/south_bridge.hh"
606899SN/A#include "mem/packet_access.hh"
617053SN/A#include "sim/system.hh"
6210348Sandreas.hansson@arm.com#include "sim/full_system.hh"
637053SN/A
647053SN/Aint
656899SN/AdivideFromConf(uint32_t conf)
6610348Sandreas.hansson@arm.com{
678184Ssomayeh@cs.wisc.edu    // This figures out what division we want from the division configuration
688184Ssomayeh@cs.wisc.edu    // register in the local APIC. The encoding is a little odd but it can
698184Ssomayeh@cs.wisc.edu    // be deciphered fairly easily.
707053SN/A    int shift = ((conf & 0x8) >> 1) | (conf & 0x3);
717053SN/A    shift = (shift + 1) % 8;
727053SN/A    return 1 << shift;
737053SN/A}
747053SN/A
757053SN/Anamespace X86ISA
767053SN/A{
777053SN/A
787053SN/AApicRegIndex
796899SN/AdecodeAddr(Addr paddr)
806899SN/A{
817053SN/A    ApicRegIndex regNum;
827053SN/A    paddr &= ~mask(3);
836899SN/A    switch (paddr)
847053SN/A    {
856899SN/A      case 0x20:
8610348Sandreas.hansson@arm.com        regNum = APIC_ID;
878950Sandreas.hansson@arm.com        break;
886899SN/A      case 0x30:
897053SN/A        regNum = APIC_VERSION;
907053SN/A        break;
916899SN/A      case 0x80:
927053SN/A        regNum = APIC_TASK_PRIORITY;
936899SN/A        break;
947053SN/A      case 0x90:
9510348Sandreas.hansson@arm.com        regNum = APIC_ARBITRATION_PRIORITY;
967053SN/A        break;
977053SN/A      case 0xA0:
988932SBrad.Beckmann@amd.com        regNum = APIC_PROCESSOR_PRIORITY;
9911266SBrad.Beckmann@amd.com        break;
10011266SBrad.Beckmann@amd.com      case 0xB0:
10111266SBrad.Beckmann@amd.com        regNum = APIC_EOI;
1027053SN/A        break;
1037053SN/A      case 0xD0:
1047053SN/A        regNum = APIC_LOGICAL_DESTINATION;
1057053SN/A        break;
1067053SN/A      case 0xE0:
1076899SN/A        regNum = APIC_DESTINATION_FORMAT;
1086899SN/A        break;
1097568SN/A      case 0xF0:
11012749Sgiacomo.travaglini@arm.com        regNum = APIC_SPURIOUS_INTERRUPT_VECTOR;
11111025Snilay@cs.wisc.edu        break;
11211435Smitch.hayenga@arm.com      case 0x100:
1137568SN/A      case 0x108:
1148949Sandreas.hansson@arm.com      case 0x110:
11510562Sandreas.hansson@arm.com      case 0x118:
11610562Sandreas.hansson@arm.com      case 0x120:
11710562Sandreas.hansson@arm.com      case 0x128:
11810566Sandreas.hansson@arm.com      case 0x130:
11910562Sandreas.hansson@arm.com      case 0x138:
1206899SN/A      case 0x140:
1217053SN/A      case 0x148:
1227053SN/A      case 0x150:
1239542Sandreas.hansson@arm.com      case 0x158:
1246899SN/A      case 0x160:
1258975Sandreas.hansson@arm.com      case 0x168:
1267053SN/A      case 0x170:
1277053SN/A      case 0x178:
1287053SN/A        regNum = APIC_IN_SERVICE((paddr - 0x100) / 0x8);
1299542Sandreas.hansson@arm.com        break;
1307053SN/A      case 0x180:
1316899SN/A      case 0x188:
1327053SN/A      case 0x190:
1337053SN/A      case 0x198:
1347053SN/A      case 0x1A0:
1356899SN/A      case 0x1A8:
1366899SN/A      case 0x1B0:
1377053SN/A      case 0x1B8:
1388184Ssomayeh@cs.wisc.edu      case 0x1C0:
1398184Ssomayeh@cs.wisc.edu      case 0x1C8:
1408184Ssomayeh@cs.wisc.edu      case 0x1D0:
1418184Ssomayeh@cs.wisc.edu      case 0x1D8:
1428184Ssomayeh@cs.wisc.edu      case 0x1E0:
14310348Sandreas.hansson@arm.com      case 0x1E8:
1448950Sandreas.hansson@arm.com      case 0x1F0:
1458184Ssomayeh@cs.wisc.edu      case 0x1F8:
1468184Ssomayeh@cs.wisc.edu        regNum = APIC_TRIGGER_MODE((paddr - 0x180) / 0x8);
1478184Ssomayeh@cs.wisc.edu        break;
14812749Sgiacomo.travaglini@arm.com      case 0x200:
14911025Snilay@cs.wisc.edu      case 0x208:
1508184Ssomayeh@cs.wisc.edu      case 0x210:
1518184Ssomayeh@cs.wisc.edu      case 0x218:
1528184Ssomayeh@cs.wisc.edu      case 0x220:
1538184Ssomayeh@cs.wisc.edu      case 0x228:
1548184Ssomayeh@cs.wisc.edu      case 0x230:
1558949Sandreas.hansson@arm.com      case 0x238:
1568184Ssomayeh@cs.wisc.edu      case 0x240:
1578184Ssomayeh@cs.wisc.edu      case 0x248:
1588184Ssomayeh@cs.wisc.edu      case 0x250:
1599542Sandreas.hansson@arm.com      case 0x258:
1608184Ssomayeh@cs.wisc.edu      case 0x260:
1618975Sandreas.hansson@arm.com      case 0x268:
1628184Ssomayeh@cs.wisc.edu      case 0x270:
1638184Ssomayeh@cs.wisc.edu      case 0x278:
1648184Ssomayeh@cs.wisc.edu        regNum = APIC_INTERRUPT_REQUEST((paddr - 0x200) / 0x8);
1658184Ssomayeh@cs.wisc.edu        break;
1668184Ssomayeh@cs.wisc.edu      case 0x280:
1677053SN/A        regNum = APIC_ERROR_STATUS;
1686899SN/A        break;
1697053SN/A      case 0x300:
1707053SN/A        regNum = APIC_INTERRUPT_COMMAND_LOW;
1716899SN/A        break;
17210348Sandreas.hansson@arm.com      case 0x310:
1738950Sandreas.hansson@arm.com        regNum = APIC_INTERRUPT_COMMAND_HIGH;
1746899SN/A        break;
1757053SN/A      case 0x320:
1766899SN/A        regNum = APIC_LVT_TIMER;
1777053SN/A        break;
17811025Snilay@cs.wisc.edu      case 0x330:
1796899SN/A        regNum = APIC_LVT_THERMAL_SENSOR;
1807053SN/A        break;
18112749Sgiacomo.travaglini@arm.com      case 0x340:
18212749Sgiacomo.travaglini@arm.com        regNum = APIC_LVT_PERFORMANCE_MONITORING_COUNTERS;
1836899SN/A        break;
18411435Smitch.hayenga@arm.com      case 0x350:
1857053SN/A        regNum = APIC_LVT_LINT0;
1867053SN/A        break;
1877053SN/A      case 0x360:
1887053SN/A        regNum = APIC_LVT_LINT1;
1897053SN/A        break;
1907053SN/A      case 0x370:
1916899SN/A        regNum = APIC_LVT_ERROR;
1927053SN/A        break;
1936899SN/A      case 0x380:
1948949Sandreas.hansson@arm.com        regNum = APIC_INITIAL_COUNT;
19510566Sandreas.hansson@arm.com        break;
1967053SN/A      case 0x390:
1977053SN/A        regNum = APIC_CURRENT_COUNT;
1986899SN/A        break;
19911266SBrad.Beckmann@amd.com      case 0x3E0:
20010563Sandreas.hansson@arm.com        regNum = APIC_DIVIDE_CONFIGURATION;
2016899SN/A        break;
2027053SN/A      default:
2037053SN/A        // A reserved register field.
2049542Sandreas.hansson@arm.com        panic("Accessed reserved register field %#x.\n", paddr);
2056899SN/A        break;
2068975Sandreas.hansson@arm.com    }
2077053SN/A    return regNum;
2087053SN/A}
2097053SN/A}
2107053SN/A
21112612Sjason@lowepower.comTick
2127053SN/AX86ISA::Interrupts::read(PacketPtr pkt)
2137053SN/A{
2147053SN/A    Addr offset = pkt->getAddr() - pioAddr;
2157053SN/A    //Make sure we're at least only accessing one register.
2169542Sandreas.hansson@arm.com    if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3)))
2177053SN/A        panic("Accessed more than one register at a time in the APIC!\n");
2187053SN/A    ApicRegIndex reg = decodeAddr(offset);
2197053SN/A    uint32_t val = htog(readReg(reg));
2207053SN/A    DPRINTF(LocalApic,
2217053SN/A            "Reading Local APIC register %d at offset %#x as %#x.\n",
2227053SN/A            reg, offset, val);
2236899SN/A    pkt->setData(((uint8_t *)&val) + (offset & mask(3)));
2246899SN/A    pkt->makeAtomicResponse();
2256899SN/A    return latency;
2267053SN/A}
2277053SN/A
2286899SN/ATick
2297053SN/AX86ISA::Interrupts::write(PacketPtr pkt)
2307053SN/A{
2316899SN/A    Addr offset = pkt->getAddr() - pioAddr;
23210348Sandreas.hansson@arm.com    //Make sure we're at least only accessing one register.
2338950Sandreas.hansson@arm.com    if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3)))
2346899SN/A        panic("Accessed more than one register at a time in the APIC!\n");
2357053SN/A    ApicRegIndex reg = decodeAddr(offset);
2366899SN/A    uint32_t val = regs[reg];
2378932SBrad.Beckmann@amd.com    pkt->writeData(((uint8_t *)&val) + (offset & mask(3)));
23811266SBrad.Beckmann@amd.com    DPRINTF(LocalApic,
23911266SBrad.Beckmann@amd.com            "Writing Local APIC register %d at offset %#x as %#x.\n",
24011266SBrad.Beckmann@amd.com            reg, offset, gtoh(val));
2417053SN/A    setReg(reg, gtoh(val));
2427053SN/A    pkt->makeAtomicResponse();
2436899SN/A    return latency;
2447568SN/A}
24512749Sgiacomo.travaglini@arm.comvoid
24611025Snilay@cs.wisc.eduX86ISA::Interrupts::requestInterrupt(uint8_t vector,
2477568SN/A        uint8_t deliveryMode, bool level)
24811435Smitch.hayenga@arm.com{
2498949Sandreas.hansson@arm.com    /*
2509208Snilay@cs.wisc.edu     * Fixed and lowest-priority delivery mode interrupts are handled
25110566Sandreas.hansson@arm.com     * using the IRR/ISR registers, checking against the TPR, etc.
2526899SN/A     * The SMI, NMI, ExtInt, INIT, etc interrupts go straight through.
25311266SBrad.Beckmann@amd.com     */
25411266SBrad.Beckmann@amd.com    if (deliveryMode == DeliveryMode::Fixed ||
2557053SN/A            deliveryMode == DeliveryMode::LowestPriority) {
2567053SN/A        DPRINTF(LocalApic, "Interrupt is an %s.\n",
2579542Sandreas.hansson@arm.com                DeliveryMode::names[deliveryMode]);
2586899SN/A        // Queue up the interrupt in the IRR.
2598975Sandreas.hansson@arm.com        if (vector > IRRV)
2607053SN/A            IRRV = vector;
2617053SN/A        if (!getRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, vector)) {
2627053SN/A            setRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, vector);
2637053SN/A            if (level) {
26412612Sjason@lowepower.com                setRegArrayBit(APIC_TRIGGER_MODE_BASE, vector);
2657053SN/A            } else {
2667053SN/A                clearRegArrayBit(APIC_TRIGGER_MODE_BASE, vector);
2677053SN/A            }
2687053SN/A        }
2699542Sandreas.hansson@arm.com    } else if (!DeliveryMode::isReserved(deliveryMode)) {
2707053SN/A        DPRINTF(LocalApic, "Interrupt is an %s.\n",
2716899SN/A                DeliveryMode::names[deliveryMode]);
2727053SN/A        if (deliveryMode == DeliveryMode::SMI && !pendingSmi) {
2737053SN/A            pendingUnmaskableInt = pendingSmi = true;
2747053SN/A            smiVector = vector;
2757053SN/A        } else if (deliveryMode == DeliveryMode::NMI && !pendingNmi) {
2767053SN/A            pendingUnmaskableInt = pendingNmi = true;
2776899SN/A            nmiVector = vector;
2786899SN/A        } else if (deliveryMode == DeliveryMode::ExtInt && !pendingExtInt) {
2797053SN/A            pendingExtInt = true;
28010302Snilay@cs.wisc.edu            extIntVector = vector;
2816899SN/A        } else if (deliveryMode == DeliveryMode::INIT && !pendingInit) {
28211025Snilay@cs.wisc.edu            pendingUnmaskableInt = pendingInit = true;
2836899SN/A            initVector = vector;
2847053SN/A        } else if (deliveryMode == DeliveryMode::SIPI &&
2857053SN/A                !pendingStartup && !startedUp) {
2866899SN/A            pendingUnmaskableInt = pendingStartup = true;
28711025Snilay@cs.wisc.edu            startupVector = vector;
2887053SN/A        }
2897053SN/A    }
2907053SN/A    if (FullSystem)
2916899SN/A        cpu->wakeup();
2926899SN/A}
2937053SN/A
2947053SN/A
2957053SN/Avoid
2967053SN/AX86ISA::Interrupts::setCPU(BaseCPU * newCPU)
2977053SN/A{
2987053SN/A    assert(newCPU);
2997053SN/A    if (cpu != NULL && cpu->cpuId() != newCPU->cpuId()) {
3007053SN/A        panic("Local APICs can't be moved between CPUs"
30112612Sjason@lowepower.com                " with different IDs.\n");
3027053SN/A    }
3037053SN/A    cpu = newCPU;
30412612Sjason@lowepower.com    initialApicId = cpu->cpuId();
30511266SBrad.Beckmann@amd.com    regs[APIC_ID] = (initialApicId << 24);
3067053SN/A    pioAddr = x86LocalAPICAddress(initialApicId, 0);
3077053SN/A}
3087053SN/A
3097053SN/A
3107053SN/Avoid
3117053SN/AX86ISA::Interrupts::init()
3127053SN/A{
3139208Snilay@cs.wisc.edu    //
31412612Sjason@lowepower.com    // The local apic must register its address ranges on both its pio port
3157805Snilay@cs.wisc.edu    // via the basicpiodevice(piodevice) init() function and its int port
3167805Snilay@cs.wisc.edu    // that it inherited from IntDev.  Note IntDev is not a SimObject itself.
3177805Snilay@cs.wisc.edu    //
3187805Snilay@cs.wisc.edu    BasicPioDevice::init();
3199475Snilay@cs.wisc.edu    IntDev::init();
3207053SN/A
3217053SN/A    // the slave port has a range so inform the connected master
3227053SN/A    intSlavePort.sendRangeChange();
3237053SN/A}
3246899SN/A
3257053SN/A
3267053SN/ATick
3276899SN/AX86ISA::Interrupts::recvMessage(PacketPtr pkt)
3287053SN/A{
32912612Sjason@lowepower.com    Addr offset = pkt->getAddr() - x86InterruptAddress(initialApicId, 0);
3307053SN/A    assert(pkt->cmd == MemCmd::MessageReq);
3317053SN/A    switch(offset)
3327053SN/A    {
3337805Snilay@cs.wisc.edu      case 0:
3349475Snilay@cs.wisc.edu        {
3357053SN/A            TriggerIntMessage message = pkt->get<TriggerIntMessage>();
3367053SN/A            DPRINTF(LocalApic,
3377053SN/A                    "Got Trigger Interrupt message with vector %#x.\n",
33811025Snilay@cs.wisc.edu                    message.vector);
3397053SN/A
3407053SN/A            requestInterrupt(message.vector,
3416899SN/A                    message.deliveryMode, message.trigger);
3426899SN/A        }
3437053SN/A        break;
34411025Snilay@cs.wisc.edu      default:
3456899SN/A        panic("Local apic got unknown interrupt message at offset %#x.\n",
3467053SN/A                offset);
3477053SN/A        break;
3487053SN/A    }
34912612Sjason@lowepower.com    pkt->makeAtomicResponse();
3507053SN/A    return latency;
3516899SN/A}
3526899SN/A
3537053SN/A
3547053SN/ATick
3556899SN/AX86ISA::Interrupts::recvResponse(PacketPtr pkt)
3567053SN/A{
35710348Sandreas.hansson@arm.com    assert(!pkt->isError());
3587053SN/A    assert(pkt->cmd == MemCmd::MessageResp);
3596899SN/A    if (--pendingIPIs == 0) {
3606899SN/A        InterruptCommandRegLow low = regs[APIC_INTERRUPT_COMMAND_LOW];
3617053SN/A        // Record that the ICR is now idle.
3627053SN/A        low.deliveryStatus = 0;
3636899SN/A        regs[APIC_INTERRUPT_COMMAND_LOW] = low;
3647053SN/A    }
3657053SN/A    DPRINTF(LocalApic, "ICR is now idle.\n");
36610348Sandreas.hansson@arm.com    return 0;
36712612Sjason@lowepower.com}
36811266SBrad.Beckmann@amd.com
3697053SN/A
3706899SN/AAddrRangeList
3716899SN/AX86ISA::Interrupts::getAddrRanges() const
3727053SN/A{
3737055SN/A    AddrRangeList ranges;
3746899SN/A    AddrRange range = RangeEx(x86LocalAPICAddress(initialApicId, 0),
3757053SN/A                              x86LocalAPICAddress(initialApicId, 0) +
3767053SN/A                              PageBytes);
3777053SN/A    ranges.push_back(range);
3787053SN/A    return ranges;
3797053SN/A}
3807053SN/A
3817055SN/A
3826899SN/AAddrRangeList
3836899SN/AX86ISA::Interrupts::getIntAddrRange() const
3847053SN/A{
3857053SN/A    AddrRangeList ranges;
3866899SN/A    ranges.push_back(RangeEx(x86InterruptAddress(initialApicId, 0),
3877053SN/A                             x86InterruptAddress(initialApicId, 0) +
3887053SN/A                             PhysAddrAPICRangeSize));
38911025Snilay@cs.wisc.edu    return ranges;
3907053SN/A}
3916899SN/A
392
393uint32_t
394X86ISA::Interrupts::readReg(ApicRegIndex reg)
395{
396    if (reg >= APIC_TRIGGER_MODE(0) &&
397            reg <= APIC_TRIGGER_MODE(15)) {
398        panic("Local APIC Trigger Mode registers are unimplemented.\n");
399    }
400    switch (reg) {
401      case APIC_ARBITRATION_PRIORITY:
402        panic("Local APIC Arbitration Priority register unimplemented.\n");
403        break;
404      case APIC_PROCESSOR_PRIORITY:
405        panic("Local APIC Processor Priority register unimplemented.\n");
406        break;
407      case APIC_ERROR_STATUS:
408        regs[APIC_INTERNAL_STATE] &= ~ULL(0x1);
409        break;
410      case APIC_CURRENT_COUNT:
411        {
412            if (apicTimerEvent.scheduled()) {
413                // Compute how many m5 ticks happen per count.
414                uint64_t ticksPerCount = clockPeriod() *
415                    divideFromConf(regs[APIC_DIVIDE_CONFIGURATION]);
416                // Compute how many m5 ticks are left.
417                uint64_t val = apicTimerEvent.when() - curTick();
418                // Turn that into a count.
419                val = (val + ticksPerCount - 1) / ticksPerCount;
420                return val;
421            } else {
422                return 0;
423            }
424        }
425      default:
426        break;
427    }
428    return regs[reg];
429}
430
431void
432X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val)
433{
434    uint32_t newVal = val;
435    if (reg >= APIC_IN_SERVICE(0) &&
436            reg <= APIC_IN_SERVICE(15)) {
437        panic("Local APIC In-Service registers are unimplemented.\n");
438    }
439    if (reg >= APIC_TRIGGER_MODE(0) &&
440            reg <= APIC_TRIGGER_MODE(15)) {
441        panic("Local APIC Trigger Mode registers are unimplemented.\n");
442    }
443    if (reg >= APIC_INTERRUPT_REQUEST(0) &&
444            reg <= APIC_INTERRUPT_REQUEST(15)) {
445        panic("Local APIC Interrupt Request registers "
446                "are unimplemented.\n");
447    }
448    switch (reg) {
449      case APIC_ID:
450        newVal = val & 0xFF;
451        break;
452      case APIC_VERSION:
453        // The Local APIC Version register is read only.
454        return;
455      case APIC_TASK_PRIORITY:
456        newVal = val & 0xFF;
457        break;
458      case APIC_ARBITRATION_PRIORITY:
459        panic("Local APIC Arbitration Priority register unimplemented.\n");
460        break;
461      case APIC_PROCESSOR_PRIORITY:
462        panic("Local APIC Processor Priority register unimplemented.\n");
463        break;
464      case APIC_EOI:
465        // Remove the interrupt that just completed from the local apic state.
466        clearRegArrayBit(APIC_IN_SERVICE_BASE, ISRV);
467        updateISRV();
468        return;
469      case APIC_LOGICAL_DESTINATION:
470        newVal = val & 0xFF000000;
471        break;
472      case APIC_DESTINATION_FORMAT:
473        newVal = val | 0x0FFFFFFF;
474        break;
475      case APIC_SPURIOUS_INTERRUPT_VECTOR:
476        regs[APIC_INTERNAL_STATE] &= ~ULL(1 << 1);
477        regs[APIC_INTERNAL_STATE] |= val & (1 << 8);
478        if (val & (1 << 9))
479            warn("Focus processor checking not implemented.\n");
480        break;
481      case APIC_ERROR_STATUS:
482        {
483            if (regs[APIC_INTERNAL_STATE] & 0x1) {
484                regs[APIC_INTERNAL_STATE] &= ~ULL(0x1);
485                newVal = 0;
486            } else {
487                regs[APIC_INTERNAL_STATE] |= ULL(0x1);
488                return;
489            }
490
491        }
492        break;
493      case APIC_INTERRUPT_COMMAND_LOW:
494        {
495            InterruptCommandRegLow low = regs[APIC_INTERRUPT_COMMAND_LOW];
496            // Check if we're already sending an IPI.
497            if (low.deliveryStatus) {
498                newVal = low;
499                break;
500            }
501            low = val;
502            InterruptCommandRegHigh high = regs[APIC_INTERRUPT_COMMAND_HIGH];
503            // Record that an IPI is being sent.
504            low.deliveryStatus = 1;
505            TriggerIntMessage message = 0;
506            message.destination = high.destination;
507            message.vector = low.vector;
508            message.deliveryMode = low.deliveryMode;
509            message.destMode = low.destMode;
510            message.level = low.level;
511            message.trigger = low.trigger;
512            bool timing(sys->isTimingMode());
513            // Be careful no updates of the delivery status bit get lost.
514            regs[APIC_INTERRUPT_COMMAND_LOW] = low;
515            ApicList apics;
516            int numContexts = sys->numContexts();
517            switch (low.destShorthand) {
518              case 0:
519                if (message.deliveryMode == DeliveryMode::LowestPriority) {
520                    panic("Lowest priority delivery mode "
521                            "IPIs aren't implemented.\n");
522                }
523                if (message.destMode == 1) {
524                    int dest = message.destination;
525                    hack_once("Assuming logical destinations are 1 << id.\n");
526                    for (int i = 0; i < numContexts; i++) {
527                        if (dest & 0x1)
528                            apics.push_back(i);
529                        dest = dest >> 1;
530                    }
531                } else {
532                    if (message.destination == 0xFF) {
533                        for (int i = 0; i < numContexts; i++) {
534                            if (i == initialApicId) {
535                                requestInterrupt(message.vector,
536                                        message.deliveryMode, message.trigger);
537                            } else {
538                                apics.push_back(i);
539                            }
540                        }
541                    } else {
542                        if (message.destination == initialApicId) {
543                            requestInterrupt(message.vector,
544                                    message.deliveryMode, message.trigger);
545                        } else {
546                            apics.push_back(message.destination);
547                        }
548                    }
549                }
550                break;
551              case 1:
552                newVal = val;
553                requestInterrupt(message.vector,
554                        message.deliveryMode, message.trigger);
555                break;
556              case 2:
557                requestInterrupt(message.vector,
558                        message.deliveryMode, message.trigger);
559                // Fall through
560              case 3:
561                {
562                    for (int i = 0; i < numContexts; i++) {
563                        if (i != initialApicId) {
564                            apics.push_back(i);
565                        }
566                    }
567                }
568                break;
569            }
570            pendingIPIs += apics.size();
571            intMasterPort.sendMessage(apics, message, timing);
572            newVal = regs[APIC_INTERRUPT_COMMAND_LOW];
573        }
574        break;
575      case APIC_LVT_TIMER:
576      case APIC_LVT_THERMAL_SENSOR:
577      case APIC_LVT_PERFORMANCE_MONITORING_COUNTERS:
578      case APIC_LVT_LINT0:
579      case APIC_LVT_LINT1:
580      case APIC_LVT_ERROR:
581        {
582            uint64_t readOnlyMask = (1 << 12) | (1 << 14);
583            newVal = (val & ~readOnlyMask) |
584                     (regs[reg] & readOnlyMask);
585        }
586        break;
587      case APIC_INITIAL_COUNT:
588        {
589            newVal = bits(val, 31, 0);
590            // Compute how many timer ticks we're being programmed for.
591            uint64_t newCount = newVal *
592                (divideFromConf(regs[APIC_DIVIDE_CONFIGURATION]));
593            // Schedule on the edge of the next tick plus the new count.
594            Tick offset = curTick() % clockPeriod();
595            if (offset) {
596                reschedule(apicTimerEvent,
597                           curTick() + (newCount + 1) *
598                           clockPeriod() - offset, true);
599            } else {
600                reschedule(apicTimerEvent,
601                           curTick() + newCount *
602                           clockPeriod(), true);
603            }
604        }
605        break;
606      case APIC_CURRENT_COUNT:
607        //Local APIC Current Count register is read only.
608        return;
609      case APIC_DIVIDE_CONFIGURATION:
610        newVal = val & 0xB;
611        break;
612      default:
613        break;
614    }
615    regs[reg] = newVal;
616    return;
617}
618
619
620X86ISA::Interrupts::Interrupts(Params * p) :
621    BasicPioDevice(p), IntDev(this, p->int_latency), latency(p->pio_latency),
622    apicTimerEvent(this),
623    pendingSmi(false), smiVector(0),
624    pendingNmi(false), nmiVector(0),
625    pendingExtInt(false), extIntVector(0),
626    pendingInit(false), initVector(0),
627    pendingStartup(false), startupVector(0),
628    startedUp(false), pendingUnmaskableInt(false),
629    pendingIPIs(0), cpu(NULL),
630    intSlavePort(name() + ".int_slave", this, this, latency)
631{
632    pioSize = PageBytes;
633    memset(regs, 0, sizeof(regs));
634    //Set the local apic DFR to the flat model.
635    regs[APIC_DESTINATION_FORMAT] = (uint32_t)(-1);
636    ISRV = 0;
637    IRRV = 0;
638}
639
640
641bool
642X86ISA::Interrupts::checkInterrupts(ThreadContext *tc) const
643{
644    RFLAGS rflags = tc->readMiscRegNoEffect(MISCREG_RFLAGS);
645    if (pendingUnmaskableInt) {
646        DPRINTF(LocalApic, "Reported pending unmaskable interrupt.\n");
647        return true;
648    }
649    if (rflags.intf) {
650        if (pendingExtInt) {
651            DPRINTF(LocalApic, "Reported pending external interrupt.\n");
652            return true;
653        }
654        if (IRRV > ISRV && bits(IRRV, 7, 4) >
655               bits(regs[APIC_TASK_PRIORITY], 7, 4)) {
656            DPRINTF(LocalApic, "Reported pending regular interrupt.\n");
657            return true;
658        }
659    }
660    return false;
661}
662
663Fault
664X86ISA::Interrupts::getInterrupt(ThreadContext *tc)
665{
666    assert(checkInterrupts(tc));
667    // These are all probably fairly uncommon, so we'll make them easier to
668    // check for.
669    if (pendingUnmaskableInt) {
670        if (pendingSmi) {
671            DPRINTF(LocalApic, "Generated SMI fault object.\n");
672            return new SystemManagementInterrupt();
673        } else if (pendingNmi) {
674            DPRINTF(LocalApic, "Generated NMI fault object.\n");
675            return new NonMaskableInterrupt(nmiVector);
676        } else if (pendingInit) {
677            DPRINTF(LocalApic, "Generated INIT fault object.\n");
678            return new InitInterrupt(initVector);
679        } else if (pendingStartup) {
680            DPRINTF(LocalApic, "Generating SIPI fault object.\n");
681            return new StartupInterrupt(startupVector);
682        } else {
683            panic("pendingUnmaskableInt set, but no unmaskable "
684                    "ints were pending.\n");
685            return NoFault;
686        }
687    } else if (pendingExtInt) {
688        DPRINTF(LocalApic, "Generated external interrupt fault object.\n");
689        return new ExternalInterrupt(extIntVector);
690    } else {
691        DPRINTF(LocalApic, "Generated regular interrupt fault object.\n");
692        // The only thing left are fixed and lowest priority interrupts.
693        return new ExternalInterrupt(IRRV);
694    }
695}
696
697void
698X86ISA::Interrupts::updateIntrInfo(ThreadContext *tc)
699{
700    assert(checkInterrupts(tc));
701    if (pendingUnmaskableInt) {
702        if (pendingSmi) {
703            DPRINTF(LocalApic, "SMI sent to core.\n");
704            pendingSmi = false;
705        } else if (pendingNmi) {
706            DPRINTF(LocalApic, "NMI sent to core.\n");
707            pendingNmi = false;
708        } else if (pendingInit) {
709            DPRINTF(LocalApic, "Init sent to core.\n");
710            pendingInit = false;
711            startedUp = false;
712        } else if (pendingStartup) {
713            DPRINTF(LocalApic, "SIPI sent to core.\n");
714            pendingStartup = false;
715            startedUp = true;
716        }
717        if (!(pendingSmi || pendingNmi || pendingInit || pendingStartup))
718            pendingUnmaskableInt = false;
719    } else if (pendingExtInt) {
720        pendingExtInt = false;
721    } else {
722        DPRINTF(LocalApic, "Interrupt %d sent to core.\n", IRRV);
723        // Mark the interrupt as "in service".
724        ISRV = IRRV;
725        setRegArrayBit(APIC_IN_SERVICE_BASE, ISRV);
726        // Clear it out of the IRR.
727        clearRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, IRRV);
728        updateIRRV();
729    }
730}
731
732void
733X86ISA::Interrupts::serialize(std::ostream &os)
734{
735    SERIALIZE_ARRAY(regs, NUM_APIC_REGS);
736    SERIALIZE_SCALAR(pendingSmi);
737    SERIALIZE_SCALAR(smiVector);
738    SERIALIZE_SCALAR(pendingNmi);
739    SERIALIZE_SCALAR(nmiVector);
740    SERIALIZE_SCALAR(pendingExtInt);
741    SERIALIZE_SCALAR(extIntVector);
742    SERIALIZE_SCALAR(pendingInit);
743    SERIALIZE_SCALAR(initVector);
744    SERIALIZE_SCALAR(pendingStartup);
745    SERIALIZE_SCALAR(startupVector);
746    SERIALIZE_SCALAR(startedUp);
747    SERIALIZE_SCALAR(pendingUnmaskableInt);
748    SERIALIZE_SCALAR(pendingIPIs);
749    SERIALIZE_SCALAR(IRRV);
750    SERIALIZE_SCALAR(ISRV);
751    bool apicTimerEventScheduled = apicTimerEvent.scheduled();
752    SERIALIZE_SCALAR(apicTimerEventScheduled);
753    Tick apicTimerEventTick = apicTimerEvent.when();
754    SERIALIZE_SCALAR(apicTimerEventTick);
755}
756
757void
758X86ISA::Interrupts::unserialize(Checkpoint *cp, const std::string &section)
759{
760    UNSERIALIZE_ARRAY(regs, NUM_APIC_REGS);
761    UNSERIALIZE_SCALAR(pendingSmi);
762    UNSERIALIZE_SCALAR(smiVector);
763    UNSERIALIZE_SCALAR(pendingNmi);
764    UNSERIALIZE_SCALAR(nmiVector);
765    UNSERIALIZE_SCALAR(pendingExtInt);
766    UNSERIALIZE_SCALAR(extIntVector);
767    UNSERIALIZE_SCALAR(pendingInit);
768    UNSERIALIZE_SCALAR(initVector);
769    UNSERIALIZE_SCALAR(pendingStartup);
770    UNSERIALIZE_SCALAR(startupVector);
771    UNSERIALIZE_SCALAR(startedUp);
772    UNSERIALIZE_SCALAR(pendingUnmaskableInt);
773    UNSERIALIZE_SCALAR(pendingIPIs);
774    UNSERIALIZE_SCALAR(IRRV);
775    UNSERIALIZE_SCALAR(ISRV);
776    bool apicTimerEventScheduled;
777    UNSERIALIZE_SCALAR(apicTimerEventScheduled);
778    if (apicTimerEventScheduled) {
779        Tick apicTimerEventTick;
780        UNSERIALIZE_SCALAR(apicTimerEventTick);
781        if (apicTimerEvent.scheduled()) {
782            reschedule(apicTimerEvent, apicTimerEventTick, true);
783        } else {
784            schedule(apicTimerEvent, apicTimerEventTick);
785        }
786    }
787}
788
789X86ISA::Interrupts *
790X86LocalApicParams::create()
791{
792    return new X86ISA::Interrupts(this);
793}
794