From f3f06e1684c6575e25b965ab0585473bb47e58cc Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Sat, 23 May 2015 13:46:56 +0100 Subject: [PATCH] arm, dev: Add support for a memory mapped generic timer There are cases when we don't want to use a system register mapped generic timer, but can't use the SP804. For example, when using KVM on aarch64, we want to intercept accesses to the generic timer, but can't do so if it is using the system register interface. In such cases, we need to use a memory-mapped generic timer. This changeset adds a device model that implements the memory mapped generic timer interface. The current implementation only supports a single frame (i.e., one virtual timer and one physical timer). --- src/dev/arm/RealView.py | 10 + src/dev/arm/generic_timer.cc | 341 +++++++++++++++++++++++++++++++++++ src/dev/arm/generic_timer.hh | 58 ++++++ 3 files changed, 409 insertions(+) diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index 2d5ccc3f9..5365ac9de 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -141,6 +141,16 @@ class GenericTimer(SimObject): int_phys = Param.UInt32("Physical timer interrupt number") int_virt = Param.UInt32("Virtual timer interrupt number") +class GenericTimerMem(PioDevice): + type = 'GenericTimerMem' + cxx_header = "dev/arm/generic_timer.hh" + gic = Param.BaseGic(Parent.any, "GIC to use for interrupting") + + base = Param.Addr(0, "Base address") + + int_phys = Param.UInt32("Interrupt number") + int_virt = Param.UInt32("Interrupt number") + class PL031(AmbaIntDevice): type = 'PL031' cxx_header = "dev/arm/rtc_pl031.hh" diff --git a/src/dev/arm/generic_timer.cc b/src/dev/arm/generic_timer.cc index 1ff91d50b..41cb4bada 100644 --- a/src/dev/arm/generic_timer.cc +++ b/src/dev/arm/generic_timer.cc @@ -43,7 +43,9 @@ #include "arch/arm/system.hh" #include "debug/Timer.hh" #include "dev/arm/base_gic.hh" +#include "mem/packet_access.hh" #include "params/GenericTimer.hh" +#include "params/GenericTimerMem.hh" SystemCounter::SystemCounter() : _freq(0), _period(0), _resetTick(0), _regCntkctl(0) @@ -479,8 +481,347 @@ GenericTimer::readMiscReg(int reg, unsigned cpu) } + +GenericTimerMem::GenericTimerMem(GenericTimerMemParams *p) + : PioDevice(p), + ctrlRange(RangeSize(p->base, TheISA::PageBytes)), + timerRange(RangeSize(p->base + TheISA::PageBytes, TheISA::PageBytes)), + addrRanges{ctrlRange, timerRange}, + systemCounter(), + physTimer(csprintf("%s.phys_timer0", name()), + *this, systemCounter, + ArchTimer::Interrupt(*p->gic, p->int_phys)), + virtTimer(csprintf("%s.virt_timer0", name()), + *this, systemCounter, + ArchTimer::Interrupt(*p->gic, p->int_virt)) +{ +} + +void +GenericTimerMem::serialize(std::ostream &os) +{ + paramOut(os, "timer_count", 1); + + nameOut(os, csprintf("%s.sys_counter", name())); + systemCounter.serialize(os); + + nameOut(os, physTimer.name()); + physTimer.serialize(os); + + nameOut(os, virtTimer.name()); + virtTimer.serialize(os); +} + +void +GenericTimerMem::unserialize(Checkpoint *cp, const std::string §ion) +{ + systemCounter.unserialize(cp, csprintf("%s.sys_counter", section)); + + unsigned timer_count; + UNSERIALIZE_SCALAR(timer_count); + // The timer count variable is just here for future versions where + // we support more than one set of timers. + if (timer_count != 1) + panic("Incompatible checkpoint: Only one set of timers supported"); + + physTimer.unserialize(cp, csprintf("%s.phys_timer0", section)); + virtTimer.unserialize(cp, csprintf("%s.virt_timer0", section)); +} + +Tick +GenericTimerMem::read(PacketPtr pkt) +{ + const unsigned size(pkt->getSize()); + const Addr addr(pkt->getAddr()); + uint64_t value; + + pkt->makeResponse(); + if (ctrlRange.contains(addr)) { + value = ctrlRead(addr - ctrlRange.start(), size); + } else if (timerRange.contains(addr)) { + value = timerRead(addr - timerRange.start(), size); + } else { + panic("Invalid address: 0x%x\n", addr); + } + + DPRINTF(Timer, "Read 0x%x <- 0x%x(%i)\n", value, addr, size); + + if (size == 8) { + pkt->set(value); + } else if (size == 4) { + pkt->set(value); + } else { + panic("Unexpected access size: %i\n", size); + } + + return 0; +} + +Tick +GenericTimerMem::write(PacketPtr pkt) +{ + const unsigned size(pkt->getSize()); + if (size != 8 && size != 4) + panic("Unexpected access size\n"); + + const Addr addr(pkt->getAddr()); + const uint64_t value(size == 8 ? + pkt->get() : pkt->get()); + + DPRINTF(Timer, "Write 0x%x -> 0x%x(%i)\n", value, addr, size); + if (ctrlRange.contains(addr)) { + ctrlWrite(addr - ctrlRange.start(), size, value); + } else if (timerRange.contains(addr)) { + timerWrite(addr - timerRange.start(), size, value); + } else { + panic("Invalid address: 0x%x\n", addr); + } + + pkt->makeResponse(); + return 0; +} + +uint64_t +GenericTimerMem::ctrlRead(Addr addr, size_t size) const +{ + if (size == 4) { + switch (addr) { + case CTRL_CNTFRQ: + return systemCounter.freq(); + + case CTRL_CNTTIDR: + return 0x3; // Frame 0 implemented with virtual timers + + case CTRL_CNTNSAR: + case CTRL_CNTACR_BASE: + warn("Reading from unimplemented control register (0x%x)\n", addr); + return 0; + + case CTRL_CNTVOFF_LO_BASE: + return virtTimer.offset(); + + case CTRL_CNTVOFF_HI_BASE: + return virtTimer.offset() >> 32; + + default: + warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); + return 0; + } + } else if (size == 8) { + switch (addr) { + case CTRL_CNTVOFF_LO_BASE: + return virtTimer.offset(); + + default: + warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); + return 0; + } + } else { + panic("Invalid access size: %i\n", size); + } +} + +void +GenericTimerMem::ctrlWrite(Addr addr, size_t size, uint64_t value) +{ + if (size == 4) { + switch (addr) { + case CTRL_CNTFRQ: + case CTRL_CNTNSAR: + case CTRL_CNTTIDR: + case CTRL_CNTACR_BASE: + warn("Write to unimplemented control register (0x%x)\n", addr); + return; + + case CTRL_CNTVOFF_LO_BASE: + virtTimer.setOffset( + insertBits(virtTimer.offset(), 31, 0, value)); + return; + + case CTRL_CNTVOFF_HI_BASE: + virtTimer.setOffset( + insertBits(virtTimer.offset(), 63, 32, value)); + return; + + default: + warn("Ignoring write to unexpected address (0x%x:%i)\n", + addr, size); + return; + } + } else if (size == 8) { + switch (addr) { + case CTRL_CNTVOFF_LO_BASE: + virtTimer.setOffset(value); + return; + + default: + warn("Ignoring write to unexpected address (0x%x:%i)\n", + addr, size); + return; + } + } else { + panic("Invalid access size: %i\n", size); + } +} + +uint64_t +GenericTimerMem::timerRead(Addr addr, size_t size) const +{ + if (size == 4) { + switch (addr) { + case TIMER_CNTPCT_LO: + return physTimer.value(); + + case TIMER_CNTPCT_HI: + return physTimer.value() >> 32; + + case TIMER_CNTVCT_LO: + return virtTimer.value(); + + case TIMER_CNTVCT_HI: + return virtTimer.value() >> 32; + + case TIMER_CNTFRQ: + return systemCounter.freq(); + + case TIMER_CNTEL0ACR: + warn("Read from unimplemented timer register (0x%x)\n", addr); + return 0; + + case CTRL_CNTVOFF_LO_BASE: + return virtTimer.offset(); + + case CTRL_CNTVOFF_HI_BASE: + return virtTimer.offset() >> 32; + + case TIMER_CNTP_CVAL_LO: + return physTimer.compareValue(); + + case TIMER_CNTP_CVAL_HI: + return physTimer.compareValue() >> 32; + + case TIMER_CNTP_TVAL: + return physTimer.timerValue(); + + case TIMER_CNTP_CTL: + return physTimer.control(); + + case TIMER_CNTV_CVAL_LO: + return virtTimer.compareValue(); + + case TIMER_CNTV_CVAL_HI: + return virtTimer.compareValue() >> 32; + + case TIMER_CNTV_TVAL: + return virtTimer.timerValue(); + + case TIMER_CNTV_CTL: + return virtTimer.control(); + + default: + warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); + return 0; + } + } else if (size == 8) { + switch (addr) { + case TIMER_CNTPCT_LO: + return physTimer.value(); + + case TIMER_CNTVCT_LO: + return virtTimer.value(); + + case CTRL_CNTVOFF_LO_BASE: + return virtTimer.offset(); + + case TIMER_CNTP_CVAL_LO: + return physTimer.compareValue(); + + case TIMER_CNTV_CVAL_LO: + return virtTimer.compareValue(); + + default: + warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); + return 0; + } + } else { + panic("Invalid access size: %i\n", size); + } +} + +void +GenericTimerMem::timerWrite(Addr addr, size_t size, uint64_t value) +{ + if (size == 4) { + switch (addr) { + case TIMER_CNTEL0ACR: + warn("Unimplemented timer register (0x%x)\n", addr); + return; + + case TIMER_CNTP_CVAL_LO: + physTimer.setCompareValue( + insertBits(physTimer.compareValue(), 31, 0, value)); + return; + + case TIMER_CNTP_CVAL_HI: + physTimer.setCompareValue( + insertBits(physTimer.compareValue(), 63, 32, value)); + return; + + case TIMER_CNTP_TVAL: + physTimer.setTimerValue(value); + return; + + case TIMER_CNTP_CTL: + physTimer.setControl(value); + return; + + case TIMER_CNTV_CVAL_LO: + virtTimer.setCompareValue( + insertBits(virtTimer.compareValue(), 31, 0, value)); + return; + + case TIMER_CNTV_CVAL_HI: + virtTimer.setCompareValue( + insertBits(virtTimer.compareValue(), 63, 32, value)); + return; + + case TIMER_CNTV_TVAL: + virtTimer.setTimerValue(value); + return; + + case TIMER_CNTV_CTL: + virtTimer.setControl(value); + return; + + default: + warn("Unexpected address (0x%x:%i), ignoring write\n", addr, size); + return; + } + } else if (size == 8) { + switch (addr) { + case TIMER_CNTP_CVAL_LO: + return physTimer.setCompareValue(value); + + case TIMER_CNTV_CVAL_LO: + return virtTimer.setCompareValue(value); + + default: + warn("Unexpected address (0x%x:%i), ignoring write\n", addr, size); + return; + } + } else { + panic("Invalid access size: %i\n", size); + } +} + GenericTimer * GenericTimerParams::create() { return new GenericTimer(this); } + +GenericTimerMem * +GenericTimerMemParams::create() +{ + return new GenericTimerMem(this); +} diff --git a/src/dev/arm/generic_timer.hh b/src/dev/arm/generic_timer.hh index 7e6bd2af3..d8f7f54e2 100644 --- a/src/dev/arm/generic_timer.hh +++ b/src/dev/arm/generic_timer.hh @@ -54,6 +54,7 @@ class Checkpoint; class GenericTimerParams; +class GenericTimerMemParams; /// Global system counter. It is shared by the architected timers. /// @todo: implement memory-mapped controls @@ -273,4 +274,61 @@ class GenericTimerISA : public ArmISA::BaseISADevice unsigned cpu; }; +class GenericTimerMem : public PioDevice +{ + public: + GenericTimerMem(GenericTimerMemParams *p); + + void serialize(std::ostream &os) M5_ATTR_OVERRIDE; + void unserialize(Checkpoint *cp, const std::string &sec) M5_ATTR_OVERRIDE; + + public: // PioDevice + AddrRangeList getAddrRanges() const M5_ATTR_OVERRIDE { return addrRanges; } + Tick read(PacketPtr pkt) M5_ATTR_OVERRIDE; + Tick write(PacketPtr pkt) M5_ATTR_OVERRIDE; + + protected: + uint64_t ctrlRead(Addr addr, size_t size) const; + void ctrlWrite(Addr addr, size_t size, uint64_t value); + + uint64_t timerRead(Addr addr, size_t size) const; + void timerWrite(Addr addr, size_t size, uint64_t value); + + protected: // Registers + static const Addr CTRL_CNTFRQ = 0x000; + static const Addr CTRL_CNTNSAR = 0x004; + static const Addr CTRL_CNTTIDR = 0x008; + static const Addr CTRL_CNTACR_BASE = 0x040; + static const Addr CTRL_CNTVOFF_LO_BASE = 0x080; + static const Addr CTRL_CNTVOFF_HI_BASE = 0x084; + + static const Addr TIMER_CNTPCT_LO = 0x000; + static const Addr TIMER_CNTPCT_HI = 0x004; + static const Addr TIMER_CNTVCT_LO = 0x008; + static const Addr TIMER_CNTVCT_HI = 0x00C; + static const Addr TIMER_CNTFRQ = 0x010; + static const Addr TIMER_CNTEL0ACR = 0x014; + static const Addr TIMER_CNTVOFF_LO = 0x018; + static const Addr TIMER_CNTVOFF_HI = 0x01C; + static const Addr TIMER_CNTP_CVAL_LO = 0x020; + static const Addr TIMER_CNTP_CVAL_HI = 0x024; + static const Addr TIMER_CNTP_TVAL = 0x028; + static const Addr TIMER_CNTP_CTL = 0x02C; + static const Addr TIMER_CNTV_CVAL_LO = 0x030; + static const Addr TIMER_CNTV_CVAL_HI = 0x034; + static const Addr TIMER_CNTV_TVAL = 0x038; + static const Addr TIMER_CNTV_CTL = 0x03C; + + protected: // Params + const AddrRange ctrlRange; + const AddrRange timerRange; + const AddrRangeList addrRanges; + + protected: + /// System counter. + SystemCounter systemCounter; + ArchTimer physTimer; + ArchTimer virtTimer; +}; + #endif // __DEV_ARM_GENERIC_TIMER_HH__ -- 2.30.2