/*
- * 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()
{