dev, arm: Add virtual timers to the generic timer model
authorAndreas Sandberg <andreas.sandberg@arm.com>
Sat, 23 May 2015 12:46:53 +0000 (13:46 +0100)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Sat, 23 May 2015 12:46:53 +0000 (13:46 +0100)
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.

src/arch/arm/miscregs.cc
src/dev/arm/RealView.py
src/dev/arm/generic_timer.cc
src/dev/arm/generic_timer.hh

index 07d938f6aabac979e5d7f5f4ed6e8ba3cb338d0f..729cb4e8bd57afddd61a4730d5801449bc28002b 100644 (file)
@@ -715,9 +715,9 @@ bitset<NUM_MISCREG_INFOS> miscRegInfo[NUM_MISCREGS] = {
     // MISCREG_CNTP_CTL_S
     bitset<NUM_MISCREG_INFOS>(string("00110011001111100000")),
     // MISCREG_CNTV_TVAL
-    bitset<NUM_MISCREG_INFOS>(string("01111000000000000000")),
+    bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_CNTV_CTL
-    bitset<NUM_MISCREG_INFOS>(string("01111000000000000000")),
+    bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_CNTHCTL
     bitset<NUM_MISCREG_INFOS>(string("01001000000000000000")),
     // MISCREG_CNTHP_TVAL
@@ -763,7 +763,7 @@ bitset<NUM_MISCREG_INFOS> miscRegInfo[NUM_MISCREGS] = {
     // MISCREG_CNTP_CVAL_S
     bitset<NUM_MISCREG_INFOS>(string("00110011001111100000")),
     // MISCREG_CNTV_CVAL
-    bitset<NUM_MISCREG_INFOS>(string("01111000000000000000")),
+    bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_CNTVOFF
     bitset<NUM_MISCREG_INFOS>(string("11001100000000000001")),
     // MISCREG_CNTHP_CVAL
@@ -1259,11 +1259,11 @@ bitset<NUM_MISCREG_INFOS> miscRegInfo[NUM_MISCREGS] = {
     // MISCREG_CNTP_CVAL_EL0
     bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_CNTV_TVAL_EL0
-    bitset<NUM_MISCREG_INFOS>(string("01111000000000000000")),
+    bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_CNTV_CTL_EL0
-    bitset<NUM_MISCREG_INFOS>(string("01111000000000000000")),
+    bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_CNTV_CVAL_EL0
-    bitset<NUM_MISCREG_INFOS>(string("01111000000000000000")),
+    bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_PMEVCNTR0_EL0
     bitset<NUM_MISCREG_INFOS>(string("11111111111111000001")),
     // MISCREG_PMEVCNTR1_EL0
index 2e58c8fa6fbf0dca98d2e7157353245aaf8e50ad..2d5ccc3f9e697509e87d50e6e3578b2f6962f26b 100644 (file)
@@ -136,9 +136,10 @@ class GenericTimer(SimObject):
     cxx_header = "dev/arm/generic_timer.hh"
     system = Param.System(Parent.any, "system")
     gic = Param.BaseGic(Parent.any, "GIC to use for interrupting")
-    int_phys = Param.UInt32("Interrupt number used per-cpu to GIC")
-    # @todo: for now only one timer per CPU is supported, which is the
-    # normal behaviour when Security and Virt. extensions are disabled.
+    # @todo: for now only two timers per CPU is supported, which is the
+    # normal behaviour when security extensions are disabled.
+    int_phys = Param.UInt32("Physical timer interrupt number")
+    int_virt = Param.UInt32("Virtual timer interrupt number")
 
 class PL031(AmbaIntDevice):
     type = 'PL031'
@@ -457,7 +458,7 @@ class VExpress_EMM(RealView):
                                idreg=0x02250000, pio_addr=0x1C010000)
     gic = Pl390(dist_addr=0x2C001000, cpu_addr=0x2C002000)
     local_cpu_timer = CpuLocalTimer(int_num_timer=29, int_num_watchdog=30, pio_addr=0x2C080000)
-    generic_timer = GenericTimer(int_phys=29)
+    generic_timer = GenericTimer(int_phys=29, int_virt=27)
     timer0 = Sp804(int_num0=34, int_num1=34, pio_addr=0x1C110000, clock0='1MHz', clock1='1MHz')
     timer1 = Sp804(int_num0=35, int_num1=35, pio_addr=0x1C120000, clock0='1MHz', clock1='1MHz')
     clcd   = Pl111(pio_addr=0x1c1f0000, int_num=46)
index 64220570437e746bf44bb66af611b7bf8cfa053b..1ff91d50b16603974bd0f8df660911bb4e13b1aa 100644 (file)
@@ -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 &section)
 {
     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 &section)
         // 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:
index 8dc921275eef8596b2bace925d7e6332418a6ad6..7e6bd2af35083feb47cb26804b0a8731777271c4 100644 (file)
@@ -145,6 +145,8 @@ class ArchTimer
     ArchTimerCtrl _control;
     /// Programmed limit value for the upcounter ({CNTP/CNTHP/CNTV}_CVAL).
     uint64_t _counterLimit;
+    /// Offset relative to the physical timer (CNTVOFF)
+    uint64_t _offset;
 
     /**
      * Timer settings or the offset has changed, re-evaluate
@@ -180,6 +182,9 @@ class ArchTimer
     uint32_t control() const { return _control; }
     void setControl(uint32_t val);
 
+    uint64_t offset() const { return _offset; }
+    void setOffset(uint64_t val);
+
     /// Returns the value of the counter which this timer relies on.
     uint64_t value() const;
 
@@ -206,17 +211,24 @@ class GenericTimer : public SimObject
   protected:
     struct CoreTimers {
         CoreTimers(GenericTimer &parent, unsigned cpu,
-                   unsigned _irqPhys)
+                   unsigned _irqPhys, unsigned _irqVirt)
             : irqPhys(*parent.gic, _irqPhys, cpu),
+              irqVirt(*parent.gic, _irqVirt, cpu),
               // This should really be phys_timerN, but we are stuck with
               // arch_timer for backwards compatibility.
               phys(csprintf("%s.arch_timer%d", parent.name(), cpu),
                    parent, parent.systemCounter,
-                   irqPhys)
+                   irqPhys),
+              virt(csprintf("%s.virt_timer%d", parent.name(), cpu),
+                   parent, parent.systemCounter,
+                   irqVirt)
         {}
 
         ArchTimer::Interrupt irqPhys;
+        ArchTimer::Interrupt irqVirt;
+
         ArchTimer phys;
+        ArchTimer virt;
 
       private:
         // Disable copying
@@ -238,6 +250,9 @@ class GenericTimer : public SimObject
 
     /// Physical timer interrupt
     const unsigned irqPhys;
+
+    /// Virtual timer interrupt
+    const unsigned irqVirt;
 };
 
 class GenericTimerISA : public ArmISA::BaseISADevice