i82094aa.cc revision 8739:925f15f96322
1/*
2 * Copyright (c) 2008 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Gabe Black
29 */
30
31#include "config/full_system.hh"
32
33#if FULL_SYSTEM
34#include "arch/x86/interrupts.hh"
35#endif
36
37#include "arch/x86/intmessage.hh"
38#include "debug/I82094AA.hh"
39#include "dev/x86/i82094aa.hh"
40#include "dev/x86/i8259.hh"
41#include "mem/packet.hh"
42#include "mem/packet_access.hh"
43#include "sim/system.hh"
44
45X86ISA::I82094AA::I82094AA(Params *p) : PioDevice(p),
46    IntDev(this, p->int_latency),
47    latency(p->pio_latency), pioAddr(p->pio_addr),
48    extIntPic(p->external_int_pic), lowestPriorityOffset(0)
49{
50    // This assumes there's only one I/O APIC in the system and since the apic
51    // id is stored in a 8-bit field with 0xff meaning broadcast, the id must
52    // be less than 0xff
53
54    assert(p->apic_id < 0xff);
55    initialApicId = id = p->apic_id;
56    arbId = id;
57    regSel = 0;
58    RedirTableEntry entry = 0;
59    entry.mask = 1;
60    for (int i = 0; i < TableSize; i++) {
61        redirTable[i] = entry;
62        pinStates[i] = false;
63    }
64}
65
66void
67X86ISA::I82094AA::init()
68{
69    // The io apic must register its address ranges on both its pio port
70    // via the piodevice init() function and its int port that it inherited
71    // from IntDev.  Note IntDev is not a SimObject itself.
72
73    PioDevice::init();
74    IntDev::init();
75}
76
77Tick
78X86ISA::I82094AA::read(PacketPtr pkt)
79{
80    assert(pkt->getSize() == 4);
81    Addr offset = pkt->getAddr() - pioAddr;
82    switch(offset) {
83      case 0:
84        pkt->set<uint32_t>(regSel);
85        break;
86      case 16:
87        pkt->set<uint32_t>(readReg(regSel));
88        break;
89      default:
90        panic("Illegal read from I/O APIC.\n");
91    }
92    pkt->makeAtomicResponse();
93    return latency;
94}
95
96Tick
97X86ISA::I82094AA::write(PacketPtr pkt)
98{
99    assert(pkt->getSize() == 4);
100    Addr offset = pkt->getAddr() - pioAddr;
101    switch(offset) {
102      case 0:
103        regSel = pkt->get<uint32_t>();
104        break;
105      case 16:
106        writeReg(regSel, pkt->get<uint32_t>());
107        break;
108      default:
109        panic("Illegal write to I/O APIC.\n");
110    }
111    pkt->makeAtomicResponse();
112    return latency;
113}
114
115void
116X86ISA::I82094AA::writeReg(uint8_t offset, uint32_t value)
117{
118    if (offset == 0x0) {
119        id = bits(value, 31, 24);
120    } else if (offset == 0x1) {
121        // The IOAPICVER register is read only.
122    } else if (offset == 0x2) {
123        arbId = bits(value, 31, 24);
124    } else if (offset >= 0x10 && offset <= (0x10 + TableSize * 2)) {
125        int index = (offset - 0x10) / 2;
126        if (offset % 2) {
127            redirTable[index].topDW = value;
128            redirTable[index].topReserved = 0;
129        } else {
130            redirTable[index].bottomDW = value;
131            redirTable[index].bottomReserved = 0;
132        }
133    } else {
134        warn("Access to undefined I/O APIC register %#x.\n", offset);
135    }
136    DPRINTF(I82094AA,
137            "Wrote %#x to I/O APIC register %#x .\n", value, offset);
138}
139
140uint32_t
141X86ISA::I82094AA::readReg(uint8_t offset)
142{
143    uint32_t result = 0;
144    if (offset == 0x0) {
145        result = id << 24;
146    } else if (offset == 0x1) {
147        result = ((TableSize - 1) << 16) | APICVersion;
148    } else if (offset == 0x2) {
149        result = arbId << 24;
150    } else if (offset >= 0x10 && offset <= (0x10 + TableSize * 2)) {
151        int index = (offset - 0x10) / 2;
152        if (offset % 2) {
153            result = redirTable[index].topDW;
154        } else {
155            result = redirTable[index].bottomDW;
156        }
157    } else {
158        warn("Access to undefined I/O APIC register %#x.\n", offset);
159    }
160    DPRINTF(I82094AA,
161            "Read %#x from I/O APIC register %#x.\n", result, offset);
162    return result;
163}
164
165void
166X86ISA::I82094AA::signalInterrupt(int line)
167{
168    DPRINTF(I82094AA, "Received interrupt %d.\n", line);
169    assert(line < TableSize);
170    RedirTableEntry entry = redirTable[line];
171    if (entry.mask) {
172        DPRINTF(I82094AA, "Entry was masked.\n");
173        return;
174    } else {
175#if FULL_SYSTEM //XXX No interrupt controller in SE mode.
176        TriggerIntMessage message = 0;
177        message.destination = entry.dest;
178        if (entry.deliveryMode == DeliveryMode::ExtInt) {
179            assert(extIntPic);
180            message.vector = extIntPic->getVector();
181        } else {
182            message.vector = entry.vector;
183        }
184        message.deliveryMode = entry.deliveryMode;
185        message.destMode = entry.destMode;
186        message.level = entry.polarity;
187        message.trigger = entry.trigger;
188        ApicList apics;
189        int numContexts = sys->numContexts();
190        if (message.destMode == 0) {
191            if (message.deliveryMode == DeliveryMode::LowestPriority) {
192                panic("Lowest priority delivery mode from the "
193                        "IO APIC aren't supported in physical "
194                        "destination mode.\n");
195            }
196            if (message.destination == 0xFF) {
197                for (int i = 0; i < numContexts; i++) {
198                    apics.push_back(i);
199                }
200            } else {
201                apics.push_back(message.destination);
202            }
203        } else {
204            for (int i = 0; i < numContexts; i++) {
205                std::map<int, Interrupts *>::iterator localApicIt =
206                    localApics.find(i);
207                assert(localApicIt != localApics.end());
208                Interrupts *localApic = localApicIt->second;
209                if ((localApic->readReg(APIC_LOGICAL_DESTINATION) >> 24) &
210                        message.destination) {
211                    apics.push_back(localApicIt->first);
212                }
213            }
214            if (message.deliveryMode == DeliveryMode::LowestPriority &&
215                    apics.size()) {
216                // The manual seems to suggest that the chipset just does
217                // something reasonable for these instead of actually using
218                // state from the local APIC. We'll just rotate an offset
219                // through the set of APICs selected above.
220                uint64_t modOffset = lowestPriorityOffset % apics.size();
221                lowestPriorityOffset++;
222                ApicList::iterator apicIt = apics.begin();
223                while (modOffset--) {
224                    apicIt++;
225                    assert(apicIt != apics.end());
226                }
227                int selected = *apicIt;
228                apics.clear();
229                apics.push_back(selected);
230            }
231        }
232        intPort->sendMessage(apics, message,
233                sys->getMemoryMode() == Enums::timing);
234#endif
235    }
236}
237
238void
239X86ISA::I82094AA::raiseInterruptPin(int number)
240{
241    assert(number < TableSize);
242    if (!pinStates[number])
243        signalInterrupt(number);
244    pinStates[number] = true;
245}
246
247void
248X86ISA::I82094AA::lowerInterruptPin(int number)
249{
250    assert(number < TableSize);
251    pinStates[number] = false;
252}
253
254void
255X86ISA::I82094AA::registerLocalApic(int initialId, Interrupts *localApic)
256{
257    assert(localApic);
258    localApics[initialId] = localApic;
259}
260
261void
262X86ISA::I82094AA::serialize(std::ostream &os)
263{
264    uint64_t* redirTableArray = (uint64_t*)redirTable;
265    SERIALIZE_SCALAR(regSel);
266    SERIALIZE_SCALAR(initialApicId);
267    SERIALIZE_SCALAR(id);
268    SERIALIZE_SCALAR(arbId);
269    SERIALIZE_SCALAR(lowestPriorityOffset);
270    SERIALIZE_ARRAY(redirTableArray, TableSize);
271    SERIALIZE_ARRAY(pinStates, TableSize);
272}
273
274void
275X86ISA::I82094AA::unserialize(Checkpoint *cp, const std::string &section)
276{
277    uint64_t redirTableArray[TableSize];
278    UNSERIALIZE_SCALAR(regSel);
279    UNSERIALIZE_SCALAR(initialApicId);
280    UNSERIALIZE_SCALAR(id);
281    UNSERIALIZE_SCALAR(arbId);
282    UNSERIALIZE_SCALAR(lowestPriorityOffset);
283    UNSERIALIZE_ARRAY(redirTableArray, TableSize);
284    UNSERIALIZE_ARRAY(pinStates, TableSize);
285    for (int i = 0; i < TableSize; i++) {
286        redirTable[i] = (RedirTableEntry)redirTableArray[i];
287    }
288}
289
290X86ISA::I82094AA *
291I82094AAParams::create()
292{
293    return new X86ISA::I82094AA(this);
294}
295