dev,arm: add Kvm mode of operation for CP15 timer
authorCurtis Dunham <Curtis.Dunham@arm.com>
Mon, 22 May 2017 19:22:14 +0000 (19:22 +0000)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Wed, 5 Jul 2017 14:24:03 +0000 (14:24 +0000)
The timer device exposed via the ARM ISA, also known as the
"CP15 timer" due to its legacy coprocessor encodings, is
implemented by the GenericTimerISA class.  During Kvm
execution, however, this functionality is directly emulated
by the hardware.

This commit subclasses the GenericTimer, which is (solely)
used by GenericTimerISA, to facilitate Kvm in much the same
way as the prior GIC changes: the gem5 model is used as the
backing store for state, so checkpointing and CPU switching
work correctly, but isn't used during Kvm execution.

The added indirection prevents the timer device from creating
events when we're just updating its state, but not actually
using it for simulation.

Change-Id: I427540d11ccf049c334afe318f575146aa888672
Reviewed-on: https://gem5-review.googlesource.com/3542
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>

src/dev/arm/generic_timer.cc
src/dev/arm/generic_timer.hh

index 48f70e135346121a362ed153928670af918c6c6f..6332b8f4d54e948b4ddb5806248f5ac3a41650a9 100644 (file)
@@ -109,8 +109,12 @@ ArchTimer::counterLimitReached()
 
     DPRINTF(Timer, "Counter limit reached\n");
     if (!_control.imask) {
-        DPRINTF(Timer, "Causing interrupt\n");
-        _interrupt.send();
+        if (scheduleEvents()) {
+            DPRINTF(Timer, "Causing interrupt\n");
+            _interrupt.send();
+        } else {
+            DPRINTF(Timer, "Kvm mode; skipping simulated interrupt\n");
+        }
     }
 }
 
@@ -122,10 +126,12 @@ ArchTimer::updateCounter()
     if (value() >= _counterLimit) {
         counterLimitReached();
     } else {
-        const auto period(_systemCounter.period());
         _control.istatus = 0;
-        _parent.schedule(_counterLimitReachedEvent,
-             curTick() + (_counterLimit - value()) * period);
+        if (scheduleEvents()) {
+            const auto period(_systemCounter.period());
+            _parent.schedule(_counterLimitReachedEvent,
+                 curTick() + (_counterLimit - value()) * period);
+        }
     }
 }
 
@@ -234,12 +240,13 @@ ArchTimer::Interrupt::clear()
 
 GenericTimer::GenericTimer(GenericTimerParams *p)
     : SimObject(p),
+      system(*p->system),
       gic(p->gic),
       irqPhys(p->int_phys),
       irqVirt(p->int_virt)
 {
     fatal_if(!p->system, "No system specified, can't instantiate timer.\n");
-    p->system->setGenericTimer(this);
+    system.setGenericTimer(this);
 }
 
 void
@@ -303,7 +310,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, irqVirt));
+            new CoreTimers(*this, system, i, irqPhys, irqVirt));
     }
 }
 
index 3eec1d42e22704ea248913149ed8d26ef1d836cd..ccfb1277fe41d7636be156c094509d21ab6dca5d 100644 (file)
@@ -42,6 +42,7 @@
 #define __DEV_ARM_GENERIC_TIMER_HH__
 
 #include "arch/arm/isa_device.hh"
+#include "arch/arm/system.hh"
 #include "base/bitunion.hh"
 #include "dev/arm/base_gic.hh"
 #include "sim/core.hh"
@@ -159,6 +160,8 @@ class ArchTimer : public Serializable, public Drainable
     void counterLimitReached();
     EventFunctionWrapper _counterLimitReachedEvent;
 
+    virtual bool scheduleEvents() { return true; }
+
   public:
     ArchTimer(const std::string &name,
               SimObject &parent,
@@ -201,6 +204,28 @@ class ArchTimer : public Serializable, public Drainable
     ArchTimer(const ArchTimer &t);
 };
 
+class ArchTimerKvm : public ArchTimer
+{
+  private:
+    ArmSystem &system;
+
+  public:
+    ArchTimerKvm(const std::string &name,
+                 ArmSystem &system,
+                 SimObject &parent,
+                 SystemCounter &sysctr,
+                 const Interrupt &interrupt)
+      : ArchTimer(name, parent, sysctr, interrupt), system(system) {}
+
+  protected:
+    // For ArchTimer's in a GenericTimerISA with Kvm execution about
+    // to begin, skip rescheduling the event.
+    // Otherwise, we should reschedule the event (if necessary).
+    bool scheduleEvents() override {
+        return !system.validKvmEnvironment();
+    }
+};
+
 class GenericTimer : public SimObject
 {
   public:
@@ -215,25 +240,25 @@ class GenericTimer : public SimObject
 
   protected:
     struct CoreTimers {
-        CoreTimers(GenericTimer &parent, unsigned cpu,
+        CoreTimers(GenericTimer &parent, ArmSystem &system, unsigned cpu,
                    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,
+                   system, parent, parent.systemCounter,
                    irqPhys),
               virt(csprintf("%s.virt_timer%d", parent.name(), cpu),
-                   parent, parent.systemCounter,
+                   system, parent, parent.systemCounter,
                    irqVirt)
         {}
 
         ArchTimer::Interrupt irqPhys;
         ArchTimer::Interrupt irqVirt;
 
-        ArchTimer phys;
-        ArchTimer virt;
+        ArchTimerKvm phys;
+        ArchTimerKvm virt;
 
       private:
         // Disable copying
@@ -250,6 +275,9 @@ class GenericTimer : public SimObject
     std::vector<std::unique_ptr<CoreTimers>> timers;
 
   protected: // Configuration
+    /// ARM system containing this timer
+    ArmSystem &system;
+
     /// Pointer to the GIC, needed to trigger timer interrupts.
     BaseGic *const gic;