Files
gem5/src/dev/arm/generic_timer.cc
Gabe Black 314dae4b86 dev-arm: Set cntkctl and cnthctl in the CoreTimers constructor.
The cntkctl and cnthctl registers were not initialized by the CoreTimers
constructor which upset valgrind when they were later used by
handleStream.

Change-Id: Iaedbb2d957aeb428fd563be2e24ccb8d2cf57f26
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/52403
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
2021-11-06 08:39:46 +00:00

1592 lines
46 KiB
C++

/*
* Copyright (c) 2013, 2015, 2017-2018,2020 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "dev/arm/generic_timer.hh"
#include <cmath>
#include "arch/arm/page_size.hh"
#include "arch/arm/system.hh"
#include "arch/arm/utility.hh"
#include "base/logging.hh"
#include "base/trace.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"
#include "params/SystemCounter.hh"
#include "sim/core.hh"
#include "sim/cur_tick.hh"
namespace gem5
{
using namespace ArmISA;
SystemCounter::SystemCounter(const SystemCounterParams &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");
// Store the table end marker as a 32-bit zero word
_freqTable.push_back(0);
fatal_if(_freqTable.size() > MAX_FREQ_ENTRIES,
"SystemCounter::SystemCounter: Architecture states a maximum of 1004 "
"frequency table entries, limit surpassed\n");
// Set the active frequency to be the base
_freq = _freqTable.front();
_period = (1.0 / _freq) * sim_clock::Frequency;
}
void
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()
{
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();
}
}
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) * sim_clock::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
{
DPRINTF(Timer, "SystemCounter::serialize: Serializing\n");
SERIALIZE_SCALAR(_enabled);
SERIALIZE_SCALAR(_freq);
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)
{
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) * sim_clock::Frequency;
}
ArchTimer::ArchTimer(const std::string &name,
SimObject &parent,
SystemCounter &sysctr,
ArmInterruptPin *interrupt)
: _name(name), _parent(parent), _systemCounter(sysctr),
_interrupt(interrupt),
_control(0), _counterLimit(0), _offset(0),
_counterLimitReachedEvent([this]{ counterLimitReached(); }, name)
{
_systemCounter.registerListener(this);
}
void
ArchTimer::counterLimitReached()
{
if (!_control.enable)
return;
DPRINTF(Timer, "Counter limit reached\n");
_control.istatus = 1;
if (!_control.imask) {
if (scheduleEvents()) {
DPRINTF(Timer, "Causing interrupt\n");
_interrupt->raise();
} else {
DPRINTF(Timer, "Kvm mode; skipping simulated interrupt\n");
}
}
}
void
ArchTimer::updateCounter()
{
if (_counterLimitReachedEvent.scheduled())
_parent.deschedule(_counterLimitReachedEvent);
if (value() >= _counterLimit) {
counterLimitReached();
} else {
// Clear the interurpt when timers conditions are not met
if (_interrupt->active()) {
DPRINTF(Timer, "Clearing interrupt\n");
_interrupt->clear();
}
_control.istatus = 0;
if (scheduleEvents()) {
_parent.schedule(_counterLimitReachedEvent,
whenValue(_counterLimit));
}
}
}
void
ArchTimer::setCompareValue(uint64_t val)
{
_counterLimit = val;
updateCounter();
}
void
ArchTimer::setTimerValue(uint32_t val)
{
setCompareValue(value() + sext<32>(val));
}
void
ArchTimer::setControl(uint32_t val)
{
ArchTimerCtrl old_ctl = _control, new_ctl = val;
_control.enable = new_ctl.enable;
_control.imask = new_ctl.imask;
_control.istatus = old_ctl.istatus;
// Timer unmasked or enabled
if ((old_ctl.imask && !new_ctl.imask) ||
(!old_ctl.enable && new_ctl.enable))
updateCounter();
// Timer masked or disabled
else if ((!old_ctl.imask && new_ctl.imask) ||
(old_ctl.enable && !new_ctl.enable)) {
if (_interrupt->active()) {
DPRINTF(Timer, "Clearing interrupt\n");
// We are clearing the interrupt but we are not
// setting istatus to 0 as we are doing
// in the updateCounter.
// istatus signals that Timer conditions are met.
// It shouldn't depend on masking.
// if enable is zero. istatus is unknown.
_interrupt->clear();
}
}
}
void
ArchTimer::setOffset(uint64_t val)
{
_offset = val;
updateCounter();
}
uint64_t
ArchTimer::value() const
{
return _systemCounter.value() - _offset;
}
void
ArchTimer::notify()
{
updateCounter();
}
void
ArchTimer::serialize(CheckpointOut &cp) const
{
paramOut(cp, "control_serial", _control);
SERIALIZE_SCALAR(_counterLimit);
SERIALIZE_SCALAR(_offset);
}
void
ArchTimer::unserialize(CheckpointIn &cp)
{
paramIn(cp, "control_serial", _control);
// We didn't serialize an offset before we added support for the
// virtual timer. Consider it optional to maintain backwards
// compatibility.
if (!UNSERIALIZE_OPT_SCALAR(_offset))
_offset = 0;
// We no longer schedule an event here because we may enter KVM
// emulation. The event creation is delayed until drainResume().
}
DrainState
ArchTimer::drain()
{
if (_counterLimitReachedEvent.scheduled())
_parent.deschedule(_counterLimitReachedEvent);
return DrainState::Drained;
}
void
ArchTimer::drainResume()
{
updateCounter();
}
GenericTimer::GenericTimer(const GenericTimerParams &p)
: SimObject(p),
systemCounter(*p.counter),
system(*p.system)
{
SystemCounter::validateCounterRef(p.counter);
fatal_if(!p.system, "GenericTimer::GenericTimer: No system specified, "
"can't instantiate architected timers\n");
system.setGenericTimer(this);
}
void
GenericTimer::serialize(CheckpointOut &cp) const
{
paramOut(cp, "cpu_count", timers.size());
for (int i = 0; i < timers.size(); ++i) {
const CoreTimers &core(*timers[i]);
core.serializeSection(cp, csprintf("pe_implementation%d", i));
}
}
void
GenericTimer::unserialize(CheckpointIn &cp)
{
// 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.
static const unsigned OLD_CPU_MAX = 8;
unsigned cpu_count;
if (!UNSERIALIZE_OPT_SCALAR(cpu_count)) {
warn("Checkpoint does not contain CPU count, assuming %i CPUs\n",
OLD_CPU_MAX);
cpu_count = OLD_CPU_MAX;
}
// We cannot assert for equality here because CPU timers are dynamically
// created on the first miscreg access. Therefore, if we take the checkpoint
// before any timer registers have been accessed, the number of counters
// is actually smaller than the total number of CPUs.
if (cpu_count > system.threads.size()) {
fatal("The simulated system has been initialized with %d CPUs, "
"but the Generic Timer checkpoint expects %d CPUs. Consider "
"restoring the checkpoint specifying %d CPUs.",
system.threads.size(), cpu_count, cpu_count);
}
for (int i = 0; i < cpu_count; ++i) {
CoreTimers &core(getTimers(i));
core.unserializeSection(cp, csprintf("pe_implementation%d", i));
}
}
GenericTimer::CoreTimers &
GenericTimer::getTimers(int cpu_id)
{
if (cpu_id >= timers.size())
createTimers(cpu_id + 1);
return *timers[cpu_id];
}
void
GenericTimer::createTimers(unsigned cpus)
{
assert(timers.size() < cpus);
auto &p = params();
const unsigned old_cpu_count(timers.size());
timers.resize(cpus);
for (unsigned i = old_cpu_count; i < cpus; ++i) {
ThreadContext *tc = system.threads[i];
timers[i].reset(
new CoreTimers(*this, system, i,
p.int_phys_s->get(tc),
p.int_phys_ns->get(tc),
p.int_virt->get(tc),
p.int_hyp->get(tc)));
}
}
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.threads[cpu];
switch (reg) {
case MISCREG_CNTFRQ:
case MISCREG_CNTFRQ_EL0:
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:
{
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:
{
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:
core.physNS.setCompareValue(val);
return;
case MISCREG_CNTP_TVAL_NS:
case MISCREG_CNTP_TVAL_EL0:
core.physNS.setTimerValue(val);
return;
case MISCREG_CNTP_CTL_NS:
case MISCREG_CNTP_CTL_EL0:
core.physNS.setControl(val);
return;
// Count registers
case MISCREG_CNTPCT:
case MISCREG_CNTPCT_EL0:
case MISCREG_CNTVCT:
case MISCREG_CNTVCT_EL0:
warn("Ignoring write to read only count register: %s\n",
miscRegName[reg]);
return;
// Virtual timer
case MISCREG_CNTVOFF:
case MISCREG_CNTVOFF_EL2:
core.virt.setOffset(val);
return;
case MISCREG_CNTV_CVAL:
case MISCREG_CNTV_CVAL_EL0:
core.virt.setCompareValue(val);
return;
case MISCREG_CNTV_TVAL:
case MISCREG_CNTV_TVAL_EL0:
core.virt.setTimerValue(val);
return;
case MISCREG_CNTV_CTL:
case MISCREG_CNTV_CTL_EL0:
core.virt.setControl(val);
return;
// Physical timer (S)
case MISCREG_CNTP_CTL_S:
case MISCREG_CNTPS_CTL_EL1:
core.physS.setControl(val);
return;
case MISCREG_CNTP_CVAL_S:
case MISCREG_CNTPS_CVAL_EL1:
core.physS.setCompareValue(val);
return;
case MISCREG_CNTP_TVAL_S:
case MISCREG_CNTPS_TVAL_EL1:
core.physS.setTimerValue(val);
return;
// Hyp phys. timer, non-secure
case MISCREG_CNTHP_CTL:
case MISCREG_CNTHP_CTL_EL2:
core.hyp.setControl(val);
return;
case MISCREG_CNTHP_CVAL:
case MISCREG_CNTHP_CVAL_EL2:
core.hyp.setCompareValue(val);
return;
case MISCREG_CNTHP_TVAL:
case MISCREG_CNTHP_TVAL_EL2:
core.hyp.setTimerValue(val);
return;
default:
warn("Writing to unknown register: %s\n", miscRegName[reg]);
return;
}
}
RegVal
GenericTimer::readMiscReg(int reg, unsigned cpu)
{
CoreTimers &core(getTimers(cpu));
switch (reg) {
case MISCREG_CNTFRQ:
case MISCREG_CNTFRQ_EL0:
return core.cntfrq;
case MISCREG_CNTKCTL:
case MISCREG_CNTKCTL_EL1:
return core.cntkctl & 0x00000000ffffffff;
case MISCREG_CNTHCTL:
case MISCREG_CNTHCTL_EL2:
return core.cnthctl & 0x00000000ffffffff;
// Physical timer
case MISCREG_CNTP_CVAL_NS:
case MISCREG_CNTP_CVAL_EL0:
return core.physNS.compareValue();
case MISCREG_CNTP_TVAL_NS:
case MISCREG_CNTP_TVAL_EL0:
return core.physNS.timerValue();
case MISCREG_CNTP_CTL_EL0:
case MISCREG_CNTP_CTL_NS:
return core.physNS.control();
case MISCREG_CNTPCT:
case MISCREG_CNTPCT_EL0:
return core.physNS.value();
// Virtual timer
case MISCREG_CNTVCT:
case MISCREG_CNTVCT_EL0:
return core.virt.value();
case MISCREG_CNTVOFF:
case MISCREG_CNTVOFF_EL2:
return core.virt.offset();
case MISCREG_CNTV_CVAL:
case MISCREG_CNTV_CVAL_EL0:
return core.virt.compareValue();
case MISCREG_CNTV_TVAL:
case MISCREG_CNTV_TVAL_EL0:
return core.virt.timerValue();
case MISCREG_CNTV_CTL:
case MISCREG_CNTV_CTL_EL0:
return core.virt.control();
// PL1 phys. timer, secure
case MISCREG_CNTP_CTL_S:
case MISCREG_CNTPS_CTL_EL1:
return core.physS.control();
case MISCREG_CNTP_CVAL_S:
case MISCREG_CNTPS_CVAL_EL1:
return core.physS.compareValue();
case MISCREG_CNTP_TVAL_S:
case MISCREG_CNTPS_TVAL_EL1:
return core.physS.timerValue();
// HYP phys. timer (NS)
case MISCREG_CNTHP_CTL:
case MISCREG_CNTHP_CTL_EL2:
return core.hyp.control();
case MISCREG_CNTHP_CVAL:
case MISCREG_CNTHP_CVAL_EL2:
return core.hyp.compareValue();
case MISCREG_CNTHP_TVAL:
case MISCREG_CNTHP_TVAL_EL2:
return core.hyp.timerValue();
default:
warn("Reading from unknown register: %s\n", miscRegName[reg]);
return 0;
}
}
GenericTimer::CoreTimers::CoreTimers(GenericTimer &_parent,
ArmSystem &system, unsigned cpu,
ArmInterruptPin *_irqPhysS, ArmInterruptPin *_irqPhysNS,
ArmInterruptPin *_irqVirt, ArmInterruptPin *_irqHyp)
: parent(_parent),
cntfrq(parent.params().cntfrq),
cntkctl(0), cnthctl(0),
threadContext(system.threads[cpu]),
irqPhysS(_irqPhysS),
irqPhysNS(_irqPhysNS),
irqVirt(_irqVirt),
irqHyp(_irqHyp),
physS(csprintf("%s.phys_s_timer%d", parent.name(), cpu),
system, parent, parent.systemCounter,
_irqPhysS),
// This should really be phys_timerN, but we are stuck with
// arch_timer for backwards compatibility.
physNS(csprintf("%s.arch_timer%d", parent.name(), cpu),
system, parent, parent.systemCounter,
_irqPhysNS),
virt(csprintf("%s.virt_timer%d", parent.name(), cpu),
system, parent, parent.systemCounter,
_irqVirt),
hyp(csprintf("%s.hyp_timer%d", parent.name(), cpu),
system, parent, parent.systemCounter,
_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
}
{
}
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
{
SERIALIZE_SCALAR(cntfrq);
SERIALIZE_SCALAR(cntkctl);
SERIALIZE_SCALAR(cnthctl);
const bool phys_ev_scheduled = physEvStream.event.scheduled();
SERIALIZE_SCALAR(phys_ev_scheduled);
if (phys_ev_scheduled) {
const Tick phys_ev_when = physEvStream.event.when();
SERIALIZE_SCALAR(phys_ev_when);
}
SERIALIZE_SCALAR(physEvStream.transitionTo);
SERIALIZE_SCALAR(physEvStream.transitionBit);
const bool virt_ev_scheduled = virtEvStream.event.scheduled();
SERIALIZE_SCALAR(virt_ev_scheduled);
if (virt_ev_scheduled) {
const Tick virt_ev_when = virtEvStream.event.when();
SERIALIZE_SCALAR(virt_ev_when);
}
SERIALIZE_SCALAR(virtEvStream.transitionTo);
SERIALIZE_SCALAR(virtEvStream.transitionBit);
physS.serializeSection(cp, "phys_s_timer");
physNS.serializeSection(cp, "phys_ns_timer");
virt.serializeSection(cp, "virt_timer");
hyp.serializeSection(cp, "hyp_timer");
}
void
GenericTimer::CoreTimers::unserialize(CheckpointIn &cp)
{
UNSERIALIZE_SCALAR(cntfrq);
UNSERIALIZE_SCALAR(cntkctl);
UNSERIALIZE_SCALAR(cnthctl);
bool phys_ev_scheduled;
UNSERIALIZE_SCALAR(phys_ev_scheduled);
if (phys_ev_scheduled) {
Tick phys_ev_when;
UNSERIALIZE_SCALAR(phys_ev_when);
parent.reschedule(physEvStream.event, phys_ev_when, true);
}
UNSERIALIZE_SCALAR(physEvStream.transitionTo);
UNSERIALIZE_SCALAR(physEvStream.transitionBit);
bool virt_ev_scheduled;
UNSERIALIZE_SCALAR(virt_ev_scheduled);
if (virt_ev_scheduled) {
Tick virt_ev_when;
UNSERIALIZE_SCALAR(virt_ev_when);
parent.reschedule(virtEvStream.event, virt_ev_when, true);
}
UNSERIALIZE_SCALAR(virtEvStream.transitionTo);
UNSERIALIZE_SCALAR(virtEvStream.transitionBit);
physS.unserializeSection(cp, "phys_s_timer");
physNS.unserializeSection(cp, "phys_ns_timer");
virt.unserializeSection(cp, "virt_timer");
hyp.unserializeSection(cp, "hyp_timer");
}
void
GenericTimerISA::setMiscReg(int reg, RegVal val)
{
DPRINTF(Timer, "Setting %s := 0x%x\n", miscRegName[reg], val);
parent.setMiscReg(reg, cpu, val);
}
RegVal
GenericTimerISA::readMiscReg(int reg)
{
RegVal value = parent.readMiscReg(reg, cpu);
DPRINTF(Timer, "Reading %s as 0x%x\n", miscRegName[reg], value);
return value;
}
GenericTimerFrame::GenericTimerFrame(const GenericTimerFrameParams &p)
: PioDevice(p),
timerRange(RangeSize(p.cnt_base, ArmSystem::PageBytes)),
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_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, ArmSystem::PageBytes);
accessBitsEl0 = 0x303;
addrRanges.push_back(timerEl0Range);
}
for (auto &range : addrRanges)
GenericTimerMem::validateFrameRange(range);
}
void
GenericTimerFrame::serialize(CheckpointOut &cp) const
{
SERIALIZE_SCALAR(accessBits);
if (hasEl0View())
SERIALIZE_SCALAR(accessBitsEl0);
SERIALIZE_SCALAR(nonSecureAccess);
physTimer.serializeSection(cp, "phys_timer");
virtTimer.serializeSection(cp, "virt_timer");
}
void
GenericTimerFrame::unserialize(CheckpointIn &cp)
{
UNSERIALIZE_SCALAR(accessBits);
if (hasEl0View())
UNSERIALIZE_SCALAR(accessBitsEl0);
UNSERIALIZE_SCALAR(nonSecureAccess);
physTimer.unserializeSection(cp, "phys_timer");
virtTimer.unserializeSection(cp, "virt_timer");
}
uint64_t
GenericTimerFrame::getVirtOffset() const
{
return virtTimer.offset();
}
void
GenericTimerFrame::setVirtOffset(uint64_t new_offset)
{
virtTimer.setOffset(new_offset);
}
bool
GenericTimerFrame::hasEl0View() const
{
return timerEl0Range.valid();
}
uint8_t
GenericTimerFrame::getAccessBits() const
{
return accessBits;
}
void
GenericTimerFrame::setAccessBits(uint8_t data)
{
accessBits = data & 0x3f;
}
bool
GenericTimerFrame::hasNonSecureAccess() const
{
return nonSecureAccess;
}
void
GenericTimerFrame::setNonSecureAccess()
{
nonSecureAccess = true;
}
bool
GenericTimerFrame::hasReadableVoff() const
{
return accessBits.rvoff;
}
AddrRangeList
GenericTimerFrame::getAddrRanges() const
{
return addrRanges;
}
Tick
GenericTimerFrame::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,
"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("GenericTimerFrame::read: Invalid address: 0x%x\n", addr);
}
resp = timerRead(offset, size, is_sec, to_el0);
DPRINTF(Timer, "GenericTimerFrame::read: 0x%x<-0x%x(%i) [S = %u]\n", resp,
addr, size, is_sec);
pkt->setUintX(resp, ByteOrder::little);
pkt->makeResponse();
return 0;
}
Tick
GenericTimerFrame::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,
"GenericTimerFrame::write: Invalid size %i\n", size);
bool to_el0 = false;
const uint64_t data = pkt->getUintX(ByteOrder::little);
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("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
GenericTimerFrame::timerRead(Addr addr, size_t size, bool is_sec,
bool to_el0) const
{
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 TIMER_CNTEL0ACR:
if (!hasEl0View() || to_el0)
return 0;
else
return accessBitsEl0;
case TIMER_CNTP_CVAL_LO:
if (!accessBits.rwpt || (to_el0 && !accessBitsEl0.pten))
return 0;
else
return physTimer.compareValue();
case TIMER_CNTP_CVAL_HI:
if (!accessBits.rwpt || (to_el0 && !accessBitsEl0.pten))
return 0;
else
return physTimer.compareValue() >> 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();
case TIMER_CNTVCT_LO:
if (!accessBits.rvct || (to_el0 && !accessBitsEl0.vcten))
return 0;
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();
case TIMER_CNTVOFF_HI:
if (!accessBits.rvoff || (to_el0))
return 0;
else
return virtTimer.offset() >> 32;
case TIMER_CNTV_CVAL_LO:
if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
return 0;
else
return virtTimer.compareValue();
case TIMER_CNTV_CVAL_HI:
if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
return 0;
else
return virtTimer.compareValue() >> 32;
case TIMER_CNTV_TVAL:
if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
return 0;
else
return virtTimer.timerValue();
case TIMER_CNTV_CTL:
if (!accessBits.rwvt || (to_el0 && !accessBitsEl0.vten))
return 0;
else
return virtTimer.control();
default:
warn("GenericTimerFrame::timerRead: Unexpected address (0x%x:%i), "
"assuming RAZ\n", addr, size);
return 0;
}
}
void
GenericTimerFrame::timerWrite(Addr addr, size_t size, uint64_t data,
bool is_sec, bool to_el0)
{
if (!GenericTimerMem::validateAccessPerm(system, is_sec) &&
!nonSecureAccess)
return;
switch (addr) {
case TIMER_CNTPCT_LO ... TIMER_CNTPCT_HI:
warn("GenericTimerFrame::timerWrite: RO reg (0x%x) [CNTPCT]\n",
addr);
return;
case TIMER_CNTFRQ:
warn("GenericTimerFrame::timerWrite: RO reg (0x%x) [CNTFRQ]\n",
addr);
return;
case TIMER_CNTEL0ACR:
if (!hasEl0View() || to_el0)
return;
insertBits(accessBitsEl0, 9, 8, data);
insertBits(accessBitsEl0, 1, 0, data);
return;
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 TIMER_CNTP_CVAL_HI:
if ((!accessBits.rwpt) || (to_el0 && !accessBitsEl0.pten))
return;
data = insertBits(physTimer.compareValue(), 63, 32, data);
physTimer.setCompareValue(data);
return;
case TIMER_CNTP_TVAL:
if ((!accessBits.rwpt) || (to_el0 && !accessBitsEl0.pten))
return;
physTimer.setTimerValue(data);
return;
case TIMER_CNTP_CTL:
if ((!accessBits.rwpt) || (to_el0 && !accessBitsEl0.pten))
return;
physTimer.setControl(data);
return;
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_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_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_TVAL:
if ((!accessBits.rwvt) || (to_el0 && !accessBitsEl0.vten))
return;
virtTimer.setTimerValue(data);
return;
case TIMER_CNTV_CTL:
if ((!accessBits.rwvt) || (to_el0 && !accessBitsEl0.vten))
return;
virtTimer.setControl(data);
return;
default:
warn("GenericTimerFrame::timerWrite: Unexpected address (0x%x:%i), "
"assuming WI\n", addr, size);
}
}
GenericTimerMem::GenericTimerMem(const GenericTimerMemParams &p)
: PioDevice(p),
counterCtrlRange(RangeSize(p.cnt_control_base, ArmSystem::PageBytes)),
counterStatusRange(RangeSize(p.cnt_read_base, ArmSystem::PageBytes)),
timerCtrlRange(RangeSize(p.cnt_ctl_base, ArmSystem::PageBytes)),
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);
}
}
void
GenericTimerMem::validateFrameRange(const AddrRange &range)
{
fatal_if(range.start() % ArmSystem::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");
}
bool
GenericTimerMem::validateAccessPerm(ArmSystem &sys, bool is_sec)
{
return !sys.has(ArmExtension::SECURITY) || is_sec;
}
AddrRangeList
GenericTimerMem::getAddrRanges() const
{
return addrRanges;
}
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, ByteOrder::little);
pkt->makeResponse();
return 0;
}
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(ByteOrder::little);
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);
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];
}
warn("GenericTimerMem::counterCtrlRead: Unexpected address "
"(0x%x:%i), assuming RAZ\n", addr, size);
return 0;
}
}
}
void
GenericTimerMem::counterCtrlWrite(Addr addr, size_t size, uint64_t data,
bool is_sec)
{
if (!GenericTimerMem::validateAccessPerm(system, is_sec))
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 COUNTER_CTRL_CNTSR:
warn("GenericTimerMem::counterCtrlWrite: RO reg (0x%x) [CNTSR]\n",
addr);
return;
case COUNTER_CTRL_CNTCV_LO:
data = size == 4 ? insertBits(systemCounter.value(), 31, 0, data)
: data;
systemCounter.setValue(data);
return;
case COUNTER_CTRL_CNTCV_HI:
data = insertBits(systemCounter.value(), 63, 32, data);
systemCounter.setValue(data);
return;
case COUNTER_CTRL_CNTSCR:
return;
case COUNTER_CTRL_CNTID:
warn("GenericTimerMem::counterCtrlWrite: RO reg (0x%x) [CNTID]\n",
addr);
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);
}
}
}
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;
}
}
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);
}
}
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;
}
}
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();
}
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);
}
}
} // namespace gem5