summaryrefslogtreecommitdiff
path: root/src/arch
diff options
context:
space:
mode:
authorGabe Black <gblack@eecs.umich.edu>2008-10-12 11:08:00 -0700
committerGabe Black <gblack@eecs.umich.edu>2008-10-12 11:08:00 -0700
commit42ebebf99a7d6ce2358b152f643b52c7946f9202 (patch)
tree2255b51553c45c650a94950b1654cdec917ff339 /src/arch
parentd9f9c967fbe651e09d444e460a9b1c5a450b1cd2 (diff)
downloadgem5-42ebebf99a7d6ce2358b152f643b52c7946f9202.tar.xz
X86: Make the local APIC accessible through the memory system directly, and make the timer work.
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/x86/interrupts.cc207
-rw-r--r--src/arch/x86/interrupts.hh33
-rw-r--r--src/arch/x86/miscregfile.cc25
-rw-r--r--src/arch/x86/miscregs.hh11
-rw-r--r--src/arch/x86/mmaped_ipr.hh6
-rw-r--r--src/arch/x86/tlb.cc141
-rw-r--r--src/arch/x86/utility.cc2
-rw-r--r--src/arch/x86/x86_traits.hh17
8 files changed, 237 insertions, 205 deletions
diff --git a/src/arch/x86/interrupts.cc b/src/arch/x86/interrupts.cc
index 73536f2b4..5814859b3 100644
--- a/src/arch/x86/interrupts.cc
+++ b/src/arch/x86/interrupts.cc
@@ -55,10 +55,12 @@
* Authors: Gabe Black
*/
+#include "arch/x86/apicregs.hh"
#include "arch/x86/interrupts.hh"
#include "cpu/base.hh"
-int divideFromConf(uint32_t conf)
+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
@@ -68,14 +70,171 @@ int divideFromConf(uint32_t conf)
return 1 << shift;
}
-uint32_t
-X86ISA::Interrupts::readRegNoEffect(ApicRegIndex reg)
+namespace X86ISA
{
- return regs[reg];
+
+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 0x108:
+ case 0x110:
+ case 0x118:
+ case 0x120:
+ case 0x128:
+ case 0x130:
+ case 0x138:
+ case 0x140:
+ case 0x148:
+ case 0x150:
+ case 0x158:
+ case 0x160:
+ case 0x168:
+ case 0x170:
+ case 0x178:
+ regNum = APIC_IN_SERVICE((paddr - 0x100) / 0x8);
+ break;
+ case 0x180:
+ case 0x188:
+ case 0x190:
+ case 0x198:
+ case 0x1A0:
+ case 0x1A8:
+ case 0x1B0:
+ case 0x1B8:
+ case 0x1C0:
+ case 0x1C8:
+ case 0x1D0:
+ case 0x1D8:
+ case 0x1E0:
+ case 0x1E8:
+ case 0x1F0:
+ case 0x1F8:
+ regNum = APIC_TRIGGER_MODE((paddr - 0x180) / 0x8);
+ break;
+ case 0x200:
+ case 0x208:
+ case 0x210:
+ case 0x218:
+ case 0x220:
+ case 0x228:
+ case 0x230:
+ case 0x238:
+ case 0x240:
+ case 0x248:
+ case 0x250:
+ case 0x258:
+ case 0x260:
+ case 0x268:
+ case 0x270:
+ case 0x278:
+ regNum = APIC_INTERRUPT_REQUEST((paddr - 0x200) / 0x8);
+ 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));
+ pkt->setData(((uint8_t *)&val) + (offset & mask(3)));
+ return latency;
+}
+
+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)));
+ setReg(reg, gtoh(val));
+ return latency;
}
uint32_t
-X86ISA::Interrupts::readReg(ApicRegIndex reg, ThreadContext * tc)
+X86ISA::Interrupts::readReg(ApicRegIndex reg)
{
if (reg >= APIC_TRIGGER_MODE(0) &&
reg <= APIC_TRIGGER_MODE(15)) {
@@ -104,24 +263,19 @@ X86ISA::Interrupts::readReg(ApicRegIndex reg, ThreadContext * tc)
break;
case APIC_CURRENT_COUNT:
{
- uint32_t val = regs[reg] - tc->getCpuPtr()->curCycle();
+ assert(clock);
+ uint32_t val = regs[reg] - curTick / clock;
val /= (16 * divideFromConf(regs[APIC_DIVIDE_CONFIGURATION]));
return val;
}
default:
break;
}
- return readRegNoEffect(reg);
-}
-
-void
-X86ISA::Interrupts::setRegNoEffect(ApicRegIndex reg, uint32_t val)
-{
- regs[reg] = val;
+ return regs[reg];
}
void
-X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc)
+X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val)
{
uint32_t newVal = val;
if (reg >= APIC_IN_SERVICE(0) &&
@@ -201,11 +355,24 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc)
}
break;
case APIC_INITIAL_COUNT:
- newVal = bits(val, 31, 0);
- regs[APIC_CURRENT_COUNT] =
- tc->getCpuPtr()->curCycle() +
- (16 * divideFromConf(regs[APIC_DIVIDE_CONFIGURATION])) * newVal;
- //FIXME This should schedule the timer event.
+ {
+ 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;
+ // Schedule on the edge of the next tick plus the new count.
+ Tick offset = curTick % timerTick;
+ if (offset) {
+ reschedule(apicTimerEvent,
+ curTick + (newCount + 1) * timerTick - offset, true);
+ } else {
+ reschedule(apicTimerEvent,
+ curTick + newCount * timerTick, true);
+ }
+ }
break;
case APIC_CURRENT_COUNT:
//Local APIC Current Count register is read only.
@@ -216,7 +383,7 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc)
default:
break;
}
- setRegNoEffect(reg, newVal);
+ regs[reg] = newVal;
return;
}
diff --git a/src/arch/x86/interrupts.hh b/src/arch/x86/interrupts.hh
index 68bb2e07d..1d5f600bd 100644
--- a/src/arch/x86/interrupts.hh
+++ b/src/arch/x86/interrupts.hh
@@ -61,6 +61,7 @@
#include "arch/x86/apicregs.hh"
#include "arch/x86/faults.hh"
#include "cpu/thread_context.hh"
+#include "dev/io_device.hh"
#include "params/X86LocalApic.hh"
#include "sim/eventq.hh"
#include "sim/sim_object.hh"
@@ -70,10 +71,12 @@ class ThreadContext;
namespace X86ISA
{
-class Interrupts : public SimObject
+class Interrupts : public BasicPioDevice
{
protected:
uint32_t regs[NUM_APIC_REGS];
+ Tick latency;
+ Tick clock;
class ApicTimerEvent : public Event
{
@@ -92,20 +95,38 @@ class Interrupts : public SimObject
public:
typedef X86LocalApicParams Params;
+ void setClock(Tick newClock)
+ {
+ clock = newClock;
+ }
+
const Params *
params() const
{
return dynamic_cast<const Params *>(_params);
}
- uint32_t readRegNoEffect(ApicRegIndex reg);
- uint32_t readReg(ApicRegIndex miscReg, ThreadContext *tc);
+ Tick read(PacketPtr pkt);
+ Tick write(PacketPtr pkt);
+
+ void addressRanges(AddrRangeList &range_list)
+ {
+ range_list.clear();
+ range_list.push_back(RangeEx(x86LocalAPICAddress(0, 0),
+ x86LocalAPICAddress(0, 0) + PageBytes));
+ }
- void setRegNoEffect(ApicRegIndex reg, uint32_t val);
- void setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc);
+ uint32_t readReg(ApicRegIndex miscReg);
+ void setReg(ApicRegIndex reg, uint32_t val);
+ void setRegNoEffect(ApicRegIndex reg, uint32_t val)
+ {
+ regs[reg] = val;
+ }
- Interrupts(Params * p) : SimObject(p)
+ Interrupts(Params * p) : BasicPioDevice(p),
+ latency(p->pio_latency), clock(0)
{
+ pioSize = PageBytes;
//Set the local apic DFR to the flat model.
regs[APIC_DESTINATION_FORMAT] = (uint32_t)(-1);
memset(regs, 0, sizeof(regs));
diff --git a/src/arch/x86/miscregfile.cc b/src/arch/x86/miscregfile.cc
index aba498616..388a83e8d 100644
--- a/src/arch/x86/miscregfile.cc
+++ b/src/arch/x86/miscregfile.cc
@@ -119,23 +119,11 @@ MiscReg MiscRegFile::readRegNoEffect(MiscRegIndex miscReg)
!(miscReg > MISCREG_CR8 &&
miscReg <= MISCREG_CR15));
- if (isApicReg(miscReg)) {
- panic("Can't readRegNoEffect from the local APIC.\n");
- }
return regVal[miscReg];
}
MiscReg MiscRegFile::readReg(MiscRegIndex miscReg, ThreadContext * tc)
{
-#if FULL_SYSTEM
- if (isApicReg(miscReg)) {
- Interrupts * interrupts = dynamic_cast<Interrupts *>(
- tc->getCpuPtr()->getInterruptController());
- assert(interrupts);
- return interrupts->readReg(
- (ApicRegIndex)(miscReg - MISCREG_APIC_START), tc);
- }
-#endif
if (miscReg == MISCREG_TSC) {
return regVal[MISCREG_TSC] + tc->getCpuPtr()->curCycle();
}
@@ -152,9 +140,6 @@ void MiscRegFile::setRegNoEffect(MiscRegIndex miscReg, const MiscReg &val)
miscReg < MISCREG_CR8) &&
!(miscReg > MISCREG_CR8 &&
miscReg <= MISCREG_CR15));
- if (isApicReg(miscReg)) {
- panic("Can't setRegNoEffect from the local APIC.\n");
- }
regVal[miscReg] = val;
}
@@ -162,16 +147,6 @@ void MiscRegFile::setReg(MiscRegIndex miscReg,
const MiscReg &val, ThreadContext * tc)
{
MiscReg newVal = val;
-#if FULL_SYSTEM
- if (isApicReg(miscReg)) {
- Interrupts * interrupts = dynamic_cast<Interrupts *>(
- tc->getCpuPtr()->getInterruptController());
- assert(interrupts);
- interrupts->setReg(
- ApicRegIndex(miscReg - MISCREG_APIC_START), val, tc);
- return;
- }
-#endif
switch(miscReg)
{
case MISCREG_CR0:
diff --git a/src/arch/x86/miscregs.hh b/src/arch/x86/miscregs.hh
index dbf08ec5d..a536d9e3b 100644
--- a/src/arch/x86/miscregs.hh
+++ b/src/arch/x86/miscregs.hh
@@ -58,7 +58,6 @@
#ifndef __ARCH_X86_MISCREGS_HH__
#define __ARCH_X86_MISCREGS_HH__
-#include "arch/x86/apicregs.hh"
#include "arch/x86/segmentregs.hh"
#include "arch/x86/x86_traits.hh"
#include "base/bitunion.hh"
@@ -369,22 +368,12 @@ namespace X86ISA
MISCREG_APIC_BASE,
- // Space for the APIC registers
- MISCREG_APIC_START,
- MISCREG_APIC_END = MISCREG_APIC_START + NUM_APIC_REGS - 1,
-
// "Fake" MSRs for internally implemented devices
MISCREG_PCI_CONFIG_ADDRESS,
NUM_MISCREGS
};
- static inline bool
- isApicReg(MiscRegIndex index)
- {
- return index >= MISCREG_APIC_START && index <= MISCREG_APIC_END;
- }
-
static inline MiscRegIndex
MISCREG_CR(int index)
{
diff --git a/src/arch/x86/mmaped_ipr.hh b/src/arch/x86/mmaped_ipr.hh
index 67a0b239e..7056c0902 100644
--- a/src/arch/x86/mmaped_ipr.hh
+++ b/src/arch/x86/mmaped_ipr.hh
@@ -97,11 +97,7 @@ namespace X86ISA
Addr offset = pkt->getAddr() & mask(3);
MiscRegIndex index = (MiscRegIndex)(pkt->getAddr() / sizeof(MiscReg));
MiscReg data;
- if (isApicReg(index)) {
- data = htog(xc->readMiscReg(index));
- } else {
- data = htog(xc->readMiscRegNoEffect(index));
- }
+ data = htog(xc->readMiscRegNoEffect(index));
// Make sure we don't trot off the end of data.
assert(offset + pkt->getSize() <= sizeof(MiscReg));
pkt->writeData(((uint8_t *)&data) + offset);
diff --git a/src/arch/x86/tlb.cc b/src/arch/x86/tlb.cc
index 616f026cf..5db678919 100644
--- a/src/arch/x86/tlb.cc
+++ b/src/arch/x86/tlb.cc
@@ -638,10 +638,9 @@ TLB::translate(RequestPtr &req, ThreadContext *tc, bool write, bool execute)
// Check for an access to the local APIC
#if FULL_SYSTEM
LocalApicBase localApicBase = tc->readMiscRegNoEffect(MISCREG_APIC_BASE);
- Addr baseAddr = localApicBase.base << 12;
+ Addr baseAddr = localApicBase.base * PageBytes;
Addr paddr = req->getPaddr();
- if (baseAddr <= paddr && baseAddr + (1 << 12) > paddr) {
- req->setMmapedIpr(true);
+ if (baseAddr <= paddr && baseAddr + PageBytes > paddr) {
// The Intel developer's manuals say the below restrictions apply,
// but the linux kernel, because of a compiler optimization, breaks
// them.
@@ -653,139 +652,9 @@ TLB::translate(RequestPtr &req, ThreadContext *tc, bool write, bool execute)
if (req->getSize() != (32/8))
return new GeneralProtection(0);
*/
-
- //Make sure we're at least only accessing one register.
- if ((paddr & ~mask(3)) != ((paddr + req->getSize()) & ~mask(3)))
- panic("Accessed more than one register at a time in the APIC!\n");
- MiscReg regNum;
- Addr offset = paddr & mask(3);
- paddr &= ~mask(3);
- switch (paddr - baseAddr)
- {
- 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 0x108:
- case 0x110:
- case 0x118:
- case 0x120:
- case 0x128:
- case 0x130:
- case 0x138:
- case 0x140:
- case 0x148:
- case 0x150:
- case 0x158:
- case 0x160:
- case 0x168:
- case 0x170:
- case 0x178:
- regNum = APIC_IN_SERVICE((paddr - baseAddr - 0x100) / 0x8);
- break;
- case 0x180:
- case 0x188:
- case 0x190:
- case 0x198:
- case 0x1A0:
- case 0x1A8:
- case 0x1B0:
- case 0x1B8:
- case 0x1C0:
- case 0x1C8:
- case 0x1D0:
- case 0x1D8:
- case 0x1E0:
- case 0x1E8:
- case 0x1F0:
- case 0x1F8:
- regNum = APIC_TRIGGER_MODE((paddr - baseAddr - 0x180) / 0x8);
- break;
- case 0x200:
- case 0x208:
- case 0x210:
- case 0x218:
- case 0x220:
- case 0x228:
- case 0x230:
- case 0x238:
- case 0x240:
- case 0x248:
- case 0x250:
- case 0x258:
- case 0x260:
- case 0x268:
- case 0x270:
- case 0x278:
- regNum = APIC_INTERRUPT_REQUEST((paddr - baseAddr - 0x200) / 0x8);
- 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.
- return new GeneralProtection(0);
- break;
- }
- regNum += MISCREG_APIC_START;
- req->setPaddr(regNum * sizeof(MiscReg) + offset);
+ // Force the access to be uncacheable.
+ req->setFlags(req->getFlags() | UNCACHEABLE);
+ req->setPaddr(x86LocalAPICAddress(tc->readCpuId(), paddr - baseAddr));
}
#endif
return NoFault;
diff --git a/src/arch/x86/utility.cc b/src/arch/x86/utility.cc
index 852d72a2e..43a5ca1a9 100644
--- a/src/arch/x86/utility.cc
+++ b/src/arch/x86/utility.cc
@@ -267,6 +267,8 @@ void initCPU(ThreadContext *tc, int cpuId)
interrupts->setRegNoEffect(APIC_ID, cpuId << 24);
interrupts->setRegNoEffect(APIC_VERSION, (5 << 16) | 0x14);
+
+ interrupts->setClock(tc->getCpuPtr()->ticks(16));
// TODO Set the SMRAM base address (SMBASE) to 0x00030000
diff --git a/src/arch/x86/x86_traits.hh b/src/arch/x86/x86_traits.hh
index f46279c81..6b4671a08 100644
--- a/src/arch/x86/x86_traits.hh
+++ b/src/arch/x86/x86_traits.hh
@@ -55,11 +55,13 @@
* Authors: Gabe Black
*/
-#include "sim/host.hh"
-
#ifndef __ARCH_X86_X86TRAITS_HH__
#define __ARCH_X86_X86TRAITS_HH__
+#include <assert.h>
+
+#include "sim/host.hh"
+
namespace X86ISA
{
const int NumMicroIntRegs = 16;
@@ -90,6 +92,10 @@ namespace X86ISA
const Addr PhysAddrPrefixIO = ULL(0x8000000000000000);
const Addr PhysAddrPrefixPciConfig = ULL(0xC000000000000000);
+ const Addr PhysAddrPrefixLocalAPIC = ULL(0xA000000000000000);
+ // Each APIC gets two pages. One page is used for local apics to field
+ // accesses from the CPU, and the other is for all APICs to communicate.
+ const Addr PhysAddrAPICRangeSize = 1 << 12;
static inline Addr
x86IOAddress(const uint32_t port)
@@ -102,6 +108,13 @@ namespace X86ISA
{
return PhysAddrPrefixPciConfig | addr;
}
+
+ static inline Addr
+ x86LocalAPICAddress(const uint8_t id, const uint16_t addr)
+ {
+ assert(addr < (1 << 12));
+ return PhysAddrPrefixLocalAPIC | (id * (1 << 12)) | addr;
+ }
}
#endif //__ARCH_X86_X86TRAITS_HH__