summaryrefslogtreecommitdiff
path: root/src/cpu/kvm/base.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu/kvm/base.hh')
-rw-r--r--src/cpu/kvm/base.hh489
1 files changed, 489 insertions, 0 deletions
diff --git a/src/cpu/kvm/base.hh b/src/cpu/kvm/base.hh
new file mode 100644
index 000000000..1424038a9
--- /dev/null
+++ b/src/cpu/kvm/base.hh
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2012 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.
+ *
+ * Authors: Andreas Sandberg
+ */
+
+#ifndef __CPU_KVM_BASE_HH__
+#define __CPU_KVM_BASE_HH__
+
+#include <memory>
+
+#include "base/statistics.hh"
+#include "cpu/kvm/perfevent.hh"
+#include "cpu/kvm/timer.hh"
+#include "cpu/kvm/vm.hh"
+#include "cpu/base.hh"
+#include "cpu/simple_thread.hh"
+
+/** Signal to use to trigger time-based exits from KVM */
+#define KVM_TIMER_SIGNAL SIGRTMIN
+
+// forward declarations
+class ThreadContext;
+struct BaseKvmCPUParams;
+
+/**
+ * Base class for KVM based CPU models
+ *
+ * All architecture specific KVM implementation should inherit from
+ * this class. The most basic CPU models only need to override the
+ * updateKvmState() and updateThreadContext() methods to implement
+ * state synchronization between gem5 and KVM.
+ *
+ * The architecture specific implementation is also responsible for
+ * delivering interrupts into the VM. This is typically done by
+ * overriding tick() and checking the thread context before entering
+ * into the VM. In order to deliver an interrupt, the implementation
+ * then calls KvmVM::setIRQLine() or BaseKvmCPU::kvmInterrupt()
+ * depending on the specifics of the underlying hardware/drivers.
+ */
+class BaseKvmCPU : public BaseCPU
+{
+ public:
+ BaseKvmCPU(BaseKvmCPUParams *params);
+ virtual ~BaseKvmCPU();
+
+ void init();
+ void startup();
+ void regStats();
+
+ void serializeThread(std::ostream &os, ThreadID tid);
+ void unserializeThread(Checkpoint *cp, const std::string &section,
+ ThreadID tid);
+
+ unsigned int drain(DrainManager *dm);
+ void drainResume();
+
+ void switchOut();
+ void takeOverFrom(BaseCPU *cpu);
+
+ void verifyMemoryMode() const;
+
+ CpuPort &getDataPort() { return dataPort; }
+ CpuPort &getInstPort() { return instPort; }
+
+ void wakeup();
+ void activateContext(ThreadID thread_num, Cycles delay);
+ void suspendContext(ThreadID thread_num);
+ void deallocateContext(ThreadID thread_num);
+ void haltContext(ThreadID thread_num);
+
+ Counter totalInsts() const;
+ Counter totalOps() const;
+
+ /** Dump the internal state to the terminal. */
+ virtual void dump();
+
+ /** SimpleThread object, provides all the architectural state. */
+ SimpleThread *thread;
+
+ /** ThreadContext object, provides an interface for external
+ * objects to modify this thread's state.
+ */
+ ThreadContext *tc;
+
+ KvmVM &vm;
+
+ protected:
+ enum Status {
+ /** Context not scheduled in KVM */
+ Idle,
+ /** Running normally */
+ Running,
+ };
+
+ /** CPU run state */
+ Status _status;
+
+ /**
+ * Execute the CPU until the next event in the main event queue or
+ * until the guest needs service from gem5.
+ *
+ * @note This method is virtual in order to allow implementations
+ * to check for architecture specific events (e.g., interrupts)
+ * before entering the VM.
+ */
+ virtual void tick();
+
+ /**
+ * Request KVM to run the guest for a given number of ticks. The
+ * method returns the approximate number of ticks executed.
+ *
+ * @note The returned number of ticks can be both larger or
+ * smaller than the requested number of ticks. A smaller number
+ * can, for example, occur when the guest executes MMIO. A larger
+ * number is typically due to performance counter inaccuracies.
+ *
+ * @param ticks Number of ticks to execute
+ * @return Number of ticks executed (see note)
+ */
+ Tick kvmRun(Tick ticks);
+
+ /**
+ * Get a pointer to the kvm_run structure containing all the input
+ * and output parameters from kvmRun().
+ */
+ struct kvm_run *getKvmRunState() { return _kvmRun; };
+
+ /**
+ * Retrieve a pointer to guest data stored at the end of the
+ * kvm_run structure. This is mainly used for PIO operations
+ * (KVM_EXIT_IO).
+ *
+ * @param offset Offset as specified by the kvm_run structure
+ * @return Pointer to guest data
+ */
+ uint8_t *getGuestData(uint64_t offset) const {
+ return (uint8_t *)_kvmRun + offset;
+ };
+
+ /**
+ * @addtogroup KvmInterrupts
+ * @{
+ */
+ /**
+ * Send a non-maskable interrupt to the guest
+ *
+ * @note The presence of this call depends on Kvm::capUserNMI().
+ */
+ void kvmNonMaskableInterrupt();
+
+ /**
+ * Send a normal interrupt to the guest
+ *
+ * @note Make sure that ready_for_interrupt_injection in kvm_run
+ * is set prior to calling this function. If not, an interrupt
+ * window must be requested by setting request_interrupt_window in
+ * kvm_run to 1 and restarting the guest.
+ *
+ * @param interrupt Structure describing the interrupt to send
+ */
+ void kvmInterrupt(const struct kvm_interrupt &interrupt);
+
+ /** @} */
+
+ /** @{ */
+ /**
+ * Get/Set the register state of the guest vCPU
+ *
+ * KVM has two different interfaces for accessing the state of the
+ * guest CPU. One interface updates 'normal' registers and one
+ * updates 'special' registers. The distinction between special
+ * and normal registers isn't very clear and is architecture
+ * dependent.
+ */
+ void getRegisters(struct kvm_regs &regs) const;
+ void setRegisters(const struct kvm_regs &regs);
+ void getSpecialRegisters(struct kvm_sregs &regs) const;
+ void setSpecialRegisters(const struct kvm_sregs &regs);
+ /** @} */
+
+ /** @{ */
+ /**
+ * Get/Set the guest FPU/vector state
+ */
+ void getFPUState(struct kvm_fpu &state) const;
+ void setFPUState(const struct kvm_fpu &state);
+ /** @} */
+
+ /** @{ */
+ /**
+ * Get/Set single register using the KVM_(SET|GET)_ONE_REG API.
+ *
+ * @note The presence of this call depends on Kvm::capOneReg().
+ */
+ void setOneReg(uint64_t id, const void *addr);
+ void setOneReg(uint64_t id, uint64_t value) { setOneReg(id, &value); }
+ void setOneReg(uint64_t id, uint32_t value) { setOneReg(id, &value); }
+ void getOneReg(uint64_t id, void *addr) const;
+ uint64_t getOneRegU64(uint64_t id) const {
+ uint64_t value;
+ getOneReg(id, &value);
+ return value;
+ }
+ uint32_t getOneRegU32(uint64_t id) const {
+ uint32_t value;
+ getOneReg(id, &value);
+ return value;
+ }
+ /** @} */
+
+ /**
+ * Get and format one register for printout.
+ *
+ * This function call getOneReg() to retrieve the contents of one
+ * register and automatically formats it for printing.
+ *
+ * @note The presence of this call depends on Kvm::capOneReg().
+ */
+ std::string getAndFormatOneReg(uint64_t id) const;
+
+ /** @{ */
+ /**
+ * Update the KVM state from the current thread context
+ *
+ * The base CPU calls this method before starting the guest CPU
+ * when the contextDirty flag is set. The architecture dependent
+ * CPU implementation is expected to update all guest state
+ * (registers, special registers, and FPU state).
+ */
+ virtual void updateKvmState() = 0;
+
+ /**
+ * Update the current thread context with the KVM state
+ *
+ * The base CPU after the guest updates any of the KVM state. In
+ * practice, this happens after kvmRun is called. The architecture
+ * dependent code is expected to read the state of the guest CPU
+ * and update gem5's thread state.
+ */
+ virtual void updateThreadContext() = 0;
+ /** @} */
+
+ /** @{ */
+ /**
+ * Main kvmRun exit handler, calls the relevant handleKvmExit*
+ * depending on exit type.
+ *
+ * @return Number of ticks spent servicing the exit request
+ */
+ virtual Tick handleKvmExit();
+
+ /**
+ * The guest performed a legacy IO request (out/inp on x86)
+ *
+ * @return Number of ticks spent servicing the IO request
+ */
+ virtual Tick handleKvmExitIO();
+
+ /**
+ * The guest requested a monitor service using a hypercall
+ *
+ * @return Number of ticks spent servicing the hypercall
+ */
+ virtual Tick handleKvmExitHypercall();
+
+ /**
+ * The guest exited because an interrupt window was requested
+ *
+ * The guest exited because an interrupt window was requested
+ * (request_interrupt_window in the kvm_run structure was set to 1
+ * before calling kvmRun) and it is now ready to receive
+ *
+ * @return Number of ticks spent servicing the IRQ
+ */
+ virtual Tick handleKvmExitIRQWindowOpen();
+
+ /**
+ * An unknown architecture dependent error occurred when starting
+ * the vCPU
+ *
+ * The kvm_run data structure contains the hardware error
+ * code. The defaults behavior of this method just prints the HW
+ * error code and panics. Architecture dependent implementations
+ * may want to override this method to provide better,
+ * hardware-aware, error messages.
+ *
+ * @return Number of ticks delay the next CPU tick
+ */
+ virtual Tick handleKvmExitUnknown();
+
+ /**
+ * An unhandled virtualization exception occured
+ *
+ * Some KVM virtualization drivers return unhandled exceptions to
+ * the user-space monitor. This interface is currently only used
+ * by the Intel VMX KVM driver.
+ *
+ * @return Number of ticks delay the next CPU tick
+ */
+ virtual Tick handleKvmExitException();
+
+ /**
+ * KVM failed to start the virtualized CPU
+ *
+ * The kvm_run data structure contains the hardware-specific error
+ * code.
+ *
+ * @return Number of ticks delay the next CPU tick
+ */
+ virtual Tick handleKvmExitFailEntry();
+ /** @} */
+
+ /**
+ * Inject a memory mapped IO request into gem5
+ *
+ * @param paddr Physical address
+ * @param data Pointer to the source/destination buffer
+ * @param size Memory access size
+ * @param write True if write, False if read
+ * @return Number of ticks spent servicing the memory access
+ */
+ Tick doMMIOAccess(Addr paddr, void *data, int size, bool write);
+
+
+ /**
+ * @addtogroup KvmIoctl
+ * @{
+ */
+ /**
+ * vCPU ioctl interface.
+ *
+ * @param request KVM vCPU request
+ * @param p1 Optional request parameter
+ *
+ * @return -1 on error (error number in errno), ioctl dependent
+ * value otherwise.
+ */
+ int ioctl(int request, long p1) const;
+ int ioctl(int request, void *p1) const {
+ return ioctl(request, (long)p1);
+ }
+ int ioctl(int request) const {
+ return ioctl(request, 0L);
+ }
+ /** @} */
+
+ /** Port for data requests */
+ CpuPort dataPort;
+
+ /** Unused dummy port for the instruction interface */
+ CpuPort instPort;
+
+ /** Pre-allocated MMIO memory request */
+ Request mmio_req;
+
+ /**
+ * Is the gem5 context dirty? Set to true to force an update of
+ * the KVM vCPU state upon the next call to kvmRun().
+ */
+ bool contextDirty;
+
+ /** 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";
+ }
+ };
+
+ /**
+ * Service MMIO requests in the mmioRing.
+ *
+ *
+ * @return Number of ticks spent servicing the MMIO requests in
+ * the MMIO ring buffer
+ */
+ Tick flushCoalescedMMIO();
+
+ /**
+ * Setup a signal handler to catch the timer signal used to
+ * switch back to the monitor.
+ */
+ void setupSignalHandler();
+
+ /** Setup hardware performance counters */
+ void setupCounters();
+
+ /** @{ */
+ /** Start/stop counting HW performance events */
+ void startCounters();
+ void stopCounters();
+ /** @} */
+
+ /** KVM vCPU file descriptor */
+ int vcpuFD;
+ /** Size of MMAPed kvm_run area */
+ int vcpuMMapSize;
+ /**
+ * Pointer to the kvm_run structure used to communicate parameters
+ * with KVM.
+ *
+ * @note This is the base pointer of the MMAPed KVM region. The
+ * first page contains the kvm_run structure. Subsequent pages may
+ * contain other data such as the MMIO ring buffer.
+ */
+ struct kvm_run *_kvmRun;
+ /**
+ * Coalesced MMIO ring buffer. NULL if coalesced MMIO is not
+ * supported.
+ */
+ struct kvm_coalesced_mmio_ring *mmioRing;
+ /** Cached page size of the host */
+ const long pageSize;
+
+ TickEvent tickEvent;
+
+ /** @{ */
+ /** Guest performance counters */
+ PerfKvmCounter hwCycles;
+ PerfKvmCounter hwInstructions;
+ /** @} */
+
+ /**
+ * Timer used to force execution into the monitor after a
+ * specified number of simulation tick equivalents have executed
+ * in the guest. This counter generates the signal specified by
+ * KVM_TIMER_SIGNAL.
+ */
+ std::unique_ptr<BaseKvmTimer> runTimer;
+
+ float hostFactor;
+
+ public:
+ /* @{ */
+ Stats::Scalar numVMExits;
+ Stats::Scalar numMMIO;
+ Stats::Scalar numCoalescedMMIO;
+ Stats::Scalar numIO;
+ Stats::Scalar numHalt;
+ Stats::Scalar numInterrupts;
+ Stats::Scalar numHypercalls;
+ /* @} */
+};
+
+#endif