diff options
author | Andreas Sandberg <andreas.sandberg@arm.com> | 2015-05-23 13:46:53 +0100 |
---|---|---|
committer | Andreas Sandberg <andreas.sandberg@arm.com> | 2015-05-23 13:46:53 +0100 |
commit | 2278fec1d1064ec1622098153b20a773fb688e78 (patch) | |
tree | fcb7441fb487261ba3e9465fa30ad856e286f7ca /src/dev/arm/generic_timer.cc | |
parent | 65f3f097d3c270d2f28fc7d55651afaefb56ceed (diff) | |
download | gem5-2278fec1d1064ec1622098153b20a773fb688e78.tar.xz |
dev, arm: Add virtual timers to the generic timer model
The generic timer model currently does not support virtual
counters. Virtual and physical counters both tick with the same
frequency. However, virtual timers allow a hypervisor to set an offset
that is subtracted from the counter when it is read. This enables the
hypervisor to present a time base that ticks with virtual time in the
VM (i.e., doesn't tick when the VM isn't running). Modern Linux
kernels generally assume that virtual counters exist and try to use
them by default.
Diffstat (limited to 'src/dev/arm/generic_timer.cc')
-rw-r--r-- | src/dev/arm/generic_timer.cc | 50 |
1 files changed, 41 insertions, 9 deletions
diff --git a/src/dev/arm/generic_timer.cc b/src/dev/arm/generic_timer.cc index 642205704..1ff91d50b 100644 --- a/src/dev/arm/generic_timer.cc +++ b/src/dev/arm/generic_timer.cc @@ -93,7 +93,7 @@ ArchTimer::ArchTimer(const std::string &name, const Interrupt &interrupt) : _name(name), _parent(parent), _systemCounter(sysctr), _interrupt(interrupt), - _control(0), _counterLimit(0), + _control(0), _counterLimit(0), _offset(0), _counterLimitReachedEvent(this) { } @@ -159,10 +159,17 @@ ArchTimer::setControl(uint32_t val) _control.imask = new_ctl.imask; } +void +ArchTimer::setOffset(uint64_t val) +{ + _offset = val; + updateCounter(); +} + uint64_t ArchTimer::value() const { - return _systemCounter.value(); + return _systemCounter.value() - _offset; } void @@ -170,6 +177,7 @@ ArchTimer::serialize(std::ostream &os) const { paramOut(os, "control_serial", _control); SERIALIZE_SCALAR(_counterLimit); + SERIALIZE_SCALAR(_offset); const bool event_scheduled(_counterLimitReachedEvent.scheduled()); SERIALIZE_SCALAR(event_scheduled); @@ -184,6 +192,11 @@ ArchTimer::unserialize(Checkpoint *cp, const std::string §ion) { paramIn(cp, section, "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; bool event_scheduled; UNSERIALIZE_SCALAR(event_scheduled); if (event_scheduled) { @@ -218,7 +231,8 @@ ArchTimer::Interrupt::clear() GenericTimer::GenericTimer(GenericTimerParams *p) : SimObject(p), gic(p->gic), - irqPhys(p->int_phys) + irqPhys(p->int_phys), + irqVirt(p->int_virt) { dynamic_cast<ArmSystem &>(*p->system).setGenericTimer(this); } @@ -236,6 +250,9 @@ GenericTimer::serialize(std::ostream &os) nameOut(os, core.phys.name()); core.phys.serialize(os); + + nameOut(os, core.virt.name()); + core.virt.serialize(os); } } @@ -260,6 +277,7 @@ GenericTimer::unserialize(Checkpoint *cp, const std::string §ion) // This should really be phys_timerN, but we are stuck with // arch_timer for backwards compatibility. core.phys.unserialize(cp, csprintf("%s.arch_timer%d", section, i)); + core.virt.unserialize(cp, csprintf("%s.virt_timer%d", section, i)); } } @@ -282,7 +300,7 @@ GenericTimer::createTimers(unsigned cpus) timers.resize(cpus); for (unsigned i = old_cpu_count; i < cpus; ++i) { timers[i].reset( - new CoreTimers(*this, i, irqPhys)); + new CoreTimers(*this, i, irqPhys, irqVirt)); } } @@ -334,13 +352,23 @@ GenericTimer::setMiscReg(int reg, unsigned cpu, MiscReg val) // 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: - /* FALLTHROUGH */ + core.virt.setControl(val); + return; // PL1 phys. timer, secure case MISCREG_CNTP_CTL_S: @@ -405,19 +433,23 @@ GenericTimer::readMiscReg(int reg, unsigned cpu) // Virtual timer case MISCREG_CNTVCT: case MISCREG_CNTVCT_EL0: - warn_once("Virtual timer not implemented, " - "returning physical timer value\n"); - return core.phys.value(); + 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: - /* FALLTHROUGH */ + return core.virt.control(); // PL1 phys. timer, secure case MISCREG_CNTP_CTL_S: |