cpu: Turn the stage 2 ARM MMUs from params to children.
[gem5.git] / src / cpu / kvm / base.hh
index 2e3ee551bc6ce0c48e33e01b72924cc2799b90d4..7bf518f04dd94bf73ab1e487368b28472a02649f 100644 (file)
 #ifndef __CPU_KVM_BASE_HH__
 #define __CPU_KVM_BASE_HH__
 
-#include <memory>
+#include <pthread.h>
+
 #include <csignal>
+#include <memory>
+#include <queue>
 
 #include "base/statistics.hh"
 #include "cpu/kvm/perfevent.hh"
@@ -50,8 +53,8 @@
 #include "cpu/base.hh"
 #include "cpu/simple_thread.hh"
 
-/** Signal to use to trigger time-based exits from KVM */
-#define KVM_TIMER_SIGNAL SIGRTMIN
+/** Signal to use to trigger exits from KVM */
+#define KVM_KICK_SIGNAL SIGRTMIN
 
 // forward declarations
 class ThreadContext;
@@ -78,38 +81,53 @@ class BaseKvmCPU : public BaseCPU
     BaseKvmCPU(BaseKvmCPUParams *params);
     virtual ~BaseKvmCPU();
 
-    void init();
-    void startup();
-    void regStats();
+    void init() override;
+    void startup() override;
+    void regStats() override;
 
-    void serializeThread(std::ostream &os, ThreadID tid);
-    void unserializeThread(Checkpoint *cp, const std::string &section,
-                           ThreadID tid);
+    void serializeThread(CheckpointOut &cp, ThreadID tid) const override;
+    void unserializeThread(CheckpointIn &cp, ThreadID tid) override;
 
-    unsigned int drain(DrainManager *dm);
-    void drainResume();
+    DrainState drain() override;
+    void drainResume() override;
+    void notifyFork() override;
 
-    void switchOut();
-    void takeOverFrom(BaseCPU *cpu);
+    void switchOut() override;
+    void takeOverFrom(BaseCPU *cpu) override;
 
-    void verifyMemoryMode() const;
+    void verifyMemoryMode() const override;
 
-    MasterPort &getDataPort() { return dataPort; }
-    MasterPort &getInstPort() { return instPort; }
+    Port &getDataPort() override { return dataPort; }
+    Port &getInstPort() override { return instPort; }
 
-    void wakeup();
-    void activateContext(ThreadID thread_num, Cycles delay);
-    void suspendContext(ThreadID thread_num);
+    void wakeup(ThreadID tid = 0) override;
+    void activateContext(ThreadID thread_num) override;
+    void suspendContext(ThreadID thread_num) override;
     void deallocateContext(ThreadID thread_num);
-    void haltContext(ThreadID thread_num);
+    void haltContext(ThreadID thread_num) override;
 
-    ThreadContext *getContext(int tn);
+    long getVCpuID() const { return vcpuID; }
+    ThreadContext *getContext(int tn) override;
 
-    Counter totalInsts() const;
-    Counter totalOps() const;
+    Counter totalInsts() const override;
+    Counter totalOps() const override;
+
+    /**
+     * Callback from KvmCPUPort to transition the CPU out of RunningMMIOPending
+     * when all timing requests have completed.
+     */
+    void finishMMIOPending();
 
     /** Dump the internal state to the terminal. */
-    virtual void dump();
+    virtual void dump() const;
+
+    /**
+     * Force an exit from KVM.
+     *
+     * Send a signal to the thread owning this vCPU to get it to exit
+     * from KVM. Ignored if the vCPU is not executing.
+     */
+    void kick() const { pthread_kill(vcpuThread, KVM_KICK_SIGNAL); }
 
     /**
      * A cached copy of a thread's state in the form of a SimpleThread
@@ -142,6 +160,7 @@ class BaseKvmCPU : public BaseCPU
      *     Running;
      *     RunningService;
      *     RunningServiceCompletion;
+     *     RunningMMIOPending;
      *
      *     Idle -> Idle;
      *     Idle -> Running [label="activateContext()", URL="\ref activateContext"];
@@ -151,6 +170,8 @@ class BaseKvmCPU : public BaseCPU
      *     Running -> Idle [label="drain()", URL="\ref drain"];
      *     Idle -> Running [label="drainResume()", URL="\ref drainResume"];
      *     RunningService -> RunningServiceCompletion [label="handleKvmExit()", URL="\ref handleKvmExit"];
+     *     RunningService -> RunningMMIOPending [label="handleKvmExit()", URL="\ref handleKvmExit"];
+     *     RunningMMIOPending -> RunningServiceCompletion [label="finishMMIOPending()", URL="\ref finishMMIOPending"];
      *     RunningServiceCompletion -> Running [label="tick()", URL="\ref tick"];
      *     RunningServiceCompletion -> RunningService [label="tick()", URL="\ref tick"];
      *   }
@@ -180,12 +201,21 @@ class BaseKvmCPU : public BaseCPU
          * after running service is determined in handleKvmExit() and
          * depends on what kind of service the guest requested:
          * <ul>
-         *   <li>IO/MMIO: RunningServiceCompletion
+         *   <li>IO/MMIO (Atomic): RunningServiceCompletion
+         *   <li>IO/MMIO (Timing): RunningMMIOPending
          *   <li>Halt: Idle
          *   <li>Others: Running
          * </ul>
          */
         RunningService,
+        /** Timing MMIO request in flight or stalled.
+         *
+         *  The VM has requested IO/MMIO and we are in timing mode.  A timing
+         *  request is either stalled (and will be retried with recvReqRetry())
+         *  or it is in flight.  After the timing request is complete, the CPU
+         *  will transition to the RunningServiceCompletion state.
+         */
+        RunningMMIOPending,
         /** Service completion in progress.
          *
          * The VM has requested service that requires KVM to be
@@ -238,6 +268,11 @@ class BaseKvmCPU : public BaseCPU
      * make sure that the KVM state is synchronized and that the TC is
      * invalidated after entering KVM.
      *
+     * @note This method does not normally cause any state
+     * transitions. However, if it may suspend the CPU by suspending
+     * the thread, which leads to a transition to the Idle state. In
+     * such a case, kvm <i>must not</i> be entered.
+     *
      * @param ticks Number of ticks to execute, set to 0 to exit
      * immediately after finishing pending operations.
      * @return Number of ticks executed (see note)
@@ -383,6 +418,16 @@ class BaseKvmCPU : public BaseCPU
      */
     void syncThreadContext();
 
+    /**
+     * Get a pointer to the event queue owning devices.
+     *
+     * Devices always live in a separate device event queue when
+     * running in multi-core mode. We need to temporarily migrate to
+     * this queue when accessing devices. By convention, devices and
+     * the VM use the same event queue.
+     */
+    EventQueue *deviceEventQueue() { return vm.eventQueue(); }
+
     /**
      * Update the KVM if the thread context is dirty.
      */
@@ -528,28 +573,39 @@ class BaseKvmCPU : public BaseCPU
 
 
     /**
-     * KVM memory port. Uses the default MasterPort behavior, but
-     * panics on timing accesses.
+     * KVM memory port.  Uses default MasterPort behavior and provides an
+     * interface for KVM to transparently submit atomic or timing requests.
      */
     class KVMCpuPort : public MasterPort
     {
 
       public:
         KVMCpuPort(const std::string &_name, BaseKvmCPU *_cpu)
-            : MasterPort(_name, _cpu)
+            : MasterPort(_name, _cpu), cpu(_cpu), activeMMIOReqs(0)
         { }
+        /**
+         * Interface to send Atomic or Timing IO request.  Assumes that the pkt
+         * and corresponding req have been dynamically allocated and deletes
+         * them both if the system is in atomic mode.
+         */
+        Tick submitIO(PacketPtr pkt);
+
+        /** Returns next valid state after one or more IO accesses */
+        Status nextIOState() const;
 
       protected:
-        bool recvTimingResp(PacketPtr pkt)
-        {
-            panic("The KVM CPU doesn't expect recvTimingResp!\n");
-            return true;
-        }
+        /** KVM cpu pointer for finishMMIOPending() callback */
+        BaseKvmCPU *cpu;
+
+        /** Pending MMIO packets */
+        std::queue<PacketPtr> pendingMMIOPkts;
+
+        /** Number of MMIO requests in flight */
+        unsigned int activeMMIOReqs;
 
-        void recvRetry()
-        {
-            panic("The KVM CPU doesn't expect recvRetry!\n");
-        }
+        bool recvTimingResp(PacketPtr pkt) override;
+
+        void recvReqRetry() override;
 
     };
 
@@ -559,8 +615,11 @@ class BaseKvmCPU : public BaseCPU
     /** Unused dummy port for the instruction interface */
     KVMCpuPort instPort;
 
-    /** Pre-allocated MMIO memory request */
-    Request mmio_req;
+    /**
+     * Be conservative and always synchronize the thread context on
+     * KVM entry/exit.
+     */
+    const bool alwaysSyncTC;
 
     /**
      * Is the gem5 context dirty? Set to true to force an update of
@@ -577,21 +636,10 @@ class BaseKvmCPU : public BaseCPU
     /** KVM internal ID of the vCPU */
     const long vcpuID;
 
-  private:
-    struct TickEvent : public Event
-    {
-        BaseKvmCPU &cpu;
-
-        TickEvent(BaseKvmCPU &c)
-            : Event(CPU_Tick_Pri), cpu(c) {}
-
-        void process() { cpu.tick(); }
-
-        const char *description() const {
-            return "BaseKvmCPU tick";
-        }
-    };
+    /** ID of the vCPU thread */
+    pthread_t vcpuThread;
 
+  private:
     /**
      * Service MMIO requests in the mmioRing.
      *
@@ -615,8 +663,19 @@ class BaseKvmCPU : public BaseCPU
      */
     bool discardPendingSignal(int signum) const;
 
-    /** Setup hardware performance counters */
-    void setupCounters();
+    /**
+     * Thread-specific initialization.
+     *
+     * Some KVM-related initialization requires us to know the TID of
+     * the thread that is going to execute our event queue. For
+     * example, when setting up timers, we need to know the TID of the
+     * thread executing in KVM in order to deliver the timer signal to
+     * that thread. This method is called as the first event in this
+     * SimObject's event queue.
+     *
+     * @see startup
+     */
+    void startupThread();
 
     /** Try to drain the CPU if a drain is pending */
     bool tryDrain();
@@ -645,13 +704,61 @@ class BaseKvmCPU : public BaseCPU
     /** Cached page size of the host */
     const long pageSize;
 
-    TickEvent tickEvent;
+    EventFunctionWrapper tickEvent;
+
+    /**
+     * Setup an instruction break if there is one pending.
+     *
+     * Check if there are pending instruction breaks in the CPU's
+     * instruction event queue and schedule an instruction break using
+     * PerfEvent.
+     *
+     * @note This method doesn't currently handle the main system
+     * instruction event queue.
+     */
+    void setupInstStop();
 
     /** @{ */
-    /** Guest performance counters */
+    /** Setup hardware performance counters */
+    void setupCounters();
+
+    /**
+     * Setup the guest instruction counter.
+     *
+     * Setup the guest instruction counter and optionally request a
+     * signal every N instructions executed by the guest. This method
+     * will re-attach the counter if the counter has already been
+     * attached and its sampling settings have changed.
+     *
+     * @param period Signal period, set to 0 to disable signaling.
+     */
+    void setupInstCounter(uint64_t period = 0);
+
+    /** Currently active instruction count breakpoint */
+    uint64_t activeInstPeriod;
+
+    /**
+     * Guest cycle counter.
+     *
+     * This is the group leader of all performance counters measuring
+     * the guest system. It can be used in conjunction with the
+     * PerfKvmTimer (see perfControlledByTimer) to trigger exits from
+     * KVM.
+     */
     PerfKvmCounter hwCycles;
+
+    /**
+     * Guest instruction counter.
+     *
+     * This counter is typically only used to measure the number of
+     * instructions executed by the guest. However, it can also be
+     * used to trigger exits from KVM if the configuration script
+     * requests an exit after a certain number of instructions.
+     *
+     * @see setupInstBreak
+     * @see scheduleInstStop
+     */
     PerfKvmCounter hwInstructions;
-    /** @} */
 
     /**
      * Does the runTimer control the performance counters?
@@ -661,6 +768,7 @@ class BaseKvmCPU : public BaseCPU
      * exits.
      */
     bool perfControlledByTimer;
+    /** @} */
 
     /**
      * Timer used to force execution into the monitor after a
@@ -670,23 +778,15 @@ class BaseKvmCPU : public BaseCPU
      */
     std::unique_ptr<BaseKvmTimer> runTimer;
 
-    /** Host frequency */
-    Tick hostFreq;
-
     /** Host factor as specified in the configuration */
     float hostFactor;
 
-    /**
-     * Drain manager to use when signaling drain completion
-     *
-     * This pointer is non-NULL when draining and NULL otherwise.
-     */
-    DrainManager *drainManager;
-
   public:
     /* @{ */
     Stats::Scalar numInsts;
     Stats::Scalar numVMExits;
+    Stats::Scalar numVMHalfEntries;
+    Stats::Scalar numExitSignal;
     Stats::Scalar numMMIO;
     Stats::Scalar numCoalescedMMIO;
     Stats::Scalar numIO;