/*
- * Copyright (c) 2013, 2015, 2017-2018, 2019 ARM Limited
+ * Copyright (c) 2013, 2015, 2017-2018,2020 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
#include "dev/arm/generic_timer.hh"
+#include <cmath>
+
#include "arch/arm/system.hh"
+#include "arch/arm/utility.hh"
+#include "cpu/base.hh"
#include "debug/Timer.hh"
#include "dev/arm/base_gic.hh"
#include "mem/packet_access.hh"
#include "params/GenericTimer.hh"
+#include "params/GenericTimerFrame.hh"
#include "params/GenericTimerMem.hh"
-
-SystemCounter::SystemCounter(std::vector<uint32_t> &freqs)
- : _freqTable(freqs),
- _resetTick(0),
- _regCntkctl(0)
+#include "params/SystemCounter.hh"
+
+SystemCounter::SystemCounter(SystemCounterParams *const p)
+ : SimObject(p),
+ _enabled(true),
+ _value(0),
+ _increment(1),
+ _freqTable(p->freqs),
+ _activeFreqEntry(0),
+ _updateTick(0),
+ _freqUpdateEvent([this]{ freqUpdateCallback(); }, name()),
+ _nextFreqEntry(0)
{
fatal_if(_freqTable.empty(), "SystemCounter::SystemCounter: Base "
"frequency not provided\n");
"SystemCounter::SystemCounter: Architecture states a maximum of 1004 "
"frequency table entries, limit surpassed\n");
// Set the active frequency to be the base
- _freq = freqs.front();
+ _freq = _freqTable.front();
_period = (1.0 / _freq) * SimClock::Frequency;
}
void
-SystemCounter::setFreq(uint32_t freq)
+SystemCounter::validateCounterRef(SystemCounter *const sys_cnt)
+{
+ fatal_if(!sys_cnt, "SystemCounter::validateCounterRef: No valid system "
+ "counter, can't instantiate system timers\n");
+}
+
+void
+SystemCounter::enable()
{
- if (_freq != 0) {
- // Altering the frequency after boot shouldn't be done in practice.
- warn_once("The frequency of the system counter has already been set");
+ DPRINTF(Timer, "SystemCounter::enable: Counter enabled\n");
+ _enabled = true;
+ updateTick();
+}
+
+void
+SystemCounter::disable()
+{
+ DPRINTF(Timer, "SystemCounter::disable: Counter disabled\n");
+ updateValue();
+ _enabled = false;
+}
+
+uint64_t
+SystemCounter::value()
+{
+ if (_enabled)
+ updateValue();
+ return _value;
+}
+
+void
+SystemCounter::updateValue()
+{
+ uint64_t new_value =
+ _value + ((curTick() - _updateTick) / _period) * _increment;
+ if (new_value > _value) {
+ _value = new_value;
+ updateTick();
}
- _freq = freq;
- _period = (1.0 / freq) * SimClock::Frequency;
- _resetTick = curTick();
+}
+
+void
+SystemCounter::setValue(uint64_t new_value)
+{
+ if (_enabled)
+ warn("Explicit value set with counter enabled, UNKNOWNN result\n");
+ _value = new_value;
+ updateTick();
+ notifyListeners();
+}
+
+Tick
+SystemCounter::whenValue(uint64_t cur_val, uint64_t target_val) const
+{
+ Tick when = curTick();
+ if (target_val > cur_val) {
+ uint64_t num_cycles =
+ std::ceil((target_val - cur_val) / ((double) _increment));
+ // Take into account current cycle remaining ticks
+ Tick rem_ticks = _period - (curTick() % _period);
+ if (rem_ticks < _period) {
+ when += rem_ticks;
+ num_cycles -= 1;
+ }
+ when += num_cycles * _period;
+ }
+ return when;
+}
+
+Tick
+SystemCounter::whenValue(uint64_t target_val)
+{
+ return whenValue(value(), target_val);
+}
+
+void
+SystemCounter::updateTick()
+{
+ _updateTick = curTick() - (curTick() % _period);
+}
+
+void
+SystemCounter::freqUpdateSchedule(size_t new_freq_entry)
+{
+ if (new_freq_entry < _freqTable.size()) {
+ auto &new_freq = _freqTable[new_freq_entry];
+ if (new_freq != _freq) {
+ _nextFreqEntry = new_freq_entry;
+ // Wait until the value for which the lowest frequency increment
+ // is a exact divisor. This covers both high to low and low to
+ // high transitions
+ uint64_t new_incr = _freqTable[0] / new_freq;
+ uint64_t target_val = value();
+ target_val += target_val % std::max(_increment, new_incr);
+ reschedule(_freqUpdateEvent, whenValue(target_val), true);
+ }
+ }
+}
+
+void
+SystemCounter::freqUpdateCallback()
+{
+ DPRINTF(Timer, "SystemCounter::freqUpdateCallback: Changing counter "
+ "frequency\n");
+ if (_enabled)
+ updateValue();
+ _activeFreqEntry = _nextFreqEntry;
+ _freq = _freqTable[_activeFreqEntry];
+ _increment = _freqTable[0] / _freq;
+ _period = (1.0 / _freq) * SimClock::Frequency;
+ notifyListeners();
+}
+
+void
+SystemCounter::registerListener(SystemCounterListener *listener)
+{
+ _listeners.push_back(listener);
+}
+
+void
+SystemCounter::notifyListeners() const
+{
+ for (auto &listener : _listeners)
+ listener->notify();
}
void
SystemCounter::serialize(CheckpointOut &cp) const
{
- SERIALIZE_SCALAR(_regCntkctl);
- SERIALIZE_SCALAR(_regCnthctl);
+ DPRINTF(Timer, "SystemCounter::serialize: Serializing\n");
+ SERIALIZE_SCALAR(_enabled);
SERIALIZE_SCALAR(_freq);
- SERIALIZE_SCALAR(_resetTick);
+ SERIALIZE_SCALAR(_value);
+ SERIALIZE_SCALAR(_increment);
+ SERIALIZE_CONTAINER(_freqTable);
+ SERIALIZE_SCALAR(_activeFreqEntry);
+ SERIALIZE_SCALAR(_updateTick);
+ bool pending_freq_update = _freqUpdateEvent.scheduled();
+ SERIALIZE_SCALAR(pending_freq_update);
+ if (pending_freq_update) {
+ Tick when_freq_update = _freqUpdateEvent.when();
+ SERIALIZE_SCALAR(when_freq_update);
+ }
+ SERIALIZE_SCALAR(_nextFreqEntry);
}
void
SystemCounter::unserialize(CheckpointIn &cp)
{
- // We didn't handle CNTKCTL in this class before, assume it's zero
- // if it isn't present.
- if (!UNSERIALIZE_OPT_SCALAR(_regCntkctl))
- _regCntkctl = 0;
- if (!UNSERIALIZE_OPT_SCALAR(_regCnthctl))
- _regCnthctl = 0;
+ DPRINTF(Timer, "SystemCounter::unserialize: Unserializing\n");
+ UNSERIALIZE_SCALAR(_enabled);
UNSERIALIZE_SCALAR(_freq);
+ UNSERIALIZE_SCALAR(_value);
+ UNSERIALIZE_SCALAR(_increment);
+ UNSERIALIZE_CONTAINER(_freqTable);
+ UNSERIALIZE_SCALAR(_activeFreqEntry);
+ UNSERIALIZE_SCALAR(_updateTick);
+ bool pending_freq_update;
+ UNSERIALIZE_SCALAR(pending_freq_update);
+ if (pending_freq_update) {
+ Tick when_freq_update;
+ UNSERIALIZE_SCALAR(when_freq_update);
+ reschedule(_freqUpdateEvent, when_freq_update, true);
+ }
+ UNSERIALIZE_SCALAR(_nextFreqEntry);
+
_period = (1.0 / _freq) * SimClock::Frequency;
- UNSERIALIZE_SCALAR(_resetTick);
}
-
-
ArchTimer::ArchTimer(const std::string &name,
SimObject &parent,
SystemCounter &sysctr,
_control(0), _counterLimit(0), _offset(0),
_counterLimitReachedEvent([this]{ counterLimitReached(); }, name)
{
+ _systemCounter.registerListener(this);
}
void
ArchTimer::counterLimitReached()
{
- _control.istatus = 1;
-
if (!_control.enable)
return;
DPRINTF(Timer, "Counter limit reached\n");
+ _control.istatus = 1;
if (!_control.imask) {
if (scheduleEvents()) {
DPRINTF(Timer, "Causing interrupt\n");
} else {
_control.istatus = 0;
if (scheduleEvents()) {
- const auto period(_systemCounter.period());
_parent.schedule(_counterLimitReachedEvent,
- curTick() + (_counterLimit - value()) * period);
+ whenValue(_counterLimit));
}
}
}
void
ArchTimer::setControl(uint32_t val)
{
- ArchTimerCtrl new_ctl = val;
- if ((new_ctl.enable && !new_ctl.imask) &&
- !(_control.enable && !_control.imask)) {
- // Re-evalute the timer condition
- if (_counterLimit >= value()) {
- _control.istatus = 1;
-
- DPRINTF(Timer, "Causing interrupt in control\n");
- //_interrupt.send();
- }
- }
+ ArchTimerCtrl old_ctl = _control, new_ctl = val;
_control.enable = new_ctl.enable;
_control.imask = new_ctl.imask;
+ _control.istatus = old_ctl.istatus;
+ // Timer enabled
+ if (!old_ctl.enable && new_ctl.enable)
+ updateCounter();
+ // Timer disabled
+ else if (old_ctl.enable && !new_ctl.enable)
+ _control.istatus = 0;
}
void
return _systemCounter.value() - _offset;
}
+void
+ArchTimer::notify()
+{
+ updateCounter();
+}
+
void
ArchTimer::serialize(CheckpointOut &cp) const
{
updateCounter();
}
-GenericTimer::GenericTimer(GenericTimerParams *p)
- : ClockedObject(p),
- systemCounter(p->freqs),
+GenericTimer::GenericTimer(GenericTimerParams *const p)
+ : SimObject(p),
+ systemCounter(*p->counter),
system(*p->system)
{
- fatal_if(!p->system, "No system specified, can't instantiate timer.\n");
+ SystemCounter::validateCounterRef(p->counter);
+ fatal_if(!p->system, "GenericTimer::GenericTimer: No system specified, "
+ "can't instantiate architected timers\n");
system.setGenericTimer(this);
}
{
paramOut(cp, "cpu_count", timers.size());
- systemCounter.serializeSection(cp, "sys_counter");
-
for (int i = 0; i < timers.size(); ++i) {
const CoreTimers &core(*timers[i]);
-
- // This should really be phys_timerN, but we are stuck with
- // arch_timer for backwards compatibility.
- core.physNS.serializeSection(cp, csprintf("arch_timer%d", i));
- core.physS.serializeSection(cp, csprintf("phys_s_timer%d", i));
- core.virt.serializeSection(cp, csprintf("virt_timer%d", i));
- core.hyp.serializeSection(cp, csprintf("hyp_timer%d", i));
+ core.serializeSection(cp, csprintf("pe_implementation%d", i));
}
}
void
GenericTimer::unserialize(CheckpointIn &cp)
{
- systemCounter.unserializeSection(cp, "sys_counter");
-
// Try to unserialize the CPU count. Old versions of the timer
// model assumed a 8 CPUs, so we fall back to that if the field
// isn't present.
for (int i = 0; i < cpu_count; ++i) {
CoreTimers &core(getTimers(i));
- // This should really be phys_timerN, but we are stuck with
- // arch_timer for backwards compatibility.
- core.physNS.unserializeSection(cp, csprintf("arch_timer%d", i));
- core.physS.unserializeSection(cp, csprintf("phys_s_timer%d", i));
- core.virt.unserializeSection(cp, csprintf("virt_timer%d", i));
- core.hyp.unserializeSection(cp, csprintf("hyp_timer%d", i));
+ core.unserializeSection(cp, csprintf("pe_implementation%d", i));
}
}
-
GenericTimer::CoreTimers &
GenericTimer::getTimers(int cpu_id)
{
}
}
+void
+GenericTimer::handleStream(CoreTimers::EventStream *ev_stream,
+ ArchTimer *timer, RegVal old_cnt_ctl, RegVal cnt_ctl)
+{
+ uint64_t evnten = bits(cnt_ctl, 2);
+ uint64_t old_evnten = bits(old_cnt_ctl, 2);
+ uint8_t old_trans_to = ev_stream->transitionTo;
+ uint8_t old_trans_bit = ev_stream->transitionBit;
+ ev_stream->transitionTo = !bits(cnt_ctl, 3);
+ ev_stream->transitionBit = bits(cnt_ctl, 7, 4);
+ // Reschedule the Event Stream if enabled and any change in
+ // configuration
+ if (evnten && ((old_evnten != evnten) ||
+ (old_trans_to != ev_stream->transitionTo) ||
+ (old_trans_bit != ev_stream->transitionBit))) {
+
+ Tick when = timer->whenValue(
+ ev_stream->eventTargetValue(timer->value()));
+ reschedule(ev_stream->event, when, true);
+ } else if (old_evnten && !evnten) {
+ // Event Stream generation disabled
+ if (ev_stream->event.scheduled())
+ deschedule(ev_stream->event);
+ }
+}
void
GenericTimer::setMiscReg(int reg, unsigned cpu, RegVal val)
{
CoreTimers &core(getTimers(cpu));
+ ThreadContext *tc = system.getThreadContext(cpu);
switch (reg) {
case MISCREG_CNTFRQ:
case MISCREG_CNTFRQ_EL0:
- systemCounter.setFreq(val);
+ core.cntfrq = val;
+ warn_if(core.cntfrq != systemCounter.freq(), "CNTFRQ configured freq "
+ "does not match the system counter freq\n");
return;
-
case MISCREG_CNTKCTL:
case MISCREG_CNTKCTL_EL1:
- systemCounter.setKernelControl(val);
- return;
+ {
+ if (ELIsInHost(tc, currEL(tc))) {
+ tc->setMiscReg(MISCREG_CNTHCTL_EL2, val);
+ return;
+ }
+ RegVal old_cnt_ctl = core.cntkctl;
+ core.cntkctl = val;
+ ArchTimer *timer = &core.virt;
+ CoreTimers::EventStream *ev_stream = &core.virtEvStream;
+
+ handleStream(ev_stream, timer, old_cnt_ctl, val);
+ return;
+ }
case MISCREG_CNTHCTL:
case MISCREG_CNTHCTL_EL2:
- systemCounter.setHypControl(val);
- return;
+ {
+ RegVal old_cnt_ctl = core.cnthctl;
+ core.cnthctl = val;
+ ArchTimer *timer = &core.physNS;
+ CoreTimers::EventStream *ev_stream = &core.physEvStream;
+
+ handleStream(ev_stream, timer, old_cnt_ctl, val);
+ return;
+ }
// Physical timer (NS)
case MISCREG_CNTP_CVAL_NS:
case MISCREG_CNTP_CVAL_EL0:
switch (reg) {
case MISCREG_CNTFRQ:
case MISCREG_CNTFRQ_EL0:
- return systemCounter.freq();
-
+ return core.cntfrq;
case MISCREG_CNTKCTL:
case MISCREG_CNTKCTL_EL1:
- return systemCounter.getKernelControl();
-
+ return core.cntkctl & 0x00000000ffffffff;
case MISCREG_CNTHCTL:
case MISCREG_CNTHCTL_EL2:
- return systemCounter.getHypControl();
-
+ return core.cnthctl & 0x00000000ffffffff;
// Physical timer
case MISCREG_CNTP_CVAL_NS:
case MISCREG_CNTP_CVAL_EL0:
}
}
+void
+GenericTimer::CoreTimers::physEventStreamCallback()
+{
+ eventStreamCallback();
+ schedNextEvent(physEvStream, physNS);
+}
+
+void
+GenericTimer::CoreTimers::virtEventStreamCallback()
+{
+ eventStreamCallback();
+ schedNextEvent(virtEvStream, virt);
+}
+
+void
+GenericTimer::CoreTimers::eventStreamCallback() const
+{
+ sendEvent(threadContext);
+ threadContext->getCpuPtr()->wakeup(threadContext->threadId());
+}
+
+void
+GenericTimer::CoreTimers::schedNextEvent(EventStream &ev_stream,
+ ArchTimer &timer)
+{
+ parent.reschedule(ev_stream.event, timer.whenValue(
+ ev_stream.eventTargetValue(timer.value())), true);
+}
+
+void
+GenericTimer::CoreTimers::notify()
+{
+ schedNextEvent(virtEvStream, virt);
+ schedNextEvent(physEvStream, physNS);
+}
+
+void
+GenericTimer::CoreTimers::serialize(CheckpointOut &cp) const
+{
+ physS.serializeSection(cp, "phys_s_timer");
+ physNS.serializeSection(cp, "phys_ns_timer");
+ virt.serializeSection(cp, "virt_timer");
+ hyp.serializeSection(cp, "hyp_timer");
+
+ SERIALIZE_SCALAR(cntfrq);
+ SERIALIZE_SCALAR(cntkctl);
+ SERIALIZE_SCALAR(cnthctl);
+
+ bool ev_scheduled = physEvStream.event.scheduled();
+ SERIALIZE_SCALAR(ev_scheduled);
+ if (ev_scheduled)
+ SERIALIZE_SCALAR(physEvStream.event.when());
+ SERIALIZE_SCALAR(physEvStream.transitionTo);
+ SERIALIZE_SCALAR(physEvStream.transitionBit);
+ ev_scheduled = virtEvStream.event.scheduled();
+ SERIALIZE_SCALAR(ev_scheduled);
+ if (ev_scheduled)
+ SERIALIZE_SCALAR(virtEvStream.event.when());
+ SERIALIZE_SCALAR(virtEvStream.transitionTo);
+ SERIALIZE_SCALAR(virtEvStream.transitionBit);
+}
+
+void
+GenericTimer::CoreTimers::unserialize(CheckpointIn &cp)
+{
+ physS.unserializeSection(cp, "phys_s_timer");
+ physNS.unserializeSection(cp, "phys_ns_timer");
+ virt.unserializeSection(cp, "virt_timer");
+ hyp.unserializeSection(cp, "hyp_timer");
+
+ UNSERIALIZE_SCALAR(cntfrq);
+ UNSERIALIZE_SCALAR(cntkctl);
+ UNSERIALIZE_SCALAR(cnthctl);
+
+ bool ev_scheduled;
+ Tick when;
+ UNSERIALIZE_SCALAR(ev_scheduled);
+ if (ev_scheduled) {
+ UNSERIALIZE_SCALAR(when);
+ parent.reschedule(physEvStream.event, when, true);
+ }
+ UNSERIALIZE_SCALAR(physEvStream.transitionTo);
+ UNSERIALIZE_SCALAR(physEvStream.transitionBit);
+ UNSERIALIZE_SCALAR(ev_scheduled);
+ if (ev_scheduled) {
+ UNSERIALIZE_SCALAR(when);
+ parent.reschedule(virtEvStream.event, when, true);
+ }
+ UNSERIALIZE_SCALAR(virtEvStream.transitionTo);
+ UNSERIALIZE_SCALAR(virtEvStream.transitionBit);
+}
void
GenericTimerISA::setMiscReg(int reg, RegVal val)
return value;
}
-GenericTimerMem::GenericTimerMem(GenericTimerMemParams *p)
+GenericTimerFrame::GenericTimerFrame(GenericTimerFrameParams *const p)
: PioDevice(p),
- ctrlRange(RangeSize(p->base, sys->getPageBytes())),
- timerRange(RangeSize(p->base + sys->getPageBytes(),
- sys->getPageBytes())),
- addrRanges{ctrlRange, timerRange},
- systemCounter(p->freqs),
- physTimer(csprintf("%s.phys_timer0", name()),
+ timerRange(RangeSize(p->cnt_base, sys->getPageBytes())),
+ addrRanges({timerRange}),
+ systemCounter(*p->counter),
+ physTimer(csprintf("%s.phys_timer", name()),
+ *this, systemCounter, p->int_phys->get()),
+ virtTimer(csprintf("%s.virt_timer", name()),
*this, systemCounter,
- p->int_phys->get()),
- virtTimer(csprintf("%s.virt_timer0", name()),
- *this, systemCounter,
- p->int_virt->get())
+ p->int_virt->get()),
+ accessBits(0x3f),
+ system(*dynamic_cast<ArmSystem *>(sys))
+{
+ SystemCounter::validateCounterRef(p->counter);
+ // Expose optional CNTEL0Base register frame
+ if (p->cnt_el0_base != MaxAddr) {
+ timerEl0Range = RangeSize(p->cnt_el0_base, sys->getPageBytes());
+ accessBitsEl0 = 0x303;
+ addrRanges.push_back(timerEl0Range);
+ }
+ for (auto &range : addrRanges)
+ GenericTimerMem::validateFrameRange(range);
+}
+
+void
+GenericTimerFrame::serialize(CheckpointOut &cp) const
+{
+ physTimer.serializeSection(cp, "phys_timer");
+ virtTimer.serializeSection(cp, "virt_timer");
+ SERIALIZE_SCALAR(accessBits);
+ if (hasEl0View())
+ SERIALIZE_SCALAR(accessBitsEl0);
+ SERIALIZE_SCALAR(nonSecureAccess);
+}
+
+void
+GenericTimerFrame::unserialize(CheckpointIn &cp)
+{
+ physTimer.unserializeSection(cp, "phys_timer");
+ virtTimer.unserializeSection(cp, "virt_timer");
+ UNSERIALIZE_SCALAR(accessBits);
+ if (hasEl0View())
+ UNSERIALIZE_SCALAR(accessBitsEl0);
+ UNSERIALIZE_SCALAR(nonSecureAccess);
+}
+
+uint64_t
+GenericTimerFrame::getVirtOffset() const
{
+ return virtTimer.offset();
}
void
-GenericTimerMem::serialize(CheckpointOut &cp) const
+GenericTimerFrame::setVirtOffset(uint64_t new_offset)
{
- paramOut(cp, "timer_count", 1);
+ virtTimer.setOffset(new_offset);
+}
- systemCounter.serializeSection(cp, "sys_counter");
+bool
+GenericTimerFrame::hasEl0View() const
+{
+ return timerEl0Range.valid();
+}
+
+uint8_t
+GenericTimerFrame::getAccessBits() const
+{
+ return accessBits;
+}
+
+void
+GenericTimerFrame::setAccessBits(uint8_t data)
+{
+ accessBits = data & 0x3f;
+}
- physTimer.serializeSection(cp, "phys_timer0");
- virtTimer.serializeSection(cp, "virt_timer0");
+bool
+GenericTimerFrame::hasNonSecureAccess() const
+{
+ return nonSecureAccess;
}
void
-GenericTimerMem::unserialize(CheckpointIn &cp)
+GenericTimerFrame::setNonSecureAccess()
{
- systemCounter.unserializeSection(cp, "sys_counter");
+ nonSecureAccess = true;
+}
- 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");
+bool
+GenericTimerFrame::hasReadableVoff() const
+{
+ return accessBits.rvoff;
+}
- physTimer.unserializeSection(cp, "phys_timer0");
- virtTimer.unserializeSection(cp, "virt_timer0");
+AddrRangeList
+GenericTimerFrame::getAddrRanges() const
+{
+ return addrRanges;
}
Tick
-GenericTimerMem::read(PacketPtr pkt)
+GenericTimerFrame::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);
+ const Addr addr = pkt->getAddr();
+ const size_t size = pkt->getSize();
+ const bool is_sec = pkt->isSecure();
+ panic_if(size != 4 && size != 8,
+ "GenericTimerFrame::read: Invalid size %i\n", size);
+
+ bool to_el0 = false;
+ uint64_t resp = 0;
+ Addr offset = 0;
+ if (timerRange.contains(addr)) {
+ offset = addr - timerRange.start();
+ } else if (hasEl0View() && timerEl0Range.contains(addr)) {
+ offset = addr - timerEl0Range.start();
+ to_el0 = true;
} else {
- panic("Invalid address: 0x%x\n", addr);
+ panic("GenericTimerFrame::read: Invalid address: 0x%x\n", addr);
}
- DPRINTF(Timer, "Read 0x%x <- 0x%x(%i)\n", value, addr, size);
+ resp = timerRead(offset, size, is_sec, to_el0);
- if (size == 8) {
- pkt->setLE<uint64_t>(value);
- } else if (size == 4) {
- pkt->setLE<uint32_t>(value);
- } else {
- panic("Unexpected access size: %i\n", size);
- }
+ DPRINTF(Timer, "GenericTimerFrame::read: 0x%x<-0x%x(%i) [S = %u]\n", resp,
+ addr, size, is_sec);
+ pkt->setUintX(resp, LittleEndianByteOrder);
+ pkt->makeResponse();
return 0;
}
Tick
-GenericTimerMem::write(PacketPtr pkt)
+GenericTimerFrame::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->getLE<uint64_t>() : pkt->getLE<uint32_t>());
-
- 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);
+ const Addr addr = pkt->getAddr();
+ const size_t size = pkt->getSize();
+ const bool is_sec = pkt->isSecure();
+ panic_if(size != 4 && size != 8,
+ "GenericTimerFrame::write: Invalid size %i\n", size);
+
+ bool to_el0 = false;
+ const uint64_t data = pkt->getUintX(LittleEndianByteOrder);
+ Addr offset = 0;
+ if (timerRange.contains(addr)) {
+ offset = addr - timerRange.start();
+ } else if (hasEl0View() && timerEl0Range.contains(addr)) {
+ offset = addr - timerEl0Range.start();
+ to_el0 = true;
} else {
- panic("Invalid address: 0x%x\n", addr);
+ panic("GenericTimerFrame::write: Invalid address: 0x%x\n", addr);
}
+ timerWrite(offset, size, data, is_sec, to_el0);
+
+ DPRINTF(Timer, "GenericTimerFrame::write: 0x%x->0x%x(%i) [S = %u]\n", data,
+ addr, size, is_sec);
+
pkt->makeResponse();
return 0;
}
uint64_t
-GenericTimerMem::ctrlRead(Addr addr, size_t size) const
+GenericTimerFrame::timerRead(Addr addr, size_t size, bool is_sec,
+ bool to_el0) const
{
- if (size == 4) {
- switch (addr) {
- case CTRL_CNTFRQ:
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec) &&
+ !nonSecureAccess)
+ return 0;
+
+ switch (addr) {
+ case TIMER_CNTPCT_LO:
+ if (!accessBits.rpct || (to_el0 && !accessBitsEl0.pcten))
+ return 0;
+ else
+ return physTimer.value();
+
+ case TIMER_CNTPCT_HI:
+ if (!accessBits.rpct || (to_el0 && !accessBitsEl0.pcten))
+ return 0;
+ else
+ return physTimer.value() >> 32;
+
+ case TIMER_CNTFRQ:
+ if ((!accessBits.rfrq) ||
+ (to_el0 && (!accessBitsEl0.pcten && !accessBitsEl0.vcten)))
+ return 0;
+ else
return systemCounter.freq();
- case CTRL_CNTTIDR:
- return 0x3; // Frame 0 implemented with virtual timers
+ case TIMER_CNTEL0ACR:
+ if (!hasEl0View() || to_el0)
+ return 0;
+ else
+ return accessBitsEl0;
- case CTRL_CNTNSAR:
- case CTRL_CNTACR_BASE:
- warn("Reading from unimplemented control register (0x%x)\n", addr);
+ case TIMER_CNTP_CVAL_LO:
+ if (!accessBits.rwpt || (to_el0 && !accessBitsEl0.pten))
return 0;
+ else
+ return physTimer.compareValue();
- case CTRL_CNTVOFF_LO_BASE:
- return virtTimer.offset();
+ case TIMER_CNTP_CVAL_HI:
+ if (!accessBits.rwpt || (to_el0 && !accessBitsEl0.pten))
+ return 0;
+ else
+ return physTimer.compareValue() >> 32;
- case CTRL_CNTVOFF_HI_BASE:
- return virtTimer.offset() >> 32;
+ case TIMER_CNTP_TVAL:
+ if (!accessBits.rwpt || (to_el0 && !accessBitsEl0.pten))
+ return 0;
+ else
+ return physTimer.timerValue();
+ case TIMER_CNTP_CTL:
+ if (!accessBits.rwpt || (to_el0 && !accessBitsEl0.pten))
+ return 0;
+ else
+ return physTimer.control();
- default:
- warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size);
+ case TIMER_CNTVCT_LO:
+ if (!accessBits.rvct || (to_el0 && !accessBitsEl0.vcten))
return 0;
- }
- } else if (size == 8) {
- switch (addr) {
- case CTRL_CNTVOFF_LO_BASE:
+ else
+ return virtTimer.value();
+
+ case TIMER_CNTVCT_HI:
+ if (!accessBits.rvct || (to_el0 && !accessBitsEl0.vcten))
+ return 0;
+ else
+ return virtTimer.value() >> 32;
+
+ case TIMER_CNTVOFF_LO:
+ if (!accessBits.rvoff || (to_el0))
+ return 0;
+ else
return virtTimer.offset();
- default:
- warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size);
+ case TIMER_CNTVOFF_HI:
+ if (!accessBits.rvoff || (to_el0))
return 0;
- }
- } else {
- panic("Invalid access size: %i\n", size);
- }
-}
+ else
+ return virtTimer.offset() >> 32;
-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 TIMER_CNTV_CVAL_LO:
+ if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
+ return 0;
+ else
+ return virtTimer.compareValue();
- case CTRL_CNTVOFF_LO_BASE:
- virtTimer.setOffset(
- insertBits(virtTimer.offset(), 31, 0, value));
- return;
+ case TIMER_CNTV_CVAL_HI:
+ if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
+ return 0;
+ else
+ return virtTimer.compareValue() >> 32;
- case CTRL_CNTVOFF_HI_BASE:
- virtTimer.setOffset(
- insertBits(virtTimer.offset(), 63, 32, value));
- return;
+ case TIMER_CNTV_TVAL:
+ if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
+ return 0;
+ else
+ return virtTimer.timerValue();
- 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;
+ case TIMER_CNTV_CTL:
+ if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
+ return 0;
+ else
+ return virtTimer.control();
- default:
- warn("Ignoring write to unexpected address (0x%x:%i)\n",
- addr, size);
- return;
- }
- } else {
- panic("Invalid access size: %i\n", size);
+ default:
+ warn("GenericTimerFrame::timerRead: Unexpected address (0x%x:%i), "
+ "assuming RAZ\n", addr, size);
+ return 0;
}
}
-uint64_t
-GenericTimerMem::timerRead(Addr addr, size_t size) const
+void
+GenericTimerFrame::timerWrite(Addr addr, size_t size, uint64_t data,
+ bool is_sec, bool to_el0)
{
- if (size == 4) {
- switch (addr) {
- case TIMER_CNTPCT_LO:
- return physTimer.value();
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec) &&
+ !nonSecureAccess)
+ return;
- case TIMER_CNTPCT_HI:
- return physTimer.value() >> 32;
+ switch (addr) {
+ case TIMER_CNTPCT_LO ... TIMER_CNTPCT_HI:
+ warn("GenericTimerFrame::timerWrite: RO reg (0x%x) [CNTPCT]\n",
+ addr);
+ return;
- case TIMER_CNTVCT_LO:
- return virtTimer.value();
+ case TIMER_CNTFRQ:
+ warn("GenericTimerFrame::timerWrite: RO reg (0x%x) [CNTFRQ]\n",
+ addr);
+ return;
- case TIMER_CNTVCT_HI:
- return virtTimer.value() >> 32;
+ case TIMER_CNTEL0ACR:
+ if (!hasEl0View() || to_el0)
+ return;
- case TIMER_CNTFRQ:
- return systemCounter.freq();
+ insertBits(accessBitsEl0, 9, 8, data);
+ insertBits(accessBitsEl0, 1, 0, data);
+ return;
- case TIMER_CNTEL0ACR:
- warn("Read from unimplemented timer register (0x%x)\n", addr);
- return 0;
+ case TIMER_CNTP_CVAL_LO:
+ if ((!accessBits.rwpt) || (to_el0 && !accessBitsEl0.pten))
+ return;
+ data = size == 4 ? insertBits(physTimer.compareValue(),
+ 31, 0, data) : data;
+ physTimer.setCompareValue(data);
+ return;
- case CTRL_CNTVOFF_LO_BASE:
- return virtTimer.offset();
+ case TIMER_CNTP_CVAL_HI:
+ if ((!accessBits.rwpt) || (to_el0 && !accessBitsEl0.pten))
+ return;
+ data = insertBits(physTimer.compareValue(), 63, 32, data);
+ physTimer.setCompareValue(data);
+ return;
- case CTRL_CNTVOFF_HI_BASE:
- return virtTimer.offset() >> 32;
+ case TIMER_CNTP_TVAL:
+ if ((!accessBits.rwpt) || (to_el0 && !accessBitsEl0.pten))
+ return;
+ physTimer.setTimerValue(data);
+ return;
- case TIMER_CNTP_CVAL_LO:
- return physTimer.compareValue();
+ case TIMER_CNTP_CTL:
+ if ((!accessBits.rwpt) || (to_el0 && !accessBitsEl0.pten))
+ return;
+ physTimer.setControl(data);
+ return;
- case TIMER_CNTP_CVAL_HI:
- return physTimer.compareValue() >> 32;
+ case TIMER_CNTVCT_LO ... TIMER_CNTVCT_HI:
+ warn("GenericTimerFrame::timerWrite: RO reg (0x%x) [CNTVCT]\n",
+ addr);
+ return;
+ case TIMER_CNTVOFF_LO ... TIMER_CNTVOFF_HI:
+ warn("GenericTimerFrame::timerWrite: RO reg (0x%x) [CNTVOFF]\n",
+ addr);
+ return;
- case TIMER_CNTP_TVAL:
- return physTimer.timerValue();
+ case TIMER_CNTV_CVAL_LO:
+ if ((!accessBits.rwvt) || (to_el0 && !accessBitsEl0.vten))
+ return;
+ data = size == 4 ? insertBits(virtTimer.compareValue(),
+ 31, 0, data) : data;
+ virtTimer.setCompareValue(data);
+ return;
- case TIMER_CNTP_CTL:
- return physTimer.control();
+ case TIMER_CNTV_CVAL_HI:
+ if ((!accessBits.rwvt) || (to_el0 && !accessBitsEl0.vten))
+ return;
+ data = insertBits(virtTimer.compareValue(), 63, 32, data);
+ virtTimer.setCompareValue(data);
+ return;
- case TIMER_CNTV_CVAL_LO:
- return virtTimer.compareValue();
+ case TIMER_CNTV_TVAL:
+ if ((!accessBits.rwvt) || (to_el0 && !accessBitsEl0.vten))
+ return;
+ virtTimer.setTimerValue(data);
+ return;
- case TIMER_CNTV_CVAL_HI:
- return virtTimer.compareValue() >> 32;
+ case TIMER_CNTV_CTL:
+ if ((!accessBits.rwvt) || (to_el0 && !accessBitsEl0.vten))
+ return;
+ virtTimer.setControl(data);
+ return;
- case TIMER_CNTV_TVAL:
- return virtTimer.timerValue();
+ default:
+ warn("GenericTimerFrame::timerWrite: Unexpected address (0x%x:%i), "
+ "assuming WI\n", addr, size);
+ }
+}
- case TIMER_CNTV_CTL:
- return virtTimer.control();
+GenericTimerMem::GenericTimerMem(GenericTimerMemParams *const p)
+ : PioDevice(p),
+ counterCtrlRange(RangeSize(p->cnt_control_base, sys->getPageBytes())),
+ counterStatusRange(RangeSize(p->cnt_read_base, sys->getPageBytes())),
+ timerCtrlRange(RangeSize(p->cnt_ctl_base, sys->getPageBytes())),
+ cnttidr(0x0),
+ addrRanges{counterCtrlRange, counterStatusRange, timerCtrlRange},
+ systemCounter(*p->counter),
+ frames(p->frames),
+ system(*dynamic_cast<ArmSystem *>(sys))
+{
+ SystemCounter::validateCounterRef(p->counter);
+ for (auto &range : addrRanges)
+ GenericTimerMem::validateFrameRange(range);
+ fatal_if(frames.size() > MAX_TIMER_FRAMES,
+ "GenericTimerMem::GenericTimerMem: Architecture states a maximum of "
+ "8 memory-mapped timer frames, limit surpassed\n");
+ // Initialize CNTTIDR with each frame's features
+ for (int i = 0; i < frames.size(); i++) {
+ uint32_t features = 0x1;
+ features |= 0x2;
+ if (frames[i]->hasEl0View())
+ features |= 0x4;
+ features <<= i * 4;
+ replaceBits(cnttidr, (i + 1) * 4 - 1, i * 4, features);
+ }
+}
- 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();
+void
+GenericTimerMem::validateFrameRange(const AddrRange &range)
+{
+ fatal_if(range.start() % TheISA::PageBytes,
+ "GenericTimerMem::validateFrameRange: Architecture states each "
+ "register frame should be in a separate memory page, specified "
+ "range base address [0x%x] is not compliant\n");
+}
- case TIMER_CNTVCT_LO:
- return virtTimer.value();
+bool
+GenericTimerMem::validateAccessPerm(ArmSystem &sys, bool is_sec)
+{
+ return !sys.haveSecurity() || is_sec;
+}
- case CTRL_CNTVOFF_LO_BASE:
- return virtTimer.offset();
+AddrRangeList
+GenericTimerMem::getAddrRanges() const
+{
+ return addrRanges;
+}
- case TIMER_CNTP_CVAL_LO:
- return physTimer.compareValue();
+Tick
+GenericTimerMem::read(PacketPtr pkt)
+{
+ const Addr addr = pkt->getAddr();
+ const size_t size = pkt->getSize();
+ const bool is_sec = pkt->isSecure();
+ panic_if(size != 4 && size != 8,
+ "GenericTimerMem::read: Invalid size %i\n", size);
+
+ uint64_t resp = 0;
+ if (counterCtrlRange.contains(addr))
+ resp = counterCtrlRead(addr - counterCtrlRange.start(), size, is_sec);
+ else if (counterStatusRange.contains(addr))
+ resp = counterStatusRead(addr - counterStatusRange.start(), size);
+ else if (timerCtrlRange.contains(addr))
+ resp = timerCtrlRead(addr - timerCtrlRange.start(), size, is_sec);
+ else
+ panic("GenericTimerMem::read: Invalid address: 0x%x\n", addr);
+
+ DPRINTF(Timer, "GenericTimerMem::read: 0x%x<-0x%x(%i) [S = %u]\n", resp,
+ addr, size, is_sec);
+
+ pkt->setUintX(resp, LittleEndianByteOrder);
+ pkt->makeResponse();
+ return 0;
+}
- case TIMER_CNTV_CVAL_LO:
- return virtTimer.compareValue();
+Tick
+GenericTimerMem::write(PacketPtr pkt)
+{
+ const Addr addr = pkt->getAddr();
+ const size_t size = pkt->getSize();
+ const bool is_sec = pkt->isSecure();
+ panic_if(size != 4 && size != 8,
+ "GenericTimerMem::write: Invalid size %i\n", size);
+
+ const uint64_t data = pkt->getUintX(LittleEndianByteOrder);
+ if (counterCtrlRange.contains(addr))
+ counterCtrlWrite(addr - counterCtrlRange.start(), size, data, is_sec);
+ else if (counterStatusRange.contains(addr))
+ counterStatusWrite(addr - counterStatusRange.start(), size, data);
+ else if (timerCtrlRange.contains(addr))
+ timerCtrlWrite(addr - timerCtrlRange.start(), size, data, is_sec);
+ else
+ panic("GenericTimerMem::write: Invalid address: 0x%x\n", addr);
+
+ DPRINTF(Timer, "GenericTimerMem::write: 0x%x->0x%x(%i) [S = %u]\n", data,
+ addr, size, is_sec);
- default:
- warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size);
- return 0;
+ pkt->makeResponse();
+ return 0;
+}
+
+uint64_t
+GenericTimerMem::counterCtrlRead(Addr addr, size_t size, bool is_sec) const
+{
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec))
+ return 0;
+ switch (addr) {
+ case COUNTER_CTRL_CNTCR:
+ {
+ CNTCR cntcr = 0;
+ cntcr.en = systemCounter.enabled();
+ cntcr.fcreq = systemCounter.activeFreqEntry();
+ return cntcr;
+ }
+ case COUNTER_CTRL_CNTSR:
+ {
+ CNTSR cntsr = 0;
+ cntsr.fcack = systemCounter.activeFreqEntry();
+ return cntsr;
+ }
+ case COUNTER_CTRL_CNTCV_LO: return systemCounter.value();
+ case COUNTER_CTRL_CNTCV_HI: return systemCounter.value() >> 32;
+ case COUNTER_CTRL_CNTSCR: return 0;
+ case COUNTER_CTRL_CNTID: return 0;
+ default:
+ {
+ auto &freq_table = systemCounter.freqTable();
+ for (int i = 0; i < (freq_table.size() - 1); i++) {
+ Addr offset = COUNTER_CTRL_CNTFID + (i * 0x4);
+ if (addr == offset)
+ return freq_table[i];
}
- } else {
- panic("Invalid access size: %i\n", size);
+ warn("GenericTimerMem::counterCtrlRead: Unexpected address "
+ "(0x%x:%i), assuming RAZ\n", addr, size);
+ return 0;
+ }
}
}
void
-GenericTimerMem::timerWrite(Addr addr, size_t size, uint64_t value)
+GenericTimerMem::counterCtrlWrite(Addr addr, size_t size, uint64_t data,
+ bool is_sec)
{
- if (size == 4) {
- switch (addr) {
- case TIMER_CNTEL0ACR:
- warn("Unimplemented timer register (0x%x)\n", addr);
- return;
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec))
+ return;
- case TIMER_CNTP_CVAL_LO:
- physTimer.setCompareValue(
- insertBits(physTimer.compareValue(), 31, 0, value));
- return;
+ switch (addr) {
+ case COUNTER_CTRL_CNTCR:
+ {
+ CNTCR val = data;
+ if (!systemCounter.enabled() && val.en)
+ systemCounter.enable();
+ else if (systemCounter.enabled() && !val.en)
+ systemCounter.disable();
+
+ if (val.hdbg)
+ warn("GenericTimerMem::counterCtrlWrite: Halt-on-debug is not "
+ "supported\n");
+ if (val.scen)
+ warn("GenericTimerMem::counterCtrlWrite: Counter Scaling is not "
+ "supported\n");
+ if (val.fcreq != systemCounter.activeFreqEntry())
+ systemCounter.freqUpdateSchedule(val.fcreq);
+ return;
+ }
- case TIMER_CNTP_CVAL_HI:
- physTimer.setCompareValue(
- insertBits(physTimer.compareValue(), 63, 32, value));
- return;
+ case COUNTER_CTRL_CNTSR:
+ warn("GenericTimerMem::counterCtrlWrite: RO reg (0x%x) [CNTSR]\n",
+ addr);
+ return;
- case TIMER_CNTP_TVAL:
- physTimer.setTimerValue(value);
- return;
+ case COUNTER_CTRL_CNTCV_LO:
+ data = size == 4 ? insertBits(systemCounter.value(), 31, 0, data)
+ : data;
+ systemCounter.setValue(data);
+ return;
- case TIMER_CNTP_CTL:
- physTimer.setControl(value);
- return;
+ case COUNTER_CTRL_CNTCV_HI:
+ data = insertBits(systemCounter.value(), 63, 32, data);
+ systemCounter.setValue(data);
+ return;
- case TIMER_CNTV_CVAL_LO:
- virtTimer.setCompareValue(
- insertBits(virtTimer.compareValue(), 31, 0, value));
- return;
+ case COUNTER_CTRL_CNTSCR:
+ return;
- case TIMER_CNTV_CVAL_HI:
- virtTimer.setCompareValue(
- insertBits(virtTimer.compareValue(), 63, 32, value));
- return;
+ case COUNTER_CTRL_CNTID:
+ warn("GenericTimerMem::counterCtrlWrite: RO reg (0x%x) [CNTID]\n",
+ addr);
+ return;
- case TIMER_CNTV_TVAL:
- virtTimer.setTimerValue(value);
- return;
+ default:
+ {
+ auto &freq_table = systemCounter.freqTable();
+ for (int i = 0; i < (freq_table.size() - 1); i++) {
+ Addr offset = COUNTER_CTRL_CNTFID + (i * 0x4);
+ if (addr == offset) {
+ freq_table[i] = data;
+ // This is changing the currently selected frequency
+ if (i == systemCounter.activeFreqEntry()) {
+ // We've changed the frequency in the table entry,
+ // however the counter will still work with the
+ // current one until transition is completed
+ systemCounter.freqUpdateSchedule(i);
+ }
+ return;
+ }
+ }
+ warn("GenericTimerMem::counterCtrlWrite: Unexpected address "
+ "(0x%x:%i), assuming WI\n", addr, size);
+ }
+ }
+}
- case TIMER_CNTV_CTL:
- virtTimer.setControl(value);
- return;
+uint64_t
+GenericTimerMem::counterStatusRead(Addr addr, size_t size) const
+{
+ switch (addr) {
+ case COUNTER_STATUS_CNTCV_LO: return systemCounter.value();
+ case COUNTER_STATUS_CNTCV_HI: return systemCounter.value() >> 32;
+ default:
+ warn("GenericTimerMem::counterStatusRead: Unexpected address "
+ "(0x%x:%i), assuming RAZ\n", addr, size);
+ return 0;
+ }
+}
- 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);
+void
+GenericTimerMem::counterStatusWrite(Addr addr, size_t size, uint64_t data)
+{
+ switch (addr) {
+ case COUNTER_STATUS_CNTCV_LO ... COUNTER_STATUS_CNTCV_HI:
+ warn("GenericTimerMem::counterStatusWrite: RO reg (0x%x) [CNTCV]\n",
+ addr);
+ return;
+ default:
+ warn("GenericTimerMem::counterStatusWrite: Unexpected address "
+ "(0x%x:%i), assuming WI\n", addr, size);
+ }
+}
- case TIMER_CNTV_CVAL_LO:
- return virtTimer.setCompareValue(value);
+uint64_t
+GenericTimerMem::timerCtrlRead(Addr addr, size_t size, bool is_sec) const
+{
+ switch (addr) {
+ case TIMER_CTRL_CNTFRQ:
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec)) return 0;
+ return systemCounter.freq();
+ case TIMER_CTRL_CNTNSAR:
+ {
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec)) return 0;
+ uint32_t cntnsar = 0x0;
+ for (int i = 0; i < frames.size(); i++) {
+ if (frames[i]->hasNonSecureAccess())
+ cntnsar |= 0x1 << i;
+ }
+ return cntnsar;
+ }
+ case TIMER_CTRL_CNTTIDR: return cnttidr;
+ default:
+ for (int i = 0; i < frames.size(); i++) {
+ Addr cntacr_off = TIMER_CTRL_CNTACR + (i * 0x4);
+ Addr cntvoff_lo_off = TIMER_CTRL_CNTVOFF_LO + (i * 0x4);
+ Addr cntvoff_hi_off = TIMER_CTRL_CNTVOFF_HI + (i * 0x4);
+ // CNTNSAR.NS determines if CNTACR/CNTVOFF are accessible from
+ // normal world
+ bool hit = addr == cntacr_off || addr == cntvoff_lo_off ||
+ addr == cntvoff_hi_off;
+ bool has_access =
+ GenericTimerMem::validateAccessPerm(system, is_sec) ||
+ frames[i]->hasNonSecureAccess();
+ if (hit && !has_access) return 0;
+ if (addr == cntacr_off)
+ return frames[i]->getAccessBits();
+ if (addr == cntvoff_lo_off || addr == cntvoff_hi_off) {
+ return addr == cntvoff_lo_off ? frames[i]->getVirtOffset()
+ : frames[i]->getVirtOffset() >> 32;
+ }
+ }
+ warn("GenericTimerMem::timerCtrlRead: Unexpected address (0x%x:%i), "
+ "assuming RAZ\n", addr, size);
+ return 0;
+ }
+}
- default:
- warn("Unexpected address (0x%x:%i), ignoring write\n", addr, size);
- return;
+void
+GenericTimerMem::timerCtrlWrite(Addr addr, size_t size, uint64_t data,
+ bool is_sec)
+{
+ switch (addr) {
+ case TIMER_CTRL_CNTFRQ:
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec)) return;
+ warn_if(data != systemCounter.freq(),
+ "GenericTimerMem::timerCtrlWrite: CNTFRQ configured freq "
+ "does not match the counter freq, ignoring\n");
+ return;
+ case TIMER_CTRL_CNTNSAR:
+ if (!GenericTimerMem::validateAccessPerm(system, is_sec)) return;
+ for (int i = 0; i < frames.size(); i++) {
+ // Check if the CNTNSAR.NS bit is set for this frame
+ if (data & (0x1 << i))
+ frames[i]->setNonSecureAccess();
}
- } else {
- panic("Invalid access size: %i\n", size);
+ return;
+ case TIMER_CTRL_CNTTIDR:
+ warn("GenericTimerMem::timerCtrlWrite: RO reg (0x%x) [CNTTIDR]\n",
+ addr);
+ return;
+ default:
+ for (int i = 0; i < frames.size(); i++) {
+ Addr cntacr_off = TIMER_CTRL_CNTACR + (i * 0x4);
+ Addr cntvoff_lo_off = TIMER_CTRL_CNTVOFF_LO + (i * 0x4);
+ Addr cntvoff_hi_off = TIMER_CTRL_CNTVOFF_HI + (i * 0x4);
+ // CNTNSAR.NS determines if CNTACR/CNTVOFF are accessible from
+ // normal world
+ bool hit = addr == cntacr_off || addr == cntvoff_lo_off ||
+ addr == cntvoff_hi_off;
+ bool has_access =
+ GenericTimerMem::validateAccessPerm(system, is_sec) ||
+ frames[i]->hasNonSecureAccess();
+ if (hit && !has_access) return;
+ if (addr == cntacr_off) {
+ frames[i]->setAccessBits(data);
+ return;
+ }
+ if (addr == cntvoff_lo_off || addr == cntvoff_hi_off) {
+ if (addr == cntvoff_lo_off)
+ data = size == 4 ? insertBits(frames[i]->getVirtOffset(),
+ 31, 0, data) : data;
+ else
+ data = insertBits(frames[i]->getVirtOffset(),
+ 63, 32, data);
+ frames[i]->setVirtOffset(data);
+ return;
+ }
+ }
+ warn("GenericTimerMem::timerCtrlWrite: Unexpected address "
+ "(0x%x:%i), assuming WI\n", addr, size);
}
}
+SystemCounter *
+SystemCounterParams::create()
+{
+ return new SystemCounter(this);
+}
+
GenericTimer *
GenericTimerParams::create()
{
return new GenericTimer(this);
}
+GenericTimerFrame *
+GenericTimerFrameParams::create()
+{
+ return new GenericTimerFrame(this);
+}
+
GenericTimerMem *
GenericTimerMemParams::create()
{
/*
- * Copyright (c) 2013, 2015, 2017-2018, 2019 ARM Limited
+ * Copyright (c) 2013, 2015, 2017-2018,2020 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
#include "arch/arm/isa_device.hh"
#include "arch/arm/system.hh"
-#include "base/bitunion.hh"
#include "dev/arm/base_gic.hh"
+#include "dev/arm/generic_timer_miscregs_types.hh"
#include "sim/core.hh"
#include "sim/sim_object.hh"
/// @file
/// This module implements the global system counter and the local per-CPU
-/// architected timers as specified by the ARM Generic Timer extension (ARM
-/// ARM, Issue C, Chapter 17).
+/// architected timers as specified by the ARM Generic Timer extension:
+/// Arm ARM (ARM DDI 0487E.a)
+/// D11.1.2 - The system counter
+/// D11.2 - The AArch64 view of the Generic Timer
+/// G6.2 - The AArch32 view of the Generic Timer
+/// I2 - System Level Implementation of the Generic Timer
class Checkpoint;
+class SystemCounterParams;
class GenericTimerParams;
+class GenericTimerFrameParams;
class GenericTimerMemParams;
-/// Global system counter. It is shared by the architected timers.
-/// @todo: implement memory-mapped controls
-class SystemCounter : public Serializable
+/// Abstract class for elements whose events depend on the counting speed
+/// of the System Counter
+class SystemCounterListener : public Serializable
+{
+ public:
+ /// Called from the SystemCounter when a change in counting speed occurred
+ /// Events should be rescheduled properly inside this member function
+ virtual void notify(void) = 0;
+};
+
+/// Global system counter. It is shared by the architected and memory-mapped
+/// timers.
+class SystemCounter : public SimObject
{
protected:
+ /// Indicates if the counter is enabled
+ bool _enabled;
/// Counter frequency (as specified by CNTFRQ).
uint32_t _freq;
+ /// Counter value (as specified in CNTCV).
+ uint64_t _value;
+ /// Value increment in each counter cycle
+ uint64_t _increment;
/// Frequency modes table with all possible frequencies for the counter
std::vector<uint32_t> _freqTable;
+ /// Currently selected entry in the table, its contents should match _freq
+ size_t _activeFreqEntry;
/// Cached copy of the counter period (inverse of the frequency).
Tick _period;
- /// Tick when the counter was reset.
- Tick _resetTick;
+ /// Counter cycle start Tick when the counter status affecting
+ /// its value has been updated
+ Tick _updateTick;
- /// Kernel event stream control register
- uint32_t _regCntkctl;
- /// Hypervisor event stream control register
- uint32_t _regCnthctl;
+ /// Listeners to changes in counting speed
+ std::vector<SystemCounterListener *> _listeners;
/// Maximum architectural number of frequency table entries
static constexpr size_t MAX_FREQ_ENTRIES = 1004;
public:
- SystemCounter(std::vector<uint32_t> &freqs);
+ SystemCounter(SystemCounterParams *const p);
- /// Returns the current value of the physical counter.
- uint64_t value() const
- {
- if (_freq == 0)
- return 0; // Counter is still off.
- return (curTick() - _resetTick) / _period;
- }
+ /// Validates a System Counter reference
+ /// @param sys_cnt System counter reference to validate
+ static void validateCounterRef(SystemCounter *sys_cnt);
+ /// Indicates if the counter is enabled.
+ bool enabled() const { return _enabled; }
/// Returns the counter frequency.
uint32_t freq() const { return _freq; }
- /// Sets the counter frequency.
- /// @param freq frequency in Hz.
- void setFreq(uint32_t freq);
-
+ /// Updates and returns the counter value.
+ uint64_t value();
+ /// Returns the value increment
+ uint64_t increment() const { return _increment; }
+ /// Returns a reference to the frequency modes table.
+ std::vector<uint32_t>& freqTable() { return _freqTable; }
+ /// Returns the currently active frequency table entry.
+ size_t activeFreqEntry() const { return _activeFreqEntry; }
/// Returns the counter period.
Tick period() const { return _period; }
- void setKernelControl(uint32_t val) { _regCntkctl = val; }
- uint32_t getKernelControl() { return _regCntkctl; }
+ /// Enables the counter after a CNTCR.EN == 1
+ void enable();
+ /// Disables the counter after a CNTCR.EN == 0
+ void disable();
+
+ /// Schedules a counter frequency update after a CNTCR.FCREQ == 1
+ /// This complies with frequency transitions as per the architecture
+ /// @param new_freq_entry Index in CNTFID of the new freq
+ void freqUpdateSchedule(size_t new_freq_entry);
- void setHypControl(uint32_t val) { _regCnthctl = val; }
- uint32_t getHypControl() { return _regCnthctl; }
+ /// Sets the value explicitly from writes to CNTCR.CNTCV
+ void setValue(uint64_t new_value);
+
+ /// Called from System Counter Listeners to register
+ void registerListener(SystemCounterListener *listener);
+
+ /// Returns the tick at which a certain counter value is reached
+ Tick whenValue(uint64_t target_val);
+ Tick whenValue(uint64_t cur_val, uint64_t target_val) const;
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
private:
// Disable copying
SystemCounter(const SystemCounter &c);
+
+ /// Frequency update event handling
+ EventFunctionWrapper _freqUpdateEvent;
+ size_t _nextFreqEntry;
+ /// Callback for the frequency update
+ void freqUpdateCallback();
+
+ /// Updates the counter value.
+ void updateValue(void);
+
+ /// Updates the update tick, normalizes to the lower cycle start tick
+ void updateTick(void);
+
+ /// Notifies counting speed changes to listeners
+ void notifyListeners(void) const;
};
/// Per-CPU architected timer.
-class ArchTimer : public Serializable, public Drainable
+class ArchTimer : public SystemCounterListener, public Drainable
{
protected:
/// Control register.
/// Returns the value of the counter which this timer relies on.
uint64_t value() const;
+ Tick whenValue(uint64_t target_val) {
+ return _systemCounter.whenValue(value(), target_val);
+ }
+
+ void notify(void) override;
// Serializable
void serialize(CheckpointOut &cp) const override;
}
};
-class GenericTimer : public ClockedObject
+class GenericTimer : public SimObject
{
public:
const GenericTimerParams * params() const;
- GenericTimer(GenericTimerParams *p);
+ GenericTimer(GenericTimerParams *const p);
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
RegVal readMiscReg(int misc_reg, unsigned cpu);
protected:
- struct CoreTimers {
- CoreTimers(GenericTimer &parent, ArmSystem &system, unsigned cpu,
+ class CoreTimers : public SystemCounterListener
+ {
+ public:
+ CoreTimers(GenericTimer &_parent, ArmSystem &system, unsigned cpu,
ArmInterruptPin *_irqPhysS, ArmInterruptPin *_irqPhysNS,
ArmInterruptPin *_irqVirt, ArmInterruptPin *_irqHyp)
- : irqPhysS(_irqPhysS),
+ : parent(_parent),
+ threadContext(system.getThreadContext(cpu)),
+ irqPhysS(_irqPhysS),
irqPhysNS(_irqPhysNS),
irqVirt(_irqVirt),
irqHyp(_irqHyp),
_irqVirt),
hyp(csprintf("%s.hyp_timer%d", parent.name(), cpu),
system, parent, parent.systemCounter,
- _irqHyp)
- {}
+ _irqHyp),
+ physEvStream{
+ EventFunctionWrapper([this]{ physEventStreamCallback(); },
+ csprintf("%s.phys_event_gen%d", parent.name(), cpu)), 0, 0
+ },
+ virtEvStream{
+ EventFunctionWrapper([this]{ virtEventStreamCallback(); },
+ csprintf("%s.virt_event_gen%d", parent.name(), cpu)), 0, 0
+ }
+ {
+ cntfrq = 0x01800000;
+ }
+
+ /// System counter frequency as visible from this core
+ uint32_t cntfrq;
+
+ /// Kernel control register
+ CNTKCTL cntkctl;
+
+ /// Hypervisor control register
+ CNTHCTL cnthctl;
+
+ /// Generic Timer parent reference
+ GenericTimer &parent;
+
+ /// Thread (HW) context associated to this PE implementation
+ ThreadContext *threadContext;
ArmInterruptPin const *irqPhysS;
ArmInterruptPin const *irqPhysNS;
ArchTimerKvm virt;
ArchTimerKvm hyp;
+ // Event Stream. Events are generated based on a configurable
+ // transitionBit over the counter value. transitionTo indicates
+ // the transition direction (0->1 or 1->0)
+ struct EventStream
+ {
+ EventFunctionWrapper event;
+ uint8_t transitionTo;
+ uint8_t transitionBit;
+
+ uint64_t
+ eventTargetValue(uint64_t val) const
+ {
+ uint64_t bit_val = bits(val, transitionBit);
+ uint64_t ret_val = mbits(val, 63, transitionBit);
+ uint64_t incr_val = 1 << transitionBit;
+ if (bit_val == transitionTo)
+ incr_val *= 2;
+ return ret_val + incr_val;
+ }
+ };
+
+ EventStream physEvStream;
+ EventStream virtEvStream;
+ void physEventStreamCallback();
+ void virtEventStreamCallback();
+ void eventStreamCallback() const;
+ void schedNextEvent(EventStream &ev_stream, ArchTimer &timer);
+
+ void notify(void) override;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
private:
// Disable copying
CoreTimers(const CoreTimers &c);
CoreTimers &getTimers(int cpu_id);
void createTimers(unsigned cpus);
- /// System counter.
- SystemCounter systemCounter;
+ /// System counter reference.
+ SystemCounter &systemCounter;
/// Per-CPU physical architected timers.
std::vector<std::unique_ptr<CoreTimers>> timers;
protected: // Configuration
/// ARM system containing this timer
ArmSystem &system;
+
+ void handleStream(CoreTimers::EventStream *ev_stream,
+ ArchTimer *timer, RegVal old_cnt_ctl, RegVal cnt_ctl);
};
class GenericTimerISA : public ArmISA::BaseISADevice
unsigned cpu;
};
-class GenericTimerMem : public PioDevice
+class GenericTimerFrame : public PioDevice
{
public:
- GenericTimerMem(GenericTimerMemParams *p);
+ GenericTimerFrame(GenericTimerFrameParams *const p);
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
- public: // PioDevice
- AddrRangeList getAddrRanges() const override { return addrRanges; }
+ /// Indicates if this frame implements a virtual timer
+ bool hasVirtualTimer() const;
+
+ /// Returns the virtual offset for this frame if a virtual timer is
+ /// implemented
+ uint64_t getVirtOffset() const;
+
+ /// Sets the virtual offset for this frame's virtual timer after
+ /// a write to CNTVOFF
+ void setVirtOffset(uint64_t new_offset);
+
+ /// Indicates if this frame implements a second EL0 view
+ bool hasEl0View() const;
+
+ /// Returns the access bits for this frame
+ uint8_t getAccessBits() const;
+
+ /// Updates the access bits after a write to CNTCTLBase.CNTACR
+ void setAccessBits(uint8_t data);
+
+ /// Indicates if non-secure accesses are allowed to this frame
+ bool hasNonSecureAccess() const;
+
+ /// Allows non-secure accesses after an enabling write to
+ /// CNTCTLBase.CNTNSAR
+ void setNonSecureAccess();
+
+ /// Indicates if CNTVOFF is readable for this frame
+ bool hasReadableVoff() const;
+
+ protected:
+ AddrRangeList getAddrRanges() const override;
Tick read(PacketPtr pkt) override;
Tick write(PacketPtr pkt) 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;
+ private:
+ /// CNTBase/CNTEL0Base (Memory-mapped timer frame)
+ uint64_t timerRead(Addr addr, size_t size, bool is_sec, bool to_el0) const;
+ void timerWrite(Addr addr, size_t size, uint64_t data, bool is_sec,
+ bool to_el0);
const AddrRange timerRange;
- const AddrRangeList addrRanges;
-
- protected:
- /// System counter.
- SystemCounter systemCounter;
+ AddrRange timerEl0Range;
+
+ static const Addr TIMER_CNTPCT_LO = 0x00;
+ static const Addr TIMER_CNTPCT_HI = 0x04;
+ static const Addr TIMER_CNTVCT_LO = 0x08;
+ static const Addr TIMER_CNTVCT_HI = 0x0c;
+ static const Addr TIMER_CNTFRQ = 0x10;
+ static const Addr TIMER_CNTEL0ACR = 0x14;
+ static const Addr TIMER_CNTVOFF_LO = 0x18;
+ static const Addr TIMER_CNTVOFF_HI = 0x1c;
+ static const Addr TIMER_CNTP_CVAL_LO = 0x20;
+ static const Addr TIMER_CNTP_CVAL_HI = 0x24;
+ static const Addr TIMER_CNTP_TVAL = 0x28;
+ static const Addr TIMER_CNTP_CTL = 0x2c;
+ static const Addr TIMER_CNTV_CVAL_LO = 0x30;
+ static const Addr TIMER_CNTV_CVAL_HI = 0x34;
+ static const Addr TIMER_CNTV_TVAL = 0x38;
+ static const Addr TIMER_CNTV_CTL = 0x3c;
+
+ /// All MMIO ranges GenericTimerFrame responds to
+ AddrRangeList addrRanges;
+
+ /// System counter reference.
+ SystemCounter &systemCounter;
+
+ /// Physical and virtual timers
ArchTimer physTimer;
ArchTimer virtTimer;
+
+ /// Reports access properties of the CNTBase register frame elements
+ BitUnion8(AccessBits)
+ Bitfield<5> rwpt;
+ Bitfield<4> rwvt;
+ Bitfield<3> rvoff;
+ Bitfield<2> rfrq;
+ Bitfield<1> rvct;
+ Bitfield<0> rpct;
+ EndBitUnion(AccessBits)
+ AccessBits accessBits;
+
+ // Reports access properties of the CNTEL0Base register frame elements
+ BitUnion16(AccessBitsEl0)
+ Bitfield<9> pten;
+ Bitfield<8> vten;
+ Bitfield<1> vcten;
+ Bitfield<0> pcten;
+ EndBitUnion(AccessBitsEl0)
+ AccessBitsEl0 accessBitsEl0;
+
+ /// Reports whether non-secure accesses are allowed to this frame
+ bool nonSecureAccess;
+
+ ArmSystem &system;
+};
+
+class GenericTimerMem : public PioDevice
+{
+ public:
+ GenericTimerMem(GenericTimerMemParams *const p);
+
+ /// Validates a Generic Timer register frame address range
+ /// @param base_addr Range of the register frame
+ static void validateFrameRange(const AddrRange &range);
+
+ /// Validates an MMIO access permissions
+ /// @param sys System reference where the acces is being made
+ /// @param is_sec If the access is to secure memory
+ static bool validateAccessPerm(ArmSystem &sys, bool is_sec);
+
+ protected:
+ AddrRangeList getAddrRanges() const override;
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+
+ private:
+ /// CNTControlBase (System counter control frame)
+ uint64_t counterCtrlRead(Addr addr, size_t size, bool is_sec) const;
+ void counterCtrlWrite(Addr addr, size_t size, uint64_t data, bool is_sec);
+ const AddrRange counterCtrlRange;
+
+ BitUnion32(CNTCR)
+ Bitfield<17,8> fcreq;
+ Bitfield<2> scen;
+ Bitfield<1> hdbg;
+ Bitfield<0> en;
+ EndBitUnion(CNTCR)
+
+ BitUnion32(CNTSR)
+ Bitfield<31,8> fcack;
+ EndBitUnion(CNTSR)
+
+ static const Addr COUNTER_CTRL_CNTCR = 0x00;
+ static const Addr COUNTER_CTRL_CNTSR = 0x04;
+ static const Addr COUNTER_CTRL_CNTCV_LO = 0x08;
+ static const Addr COUNTER_CTRL_CNTCV_HI = 0x0c;
+ static const Addr COUNTER_CTRL_CNTSCR = 0x10;
+ static const Addr COUNTER_CTRL_CNTID = 0x1c;
+ static const Addr COUNTER_CTRL_CNTFID = 0x20;
+
+ /// CNTReadBase (System counter status frame)
+ uint64_t counterStatusRead(Addr addr, size_t size) const;
+ void counterStatusWrite(Addr addr, size_t size, uint64_t data);
+ const AddrRange counterStatusRange;
+
+ static const Addr COUNTER_STATUS_CNTCV_LO = 0x00;
+ static const Addr COUNTER_STATUS_CNTCV_HI = 0x04;
+
+ /// CNTCTLBase (Memory-mapped timer global control frame)
+ uint64_t timerCtrlRead(Addr addr, size_t size, bool is_sec) const;
+ void timerCtrlWrite(Addr addr, size_t size, uint64_t data, bool is_sec);
+ const AddrRange timerCtrlRange;
+
+ /// ID register for reporting features of implemented timer frames
+ uint32_t cnttidr;
+
+ static const Addr TIMER_CTRL_CNTFRQ = 0x00;
+ static const Addr TIMER_CTRL_CNTNSAR = 0x04;
+ static const Addr TIMER_CTRL_CNTTIDR = 0x08;
+ static const Addr TIMER_CTRL_CNTACR = 0x40;
+ static const Addr TIMER_CTRL_CNTVOFF_LO = 0x80;
+ static const Addr TIMER_CTRL_CNTVOFF_HI = 0x84;
+
+ /// All MMIO ranges GenericTimerMem responds to
+ const AddrRangeList addrRanges;
+
+ /// System counter reference.
+ SystemCounter &systemCounter;
+
+ /// Maximum architectural number of memory-mapped timer frames
+ static constexpr size_t MAX_TIMER_FRAMES = 8;
+
+ /// Timer frame references
+ std::vector<GenericTimerFrame *> frames;
+
+ ArmSystem &system;
};
#endif // __DEV_ARM_GENERIC_TIMER_HH__