| /* |
| * Copyright (c) 2008 The Regents of The University of Michigan |
| * All rights reserved. |
| * |
| * 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 "dev/x86/i82094aa.hh" |
| |
| #include "arch/x86/interrupts.hh" |
| #include "arch/x86/intmessage.hh" |
| #include "cpu/base.hh" |
| #include "debug/I82094AA.hh" |
| #include "dev/x86/i8259.hh" |
| #include "mem/packet.hh" |
| #include "mem/packet_access.hh" |
| #include "sim/system.hh" |
| |
| X86ISA::I82094AA::I82094AA(Params *p) |
| : BasicPioDevice(p, 20), IntDevice(this, p->int_latency), |
| extIntPic(p->external_int_pic), lowestPriorityOffset(0) |
| { |
| // This assumes there's only one I/O APIC in the system and since the apic |
| // id is stored in a 8-bit field with 0xff meaning broadcast, the id must |
| // be less than 0xff |
| |
| assert(p->apic_id < 0xff); |
| initialApicId = id = p->apic_id; |
| arbId = id; |
| regSel = 0; |
| RedirTableEntry entry = 0; |
| entry.mask = 1; |
| for (int i = 0; i < TableSize; i++) { |
| redirTable[i] = entry; |
| pinStates[i] = false; |
| } |
| } |
| |
| void |
| X86ISA::I82094AA::init() |
| { |
| // The io apic must register its address ranges on both its pio port |
| // via the piodevice init() function and its int port that it inherited |
| // from IntDevice. Note IntDevice is not a SimObject itself. |
| |
| BasicPioDevice::init(); |
| IntDevice::init(); |
| } |
| |
| Port & |
| X86ISA::I82094AA::getPort(const std::string &if_name, PortID idx) |
| { |
| if (if_name == "int_master") |
| return intMasterPort; |
| return BasicPioDevice::getPort(if_name, idx); |
| } |
| |
| AddrRangeList |
| X86ISA::I82094AA::getIntAddrRange() const |
| { |
| AddrRangeList ranges; |
| ranges.push_back(RangeEx(x86InterruptAddress(initialApicId, 0), |
| x86InterruptAddress(initialApicId, 0) + |
| PhysAddrAPICRangeSize)); |
| return ranges; |
| } |
| |
| Tick |
| X86ISA::I82094AA::recvResponse(PacketPtr pkt) |
| { |
| // Packet instantiated calling sendMessage() in signalInterrupt() |
| delete pkt; |
| return 0; |
| } |
| |
| Tick |
| X86ISA::I82094AA::read(PacketPtr pkt) |
| { |
| assert(pkt->getSize() == 4); |
| Addr offset = pkt->getAddr() - pioAddr; |
| switch(offset) { |
| case 0: |
| pkt->setLE<uint32_t>(regSel); |
| break; |
| case 16: |
| pkt->setLE<uint32_t>(readReg(regSel)); |
| break; |
| default: |
| panic("Illegal read from I/O APIC.\n"); |
| } |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| Tick |
| X86ISA::I82094AA::write(PacketPtr pkt) |
| { |
| assert(pkt->getSize() == 4); |
| Addr offset = pkt->getAddr() - pioAddr; |
| switch(offset) { |
| case 0: |
| regSel = pkt->getLE<uint32_t>(); |
| break; |
| case 16: |
| writeReg(regSel, pkt->getLE<uint32_t>()); |
| break; |
| default: |
| panic("Illegal write to I/O APIC.\n"); |
| } |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| void |
| X86ISA::I82094AA::writeReg(uint8_t offset, uint32_t value) |
| { |
| if (offset == 0x0) { |
| id = bits(value, 31, 24); |
| } else if (offset == 0x1) { |
| // The IOAPICVER register is read only. |
| } else if (offset == 0x2) { |
| arbId = bits(value, 31, 24); |
| } else if (offset >= 0x10 && offset <= (0x10 + TableSize * 2)) { |
| int index = (offset - 0x10) / 2; |
| if (offset % 2) { |
| redirTable[index].topDW = value; |
| redirTable[index].topReserved = 0; |
| } else { |
| redirTable[index].bottomDW = value; |
| redirTable[index].bottomReserved = 0; |
| } |
| } else { |
| warn("Access to undefined I/O APIC register %#x.\n", offset); |
| } |
| DPRINTF(I82094AA, |
| "Wrote %#x to I/O APIC register %#x .\n", value, offset); |
| } |
| |
| uint32_t |
| X86ISA::I82094AA::readReg(uint8_t offset) |
| { |
| uint32_t result = 0; |
| if (offset == 0x0) { |
| result = id << 24; |
| } else if (offset == 0x1) { |
| result = ((TableSize - 1) << 16) | APICVersion; |
| } else if (offset == 0x2) { |
| result = arbId << 24; |
| } else if (offset >= 0x10 && offset <= (0x10 + TableSize * 2)) { |
| int index = (offset - 0x10) / 2; |
| if (offset % 2) { |
| result = redirTable[index].topDW; |
| } else { |
| result = redirTable[index].bottomDW; |
| } |
| } else { |
| warn("Access to undefined I/O APIC register %#x.\n", offset); |
| } |
| DPRINTF(I82094AA, |
| "Read %#x from I/O APIC register %#x.\n", result, offset); |
| return result; |
| } |
| |
| void |
| X86ISA::I82094AA::signalInterrupt(int line) |
| { |
| DPRINTF(I82094AA, "Received interrupt %d.\n", line); |
| assert(line < TableSize); |
| RedirTableEntry entry = redirTable[line]; |
| if (entry.mask) { |
| DPRINTF(I82094AA, "Entry was masked.\n"); |
| return; |
| } else { |
| TriggerIntMessage message = 0; |
| message.destination = entry.dest; |
| if (entry.deliveryMode == DeliveryMode::ExtInt) { |
| assert(extIntPic); |
| message.vector = extIntPic->getVector(); |
| } else { |
| message.vector = entry.vector; |
| } |
| message.deliveryMode = entry.deliveryMode; |
| message.destMode = entry.destMode; |
| message.level = entry.polarity; |
| message.trigger = entry.trigger; |
| ApicList apics; |
| int numContexts = sys->numContexts(); |
| if (message.destMode == 0) { |
| if (message.deliveryMode == DeliveryMode::LowestPriority) { |
| panic("Lowest priority delivery mode from the " |
| "IO APIC aren't supported in physical " |
| "destination mode.\n"); |
| } |
| if (message.destination == 0xFF) { |
| for (int i = 0; i < numContexts; i++) { |
| apics.push_back(i); |
| } |
| } else { |
| apics.push_back(message.destination); |
| } |
| } else { |
| for (int i = 0; i < numContexts; i++) { |
| Interrupts *localApic = sys->getThreadContext(i)-> |
| getCpuPtr()->getInterruptController(0); |
| if ((localApic->readReg(APIC_LOGICAL_DESTINATION) >> 24) & |
| message.destination) { |
| apics.push_back(localApic->getInitialApicId()); |
| } |
| } |
| if (message.deliveryMode == DeliveryMode::LowestPriority && |
| apics.size()) { |
| // The manual seems to suggest that the chipset just does |
| // something reasonable for these instead of actually using |
| // state from the local APIC. We'll just rotate an offset |
| // through the set of APICs selected above. |
| uint64_t modOffset = lowestPriorityOffset % apics.size(); |
| lowestPriorityOffset++; |
| ApicList::iterator apicIt = apics.begin(); |
| while (modOffset--) { |
| apicIt++; |
| assert(apicIt != apics.end()); |
| } |
| int selected = *apicIt; |
| apics.clear(); |
| apics.push_back(selected); |
| } |
| } |
| intMasterPort.sendMessage(apics, message, sys->isTimingMode()); |
| } |
| } |
| |
| void |
| X86ISA::I82094AA::raiseInterruptPin(int number) |
| { |
| assert(number < TableSize); |
| if (!pinStates[number]) |
| signalInterrupt(number); |
| pinStates[number] = true; |
| } |
| |
| void |
| X86ISA::I82094AA::lowerInterruptPin(int number) |
| { |
| assert(number < TableSize); |
| pinStates[number] = false; |
| } |
| |
| void |
| X86ISA::I82094AA::serialize(CheckpointOut &cp) const |
| { |
| uint64_t* redirTableArray = (uint64_t*)redirTable; |
| SERIALIZE_SCALAR(regSel); |
| SERIALIZE_SCALAR(initialApicId); |
| SERIALIZE_SCALAR(id); |
| SERIALIZE_SCALAR(arbId); |
| SERIALIZE_SCALAR(lowestPriorityOffset); |
| SERIALIZE_ARRAY(redirTableArray, TableSize); |
| SERIALIZE_ARRAY(pinStates, TableSize); |
| } |
| |
| void |
| X86ISA::I82094AA::unserialize(CheckpointIn &cp) |
| { |
| uint64_t redirTableArray[TableSize]; |
| UNSERIALIZE_SCALAR(regSel); |
| UNSERIALIZE_SCALAR(initialApicId); |
| UNSERIALIZE_SCALAR(id); |
| UNSERIALIZE_SCALAR(arbId); |
| UNSERIALIZE_SCALAR(lowestPriorityOffset); |
| UNSERIALIZE_ARRAY(redirTableArray, TableSize); |
| UNSERIALIZE_ARRAY(pinStates, TableSize); |
| for (int i = 0; i < TableSize; i++) { |
| redirTable[i] = (RedirTableEntry)redirTableArray[i]; |
| } |
| } |
| |
| X86ISA::I82094AA * |
| I82094AAParams::create() |
| { |
| return new X86ISA::I82094AA(this); |
| } |