diff --git a/src/dev/arm/GenericTimer.py b/src/dev/arm/GenericTimer.py new file mode 100644 index 0000000000..077b6f65dd --- /dev/null +++ b/src/dev/arm/GenericTimer.py @@ -0,0 +1,191 @@ +# Copyright (c) 2009-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. + +from m5.SimObject import SimObject +from m5.objects.Device import PioDevice +from m5.params import Param, MaxAddr, NULL, VectorParam +from m5.proxy import Parent +from m5.util import fatal +from m5.util.fdthelper import FdtNode, FdtProperty, FdtPropertyWords, FdtState + +class SystemCounter(SimObject): + """ +Shared by both PE-implementations and memory-mapped timers. It provides a +uniform view of system time through its counter value. + +Reference: + Arm ARM (ARM DDI 0487E.a) + D11.1.2 - The system counter + """ + + type = 'SystemCounter' + cxx_header = "dev/arm/generic_timer.hh" + + # Maximum of 1004 frequency entries, including end marker + freqs = VectorParam.UInt32([0x01800000], "Frequencies available for the " + "system counter (in Hz). First element is the base frequency, " + "following are alternative lower ones which must be exact divisors") + + def generateDtb(self): + if not self.freqs: + fatal("No counter frequency to expose in DTB") + return FdtPropertyWords("clock-frequency", [self.freqs[0]]) + +class GenericTimer(SimObject): + """ +Architected timers per PE in the system. Each of them provides a physical +counter, a virtual counter and several timers accessible from different +exception levels and security states. + +Reference: + Arm ARM (ARM DDI 0487E.a) + D11.2 - The AArch64 view of the Generic Timer + G6.2 - The AArch32 view of the Generic Timer + """ + + type = 'GenericTimer' + cxx_header = "dev/arm/generic_timer.hh" + + _freq_in_dtb = False + + system = Param.ArmSystem(Parent.any, "system") + + counter = Param.SystemCounter(Parent.any, "Global system counter") + + int_phys_s = Param.ArmPPI("Physical (S) timer interrupt") + int_phys_ns = Param.ArmPPI("Physical (NS) timer interrupt") + int_virt = Param.ArmPPI("Virtual timer interrupt") + int_hyp = Param.ArmPPI("Hypervisor timer interrupt") + + def generateDeviceTree(self, state): + node = FdtNode("timer") + + node.appendCompatible(["arm,cortex-a15-timer", + "arm,armv7-timer", + "arm,armv8-timer"]) + node.append(FdtPropertyWords("interrupts", [ + 1, int(self.int_phys_s.num) - 16, 0xf08, + 1, int(self.int_phys_ns.num) - 16, 0xf08, + 1, int(self.int_virt.num) - 16, 0xf08, + 1, int(self.int_hyp.num) - 16, 0xf08, + ])) + + if self._freq_in_dtb: + node.append(self.counter.unproxy(self).generateDtb()) + + yield node + +class GenericTimerFrame(PioDevice): + """ +Memory-mapped timer frame implementation. Controlled from GenericTimerMem, +may be used by peripherals without a system register interface. + +Reference: + Arm ARM (ARM DDI 0487E.a) + I2.3.2 - The CNTBaseN and CNTEL0BaseN frames + """ + + type = 'GenericTimerFrame' + cxx_header = "dev/arm/generic_timer.hh" + + _frame_num = 0 + + counter = Param.SystemCounter(Parent.any, "Global system counter") + + cnt_base = Param.Addr("CNTBase register frame base") + cnt_el0_base = Param.Addr(MaxAddr, "CNTEL0Base register frame base") + + int_phys = Param.ArmSPI("Physical Interrupt") + int_virt = Param.ArmSPI("Virtual Interrupt") + + def generateDeviceTree(self, state): + node = FdtNode("frame@{:08x}".format(self.cnt_base.value)) + node.append(FdtPropertyWords("frame-number", self._frame_num)) + ints = [0, int(self.int_phys.num) - 32, 4] + if self.int_virt != NULL: + ints.extend([0, int(self.int_virt.num) - 32, 4]) + node.append(FdtPropertyWords("interrupts", ints)) + reg = state.addrCells(self.cnt_base) + state.sizeCells(0x1000) + if self.cnt_el0_base.value != MaxAddr: + reg.extend(state.addrCells(self.cnt_el0_base) + + state.sizeCells(0x1000)) + node.append(FdtPropertyWords("reg", reg)) + + return node + +class GenericTimerMem(PioDevice): + """ +System level implementation. It provides three main components: +- Memory-mapped counter module: controls the system timer through the + CNTControlBase frame, and provides its value through the CNTReadBase frame +- Memory-mapped timer control module: controls the memory-mapped timers +- Memory-mapped timers: implementations of the GenericTimer for system + peripherals + +Reference: + Arm ARM (ARM DDI 0487E.a) + I2 - System Level Implementation of the Generic Timer + """ + + type = 'GenericTimerMem' + cxx_header = "dev/arm/generic_timer.hh" + + _freq_in_dtb = False + + counter = Param.SystemCounter(Parent.any, "Global system counter") + + cnt_control_base = Param.Addr("CNTControlBase register frame base") + cnt_read_base = Param.Addr("CNTReadBase register frame base") + cnt_ctl_base = Param.Addr("CNTCTLBase register frame base") + + # Maximum of 8 timer frames + frames = VectorParam.GenericTimerFrame([], "Memory-mapped timer frames") + + def generateDeviceTree(self, state): + node = self.generateBasicPioDeviceNode(state, "timer", + self.cnt_ctl_base, 0x1000) + node.appendCompatible(["arm,armv7-timer-mem"]) + node.append(state.addrCellsProperty()) + node.append(state.sizeCellsProperty()) + node.append(FdtProperty("ranges")) + + if self._freq_in_dtb: + node.append(self.counter.unproxy(self).generateDtb()) + + for i, frame in enumerate(self.frames): + frame._frame_num = i + node.append(frame.generateDeviceTree(state)) + + yield node diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index db47ed6c82..4fabcc3bdd 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -51,6 +51,7 @@ from m5.objects.Platform import Platform from m5.objects.Terminal import Terminal from m5.objects.Uart import Uart from m5.objects.SimpleMemory import SimpleMemory +from m5.objects.GenericTimer import * from m5.objects.Gic import * from m5.objects.EnergyCtrl import EnergyCtrl from m5.objects.ClockedObject import ClockedObject @@ -437,49 +438,6 @@ class CpuLocalTimer(BasicPioDevice): int_timer = Param.ArmPPI("Interrrupt used per-cpu to GIC") int_watchdog = Param.ArmPPI("Interrupt for per-cpu watchdog to GIC") -class GenericTimer(ClockedObject): - type = 'GenericTimer' - cxx_header = "dev/arm/generic_timer.hh" - system = Param.ArmSystem(Parent.any, "system") - int_phys_s = Param.ArmPPI("Physical (S) timer interrupt") - int_phys_ns = Param.ArmPPI("Physical (NS) timer interrupt") - int_virt = Param.ArmPPI("Virtual timer interrupt") - int_hyp = Param.ArmPPI("Hypervisor timer interrupt") - - freqs = VectorParam.UInt32([0x01800000], "Frequencies available for the " - "system counter (in Hz). First element is the base frequency, " - "following are alternative lower ones which must be exact divisors") - - def generateDeviceTree(self, state): - node = FdtNode("timer") - - node.appendCompatible(["arm,cortex-a15-timer", - "arm,armv7-timer", - "arm,armv8-timer"]) - node.append(FdtPropertyWords("interrupts", [ - 1, int(self.int_phys_s.num) - 16, 0xf08, - 1, int(self.int_phys_ns.num) - 16, 0xf08, - 1, int(self.int_virt.num) - 16, 0xf08, - 1, int(self.int_hyp.num) - 16, 0xf08, - ])) - clock = state.phandle(self.clk_domain.unproxy(self)) - node.append(FdtPropertyWords("clocks", clock)) - - yield node - -class GenericTimerMem(PioDevice): - type = 'GenericTimerMem' - cxx_header = "dev/arm/generic_timer.hh" - - base = Param.Addr(0, "Base address") - - int_phys = Param.ArmSPI("Physical Interrupt") - int_virt = Param.ArmSPI("Virtual Interrupt") - - freqs = VectorParam.UInt32([0x01800000], "Frequencies available for the " - "system counter (in Hz). First element is the base frequency, " - "following are alternative lower ones which must be exact divisors") - class PL031(AmbaIntDevice): type = 'PL031' cxx_header = "dev/arm/rtc_pl031.hh" @@ -732,6 +690,7 @@ class VExpress_EMM(RealView): conf_base=0x30000000, conf_size='256MB', conf_device_bits=16, pci_pio_base=0) + sys_counter = SystemCounter() generic_timer = GenericTimer(int_phys_s=ArmPPI(num=29), int_phys_ns=ArmPPI(num=30), int_virt=ArmPPI(num=27), @@ -901,7 +860,14 @@ Memory map: 0x1c170000-0x1c17ffff: RTC 0x20000000-0x3fffffff: On-chip peripherals: + 0x2a430000-0x2a43ffff: System Counter (control) 0x2a490000-0x2a49ffff: Trusted Watchdog (SP805) + 0x2a800000-0x2a800fff: System Counter (read) + 0x2a810000-0x2a810fff: System Timer (control) + + 0x2a820000-0x2a820fff: System Timer (frame 0) + 0x2a830000-0x2a830fff: System Timer (frame 1) + 0x2b000000-0x2b00ffff: HDLCD 0x2b060000-0x2b060fff: System Watchdog (SP805) @@ -946,6 +912,8 @@ Interrupts: 47 : Reserved (Ethernet) 48 : Reserved (USB) 56 : Trusted Watchdog (SP805) + 57 : System timer0 (phys) + 58 : System timer1 (phys) 95-255: On-chip interrupt sources (we use these for gem5-specific devices, SPIs) 74 : VirtIO (gem5/FM extension) @@ -991,19 +959,29 @@ Interrupts: # Trusted Watchdog, SP805 trusted_watchdog = Sp805(pio_addr=0x2a490000, int_num=56) + sys_counter = SystemCounter() generic_timer = GenericTimer(int_phys_s=ArmPPI(num=29), int_phys_ns=ArmPPI(num=30), int_virt=ArmPPI(num=27), int_hyp=ArmPPI(num=26)) + generic_timer_mem = GenericTimerMem(cnt_control_base=0x2a430000, + cnt_read_base=0x2a800000, + cnt_ctl_base=0x2a810000, + frames=[ + GenericTimerFrame(cnt_base=0x2a820000, + int_phys=ArmSPI(num=57), int_virt=ArmSPI(num=133)), + GenericTimerFrame(cnt_base=0x2a830000, + int_phys=ArmSPI(num=58), int_virt=ArmSPI(num=134)) + ]) system_watchdog = Sp805(pio_addr=0x2b060000, int_num=130) def _on_chip_devices(self): return [ - self.generic_timer, + self.generic_timer_mem, self.trusted_watchdog, self.system_watchdog - ] + ] + self.generic_timer_mem.frames def _on_chip_memory(self): memories = [ diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript index d865c67f48..b264d05cfd 100644 --- a/src/dev/arm/SConscript +++ b/src/dev/arm/SConscript @@ -41,6 +41,7 @@ if env['TARGET_ISA'] == 'arm': SimObject('AbstractNVM.py') SimObject('Display.py') SimObject('FlashDevice.py') + SimObject('GenericTimer.py') SimObject('Gic.py') SimObject('RealView.py') SimObject('SMMUv3.py') diff --git a/src/dev/arm/generic_timer.cc b/src/dev/arm/generic_timer.cc index 33a9fc72d0..6e19a3a258 100644 --- a/src/dev/arm/generic_timer.cc +++ b/src/dev/arm/generic_timer.cc @@ -1,5 +1,5 @@ /* - * 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 @@ -37,17 +37,29 @@ #include "dev/arm/generic_timer.hh" +#include + #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" +#include "params/SystemCounter.hh" -SystemCounter::SystemCounter(std::vector &freqs) - : _freqTable(freqs), - _resetTick(0), - _regCntkctl(0) +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"); @@ -57,47 +69,180 @@ SystemCounter::SystemCounter(std::vector &freqs) "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) { - 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"); + 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(); } - _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, @@ -107,17 +252,17 @@ ArchTimer::ArchTimer(const std::string &name, _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"); @@ -138,9 +283,8 @@ ArchTimer::updateCounter() } else { _control.istatus = 0; if (scheduleEvents()) { - const auto period(_systemCounter.period()); _parent.schedule(_counterLimitReachedEvent, - curTick() + (_counterLimit - value()) * period); + whenValue(_counterLimit)); } } } @@ -161,19 +305,16 @@ ArchTimer::setTimerValue(uint32_t val) 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 @@ -189,6 +330,12 @@ ArchTimer::value() const return _systemCounter.value() - _offset; } +void +ArchTimer::notify() +{ + updateCounter(); +} + void ArchTimer::serialize(CheckpointOut &cp) const { @@ -226,12 +373,14 @@ ArchTimer::drainResume() 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); } @@ -246,25 +395,15 @@ GenericTimer::serialize(CheckpointOut &cp) const { 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. @@ -278,16 +417,10 @@ GenericTimer::unserialize(CheckpointIn &cp) 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) { @@ -318,28 +451,73 @@ GenericTimer::createTimers(unsigned cpus) } } +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: @@ -433,16 +611,13 @@ GenericTimer::readMiscReg(int reg, unsigned cpu) 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: @@ -514,6 +689,97 @@ GenericTimer::readMiscReg(int reg, unsigned cpu) } } +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) @@ -530,341 +796,734 @@ GenericTimerISA::readMiscReg(int reg) 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(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 -GenericTimerMem::serialize(CheckpointOut &cp) const +GenericTimerFrame::serialize(CheckpointOut &cp) const { - paramOut(cp, "timer_count", 1); - - systemCounter.serializeSection(cp, "sys_counter"); - - physTimer.serializeSection(cp, "phys_timer0"); - virtTimer.serializeSection(cp, "virt_timer0"); + physTimer.serializeSection(cp, "phys_timer"); + virtTimer.serializeSection(cp, "virt_timer"); + SERIALIZE_SCALAR(accessBits); + if (hasEl0View()) + SERIALIZE_SCALAR(accessBitsEl0); + SERIALIZE_SCALAR(nonSecureAccess); } void -GenericTimerMem::unserialize(CheckpointIn &cp) +GenericTimerFrame::unserialize(CheckpointIn &cp) { - systemCounter.unserializeSection(cp, "sys_counter"); + physTimer.unserializeSection(cp, "phys_timer"); + virtTimer.unserializeSection(cp, "virt_timer"); + UNSERIALIZE_SCALAR(accessBits); + if (hasEl0View()) + UNSERIALIZE_SCALAR(accessBitsEl0); + UNSERIALIZE_SCALAR(nonSecureAccess); +} - 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"); +uint64_t +GenericTimerFrame::getVirtOffset() const +{ + return virtTimer.offset(); +} - physTimer.unserializeSection(cp, "phys_timer0"); - virtTimer.unserializeSection(cp, "virt_timer0"); +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, LittleEndianByteOrder); + 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(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("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(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(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() % 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"); +} + +bool +GenericTimerMem::validateAccessPerm(ArmSystem &sys, bool is_sec) +{ + return !sys.haveSecurity() || is_sec; +} + +AddrRangeList +GenericTimerMem::getAddrRanges() const +{ + return addrRanges; } Tick GenericTimerMem::read(PacketPtr pkt) { - const unsigned size(pkt->getSize()); - const Addr addr(pkt->getAddr()); - uint64_t value; + 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(); - if (ctrlRange.contains(addr)) { - value = ctrlRead(addr - ctrlRange.start(), size); - } else if (timerRange.contains(addr)) { - value = timerRead(addr - timerRange.start(), size); - } else { - panic("Invalid address: 0x%x\n", addr); - } - - DPRINTF(Timer, "Read 0x%x <- 0x%x(%i)\n", value, addr, size); - - if (size == 8) { - pkt->setLE(value); - } else if (size == 4) { - pkt->setLE(value); - } else { - panic("Unexpected access size: %i\n", size); - } - return 0; } Tick GenericTimerMem::write(PacketPtr pkt) { - const unsigned size(pkt->getSize()); - if (size != 8 && size != 4) - panic("Unexpected access size\n"); + const Addr addr = pkt->getAddr(); + const 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 Addr addr(pkt->getAddr()); - const uint64_t value(size == 8 ? - pkt->getLE() : pkt->getLE()); + 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, "Write 0x%x -> 0x%x(%i)\n", value, addr, size); - if (ctrlRange.contains(addr)) { - ctrlWrite(addr - ctrlRange.start(), size, value); - } else if (timerRange.contains(addr)) { - timerWrite(addr - timerRange.start(), size, value); - } else { - panic("Invalid address: 0x%x\n", addr); - } + DPRINTF(Timer, "GenericTimerMem::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 +GenericTimerMem::counterCtrlRead(Addr addr, size_t size, bool is_sec) const { - if (size == 4) { - switch (addr) { - case CTRL_CNTFRQ: - return systemCounter.freq(); - - case CTRL_CNTTIDR: - return 0x3; // Frame 0 implemented with virtual timers - - case CTRL_CNTNSAR: - case CTRL_CNTACR_BASE: - warn("Reading from unimplemented control register (0x%x)\n", addr); - return 0; - - case CTRL_CNTVOFF_LO_BASE: - return virtTimer.offset(); - - case CTRL_CNTVOFF_HI_BASE: - return virtTimer.offset() >> 32; - - default: - warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); - return 0; + 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 if (size == 8) { - switch (addr) { - case CTRL_CNTVOFF_LO_BASE: - return virtTimer.offset(); - - default: - warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); - return 0; - } - } else { - panic("Invalid access size: %i\n", size); + warn("GenericTimerMem::counterCtrlRead: Unexpected address " + "(0x%x:%i), assuming RAZ\n", addr, size); + return 0; + } } } void -GenericTimerMem::ctrlWrite(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 CTRL_CNTFRQ: - case CTRL_CNTNSAR: - case CTRL_CNTTIDR: - case CTRL_CNTACR_BASE: - warn("Write to unimplemented control register (0x%x)\n", addr); - return; + if (!GenericTimerMem::validateAccessPerm(system, is_sec)) + return; - case CTRL_CNTVOFF_LO_BASE: - virtTimer.setOffset( - insertBits(virtTimer.offset(), 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(); - case CTRL_CNTVOFF_HI_BASE: - virtTimer.setOffset( - insertBits(virtTimer.offset(), 63, 32, value)); - return; + 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; + } - default: - warn("Ignoring write to unexpected address (0x%x:%i)\n", - addr, size); - 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; + } } - } else if (size == 8) { - switch (addr) { - case CTRL_CNTVOFF_LO_BASE: - virtTimer.setOffset(value); - return; - - default: - warn("Ignoring write to unexpected address (0x%x:%i)\n", - addr, size); - return; - } - } else { - panic("Invalid access size: %i\n", size); + warn("GenericTimerMem::counterCtrlWrite: Unexpected address " + "(0x%x:%i), assuming WI\n", addr, size); + } } } uint64_t -GenericTimerMem::timerRead(Addr addr, size_t size) const +GenericTimerMem::counterStatusRead(Addr addr, size_t size) const { - if (size == 4) { - switch (addr) { - case TIMER_CNTPCT_LO: - return physTimer.value(); - - case TIMER_CNTPCT_HI: - return physTimer.value() >> 32; - - case TIMER_CNTVCT_LO: - return virtTimer.value(); - - case TIMER_CNTVCT_HI: - return virtTimer.value() >> 32; - - case TIMER_CNTFRQ: - return systemCounter.freq(); - - case TIMER_CNTEL0ACR: - warn("Read from unimplemented timer register (0x%x)\n", addr); - return 0; - - case CTRL_CNTVOFF_LO_BASE: - return virtTimer.offset(); - - case CTRL_CNTVOFF_HI_BASE: - return virtTimer.offset() >> 32; - - case TIMER_CNTP_CVAL_LO: - return physTimer.compareValue(); - - case TIMER_CNTP_CVAL_HI: - return physTimer.compareValue() >> 32; - - case TIMER_CNTP_TVAL: - return physTimer.timerValue(); - - case TIMER_CNTP_CTL: - return physTimer.control(); - - case TIMER_CNTV_CVAL_LO: - return virtTimer.compareValue(); - - case TIMER_CNTV_CVAL_HI: - return virtTimer.compareValue() >> 32; - - case TIMER_CNTV_TVAL: - return virtTimer.timerValue(); - - case TIMER_CNTV_CTL: - return virtTimer.control(); - - default: - warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); - return 0; - } - } else if (size == 8) { - switch (addr) { - case TIMER_CNTPCT_LO: - return physTimer.value(); - - case TIMER_CNTVCT_LO: - return virtTimer.value(); - - case CTRL_CNTVOFF_LO_BASE: - return virtTimer.offset(); - - case TIMER_CNTP_CVAL_LO: - return physTimer.compareValue(); - - case TIMER_CNTV_CVAL_LO: - return virtTimer.compareValue(); - - default: - warn("Unexpected address (0x%x:%i), assuming RAZ\n", addr, size); - return 0; - } - } else { - panic("Invalid access size: %i\n", size); + 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::timerWrite(Addr addr, size_t size, uint64_t value) +GenericTimerMem::counterStatusWrite(Addr addr, size_t size, uint64_t data) { - if (size == 4) { - switch (addr) { - case TIMER_CNTEL0ACR: - warn("Unimplemented timer register (0x%x)\n", addr); - return; - - case TIMER_CNTP_CVAL_LO: - physTimer.setCompareValue( - insertBits(physTimer.compareValue(), 31, 0, value)); - return; - - case TIMER_CNTP_CVAL_HI: - physTimer.setCompareValue( - insertBits(physTimer.compareValue(), 63, 32, value)); - return; - - case TIMER_CNTP_TVAL: - physTimer.setTimerValue(value); - return; - - case TIMER_CNTP_CTL: - physTimer.setControl(value); - return; - - case TIMER_CNTV_CVAL_LO: - virtTimer.setCompareValue( - insertBits(virtTimer.compareValue(), 31, 0, value)); - return; - - case TIMER_CNTV_CVAL_HI: - virtTimer.setCompareValue( - insertBits(virtTimer.compareValue(), 63, 32, value)); - return; - - case TIMER_CNTV_TVAL: - virtTimer.setTimerValue(value); - return; - - case TIMER_CNTV_CTL: - virtTimer.setControl(value); - return; - - default: - warn("Unexpected address (0x%x:%i), ignoring write\n", addr, size); - return; - } - } else if (size == 8) { - switch (addr) { - case TIMER_CNTP_CVAL_LO: - return physTimer.setCompareValue(value); - - case TIMER_CNTV_CVAL_LO: - return virtTimer.setCompareValue(value); - - default: - warn("Unexpected address (0x%x:%i), ignoring write\n", addr, size); - return; - } - } else { - panic("Invalid access size: %i\n", size); + 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); + } +} + +SystemCounter * +SystemCounterParams::create() +{ + return new SystemCounter(this); +} + GenericTimer * GenericTimerParams::create() { return new GenericTimer(this); } +GenericTimerFrame * +GenericTimerFrameParams::create() +{ + return new GenericTimerFrame(this); +} + GenericTimerMem * GenericTimerMemParams::create() { diff --git a/src/dev/arm/generic_timer.hh b/src/dev/arm/generic_timer.hh index 8da0467a02..b74b19a16d 100644 --- a/src/dev/arm/generic_timer.hh +++ b/src/dev/arm/generic_timer.hh @@ -1,5 +1,5 @@ /* - * 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 @@ -40,67 +40,106 @@ #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 _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 _listeners; /// Maximum architectural number of frequency table entries static constexpr size_t MAX_FREQ_ENTRIES = 1004; public: - SystemCounter(std::vector &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& 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(); - void setHypControl(uint32_t val) { _regCnthctl = val; } - uint32_t getHypControl() { return _regCnthctl; } + /// 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); + + /// 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; @@ -108,10 +147,25 @@ class SystemCounter : public Serializable 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. @@ -178,6 +232,11 @@ class ArchTimer : public Serializable, public Drainable /// 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; @@ -214,12 +273,12 @@ class ArchTimerKvm : public ArchTimer } }; -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; @@ -229,11 +288,15 @@ class GenericTimer : public ClockedObject 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), @@ -250,8 +313,33 @@ class GenericTimer : public ClockedObject _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; @@ -263,6 +351,39 @@ class GenericTimer : public ClockedObject 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); @@ -271,8 +392,8 @@ class GenericTimer : public ClockedObject 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> timers; @@ -280,6 +401,9 @@ class GenericTimer : public ClockedObject 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 @@ -296,61 +420,190 @@ 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; + AddrRange timerEl0Range; - protected: - /// System counter. - SystemCounter systemCounter; + 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 frames; + + ArmSystem &system; }; #endif // __DEV_ARM_GENERIC_TIMER_HH__ diff --git a/src/dev/arm/generic_timer_miscregs_types.hh b/src/dev/arm/generic_timer_miscregs_types.hh new file mode 100644 index 0000000000..98c9f2f36f --- /dev/null +++ b/src/dev/arm/generic_timer_miscregs_types.hh @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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. + */ + +#ifndef __DEV_ARM_GENERIC_TIMER_MISCREGS_TYPES_HH__ +#define __DEV_ARM_GENERIC_TIMER_MISCREGS_TYPES_HH__ + +#include "base/bitunion.hh" + +namespace ArmISA +{ + BitUnion64(CNTKCTL) + Bitfield<9> el0pten; + Bitfield<8> el0vten; + Bitfield<7,4> evnti; + Bitfield<3> evntdir; + Bitfield<2> evnten; + Bitfield<1> el0vcten; + Bitfield<0> el0pcten; + EndBitUnion(CNTKCTL) + + BitUnion64(CNTHCTL) + Bitfield<7,4> evnti; + Bitfield<3> evntdir; + Bitfield<2> evnten; + Bitfield<1> el1pcen; + Bitfield<0> el1pcten; + EndBitUnion(CNTHCTL) + // If Armv8.1-VHE && HCR_EL2.E2H == 1 + BitUnion64(CNTHCTL_E2H) + Bitfield<11> el1pten; + Bitfield<10> el1pcten; + Bitfield<9> el0pten; + Bitfield<8> el0vten; + Bitfield<7,4> evnti; + Bitfield<3> evntdir; + Bitfield<2> evnten; + Bitfield<1> el0vcten; + Bitfield<0> el0pcten; + EndBitUnion(CNTHCTL_E2H) +} + +#endif // __DEV_ARM_GENERIC_TIMER_MISCREGS_TYPES_HH__