print "Warning: Header file <fenv.h> not found."
print " This host has no IEEE FP rounding mode control."
+# Check if we should enable KVM-based hardware virtualization
+have_kvm = conf.CheckHeader('linux/kvm.h', '<>')
+if not have_kvm:
+ print "Info: Header file <linux/kvm.h> not found, " \
+ "disabling KVM support."
+
+# Check if the requested target ISA is compatible with the host
+def is_isa_kvm_compatible(isa):
+ isa_comp_table = {
+ }
+ try:
+ import platform
+ host_isa = platform.machine()
+ except:
+ print "Warning: Failed to determine host ISA."
+ return False
+
+ return host_isa in isa_comp_table.get(isa, [])
+
+
######################################################################
#
# Finish the configuration
BoolVariable('USE_POSIX_CLOCK', 'Use POSIX Clocks', have_posix_clock),
BoolVariable('USE_FENV', 'Use <fenv.h> IEEE mode control', have_fenv),
BoolVariable('CP_ANNOTATE', 'Enable critical path annotation capability', False),
+ BoolVariable('USE_KVM', 'Enable hardware virtualized (KVM) CPU models', have_kvm),
EnumVariable('PROTOCOL', 'Coherence protocol for Ruby', 'None',
all_protocols),
)
if env['EFENCE']:
env.Append(LIBS=['efence'])
+ if env['USE_KVM']:
+ if not have_kvm:
+ print "Warning: Can not enable KVM, host seems to lack KVM support"
+ env['USE_KVM'] = False
+ elif not is_isa_kvm_compatible(env['TARGET_ISA']):
+ print "Info: KVM support disabled due to unsupported host and " \
+ "target ISA combination"
+ env['USE_KVM'] = False
+
# Save sticky variable settings back to current variables file
sticky_vars.Save(current_vars_file, env)
--- /dev/null
+# 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
+
+from m5.params import *
+from m5.proxy import *
+
+from BaseCPU import BaseCPU
+from KvmVM import KvmVM
+
+class BaseKvmCPU(BaseCPU):
+ type = 'BaseKvmCPU'
+ cxx_header = "cpu/kvm/base.hh"
+ abstract = True
+
+ @classmethod
+ def export_method_cxx_predecls(cls, code):
+ code('#include "cpu/kvm/base.hh"')
+
+ @classmethod
+ def export_methods(cls, code):
+ code('''
+ void dump();
+''')
+
+ @classmethod
+ def memory_mode(cls):
+ return 'atomic_noncaching'
+
+ @classmethod
+ def require_caches(cls):
+ return False
+
+ @classmethod
+ def support_take_over(cls):
+ return True
+
+ kvmVM = Param.KvmVM(Parent.any, 'KVM VM (i.e., shared memory domain)')
+ hostFactor = Param.Float(1.0, "Cycle scale factor")
--- /dev/null
+# 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
+
+from m5.params import *
+from m5.proxy import *
+
+from m5.SimObject import SimObject
+
+class KvmVM(SimObject):
+ type = 'KvmVM'
+ cxx_header = "cpu/kvm/vm.hh"
+
+ system = Param.System(Parent.any, "system object")
+
+ coalescedMMIO = VectorParam.AddrRange([], "memory ranges for coalesced MMIO")
--- /dev/null
+# -*- mode:python -*-
+
+# 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
+
+Import('*')
+
+if env['USE_KVM']:
+ SimObject('KvmVM.py')
+ SimObject('BaseKvmCPU.py')
+
+ Source('base.cc')
+ Source('vm.cc')
+ Source('perfevent.cc')
+ Source('timer.cc')
+
+ DebugFlag('Kvm', 'Basic KVM Functionality')
+ DebugFlag('KvmContext', 'KVM/gem5 context synchronization')
+ DebugFlag('KvmIO', 'KVM MMIO diagnostics')
+ DebugFlag('KvmInt', 'KVM Interrupt handling')
+ DebugFlag('KvmRun', 'KvmRun entry/exit diagnostics')
+ DebugFlag('KvmTimer', 'KVM timing')
+
+ CompoundFlag('KvmAll', [ 'Kvm', 'KvmContext', 'KvmRun',
+ 'KvmIO', 'KvmInt', 'KvmTimer' ],
+ 'All KVM debug flags')
--- /dev/null
+/*
+ * 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
+ */
+
+#include <linux/kvm.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <csignal>
+#include <ostream>
+
+#include "arch/utility.hh"
+#include "cpu/kvm/base.hh"
+#include "debug/Kvm.hh"
+#include "debug/KvmIO.hh"
+#include "debug/KvmRun.hh"
+#include "params/BaseKvmCPU.hh"
+#include "sim/process.hh"
+#include "sim/system.hh"
+
+/* Used by some KVM macros */
+#define PAGE_SIZE pageSize
+
+volatile bool timerOverflowed = false;
+
+static void
+onTimerOverflow(int signo, siginfo_t *si, void *data)
+{
+ timerOverflowed = true;
+}
+
+BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
+ : BaseCPU(params),
+ vm(*params->kvmVM),
+ _status(Idle),
+ dataPort(name() + ".dcache_port", this),
+ instPort(name() + ".icache_port", this),
+ contextDirty(true),
+ vcpuID(vm.allocVCPUID()), vcpuFD(-1), vcpuMMapSize(0),
+ _kvmRun(NULL), mmioRing(NULL),
+ pageSize(sysconf(_SC_PAGE_SIZE)),
+ tickEvent(*this),
+ hostFactor(params->hostFactor)
+{
+ if (pageSize == -1)
+ panic("KVM: Failed to determine host page size (%i)\n",
+ errno);
+
+ thread = new SimpleThread(this, 0, params->system,
+ params->itb, params->dtb, params->isa[0]);
+ thread->setStatus(ThreadContext::Halted);
+ tc = thread->getTC();
+ threadContexts.push_back(tc);
+
+ setupCounters();
+ setupSignalHandler();
+
+ runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC,
+ params->hostFactor,
+ params->clock));
+}
+
+BaseKvmCPU::~BaseKvmCPU()
+{
+ if (_kvmRun)
+ munmap(_kvmRun, vcpuMMapSize);
+ close(vcpuFD);
+}
+
+void
+BaseKvmCPU::init()
+{
+ BaseCPU::init();
+
+ if (numThreads != 1)
+ fatal("KVM: Multithreading not supported");
+
+ tc->initMemProxies(tc);
+
+ // initialize CPU, including PC
+ if (FullSystem && !switchedOut())
+ TheISA::initCPU(tc, tc->contextId());
+
+ mmio_req.setThreadContext(tc->contextId(), 0);
+}
+
+void
+BaseKvmCPU::startup()
+{
+ Kvm &kvm(vm.kvm);
+
+ BaseCPU::startup();
+
+ assert(vcpuFD == -1);
+
+ // Tell the VM that a CPU is about to start.
+ vm.cpuStartup();
+
+ // We can't initialize KVM CPUs in BaseKvmCPU::init() since we are
+ // not guaranteed that the parent KVM VM has initialized at that
+ // point. Initialize virtual CPUs here instead.
+ vcpuFD = vm.createVCPU(vcpuID);
+
+ // Map the KVM run structure */
+ vcpuMMapSize = kvm.getVCPUMMapSize();
+ _kvmRun = (struct kvm_run *)mmap(0, vcpuMMapSize,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ vcpuFD, 0);
+ if (_kvmRun == MAP_FAILED)
+ panic("KVM: Failed to map run data structure\n");
+
+ // Setup a pointer to the MMIO ring buffer if coalesced MMIO is
+ // available. The offset into the KVM's communication page is
+ // provided by the coalesced MMIO capability.
+ int mmioOffset(kvm.capCoalescedMMIO());
+ if (mmioOffset) {
+ inform("KVM: Coalesced IO available\n");
+ mmioRing = (struct kvm_coalesced_mmio_ring *)(
+ (char *)_kvmRun + (mmioOffset * pageSize));
+ } else {
+ inform("KVM: Coalesced not supported by host OS\n");
+ }
+}
+
+void
+BaseKvmCPU::regStats()
+{
+ using namespace Stats;
+
+ BaseCPU::regStats();
+
+ numVMExits
+ .name(name() + ".numVMExits")
+ .desc("total number of KVM exits")
+ ;
+
+ numMMIO
+ .name(name() + ".numMMIO")
+ .desc("number of VM exits due to memory mapped IO")
+ ;
+
+ numCoalescedMMIO
+ .name(name() + ".numCoalescedMMIO")
+ .desc("number of coalesced memory mapped IO requests")
+ ;
+
+ numIO
+ .name(name() + ".numIO")
+ .desc("number of VM exits due to legacy IO")
+ ;
+
+ numHalt
+ .name(name() + ".numHalt")
+ .desc("number of VM exits due to wait for interrupt instructions")
+ ;
+
+ numInterrupts
+ .name(name() + ".numInterrupts")
+ .desc("number of interrupts delivered")
+ ;
+
+ numHypercalls
+ .name(name() + ".numHypercalls")
+ .desc("number of hypercalls")
+ ;
+}
+
+void
+BaseKvmCPU::serializeThread(std::ostream &os, ThreadID tid)
+{
+ assert(tid == 0);
+ assert(_status == Idle);
+ thread->serialize(os);
+}
+
+void
+BaseKvmCPU::unserializeThread(Checkpoint *cp, const std::string §ion,
+ ThreadID tid)
+{
+ assert(tid == 0);
+ assert(_status == Idle);
+ thread->unserialize(cp, section);
+ contextDirty = true;
+}
+
+unsigned int
+BaseKvmCPU::drain(DrainManager *dm)
+{
+ if (switchedOut())
+ return 0;
+
+ DPRINTF(Kvm, "drain\n");
+
+ // De-schedule the tick event so we don't insert any more MMIOs
+ // into the system while it is draining.
+ if (tickEvent.scheduled())
+ deschedule(tickEvent);
+
+ _status = Idle;
+ return 0;
+}
+
+void
+BaseKvmCPU::drainResume()
+{
+ assert(!tickEvent.scheduled());
+
+ // We might have been switched out. In that case, we don't need to
+ // do anything.
+ if (switchedOut())
+ return;
+
+ DPRINTF(Kvm, "drainResume\n");
+ verifyMemoryMode();
+
+ // The tick event is de-scheduled as a part of the draining
+ // process. Re-schedule it if the thread context is active.
+ if (tc->status() == ThreadContext::Active) {
+ schedule(tickEvent, nextCycle());
+ _status = Running;
+ } else {
+ _status = Idle;
+ }
+}
+
+void
+BaseKvmCPU::switchOut()
+{
+ BaseCPU::switchOut();
+
+ DPRINTF(Kvm, "switchOut\n");
+
+ // We should have drained prior to executing a switchOut, which
+ // means that the tick event shouldn't be scheduled and the CPU is
+ // idle.
+ assert(!tickEvent.scheduled());
+ assert(_status == Idle);
+}
+
+void
+BaseKvmCPU::takeOverFrom(BaseCPU *cpu)
+{
+ DPRINTF(Kvm, "takeOverFrom\n");
+
+ BaseCPU::takeOverFrom(cpu);
+
+ // We should have drained prior to executing a switchOut, which
+ // means that the tick event shouldn't be scheduled and the CPU is
+ // idle.
+ assert(!tickEvent.scheduled());
+ assert(_status == Idle);
+ assert(threadContexts.size() == 1);
+
+ // Force a gem5 -> KVM context synchronization
+ contextDirty = true;
+}
+
+void
+BaseKvmCPU::verifyMemoryMode() const
+{
+ if (!(system->isAtomicMode() && system->bypassCaches())) {
+ fatal("The KVM-based CPUs requires the memory system to be in the "
+ "'atomic_noncaching' mode.\n");
+ }
+}
+
+void
+BaseKvmCPU::wakeup()
+{
+ DPRINTF(Kvm, "wakeup()\n");
+
+ if (thread->status() != ThreadContext::Suspended)
+ return;
+
+ thread->activate();
+}
+
+void
+BaseKvmCPU::activateContext(ThreadID thread_num, Cycles delay)
+{
+ DPRINTF(Kvm, "ActivateContext %d (%d cycles)\n", thread_num, delay);
+
+ assert(thread_num == 0);
+ assert(thread);
+
+ assert(_status == Idle);
+ assert(!tickEvent.scheduled());
+
+ numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend)
+ * hostFactor;
+
+ schedule(tickEvent, clockEdge(delay));
+ _status = Running;
+}
+
+
+void
+BaseKvmCPU::suspendContext(ThreadID thread_num)
+{
+ DPRINTF(Kvm, "SuspendContext %d\n", thread_num);
+
+ assert(thread_num == 0);
+ assert(thread);
+
+ if (_status == Idle)
+ return;
+
+ assert(_status == Running);
+
+ // The tick event may no be scheduled if the quest has requested
+ // the monitor to wait for interrupts. The normal CPU models can
+ // get their tick events descheduled by quiesce instructions, but
+ // that can't happen here.
+ if (tickEvent.scheduled())
+ deschedule(tickEvent);
+
+ _status = Idle;
+}
+
+void
+BaseKvmCPU::deallocateContext(ThreadID thread_num)
+{
+ // for now, these are equivalent
+ suspendContext(thread_num);
+}
+
+void
+BaseKvmCPU::haltContext(ThreadID thread_num)
+{
+ // for now, these are equivalent
+ suspendContext(thread_num);
+}
+
+Counter
+BaseKvmCPU::totalInsts() const
+{
+ return hwInstructions.read();
+}
+
+Counter
+BaseKvmCPU::totalOps() const
+{
+ hack_once("Pretending totalOps is equivalent to totalInsts()\n");
+ return hwInstructions.read();
+}
+
+void
+BaseKvmCPU::dump()
+{
+ inform("State dumping not implemented.");
+}
+
+void
+BaseKvmCPU::tick()
+{
+ assert(_status == Running);
+
+ DPRINTF(KvmRun, "Entering KVM...\n");
+
+ if (contextDirty) {
+ contextDirty = false;
+ updateKvmState();
+ }
+
+ Tick ticksToExecute(mainEventQueue.nextTick() - curTick());
+ Tick ticksExecuted(kvmRun(ticksToExecute));
+ updateThreadContext();
+
+ Tick delay(ticksExecuted + handleKvmExit());
+
+ switch (_status) {
+ case Running:
+ schedule(tickEvent, clockEdge(ticksToCycles(delay)));
+ break;
+
+ default:
+ /* The CPU is halted or waiting for an interrupt from a
+ * device. Don't start it. */
+ break;
+ }
+}
+
+Tick
+BaseKvmCPU::kvmRun(Tick ticks)
+{
+ uint64_t baseCycles(hwCycles.read());
+ uint64_t baseInstrs(hwInstructions.read());
+
+ if (ticks < runTimer->resolution()) {
+ DPRINTF(KvmRun, "KVM: Adjusting tick count (%i -> %i)\n",
+ ticks, runTimer->resolution());
+ ticks = runTimer->resolution();
+ }
+
+ DPRINTF(KvmRun, "KVM: Executing for %i ticks\n", ticks);
+ timerOverflowed = false;
+ runTimer->arm(ticks);
+ startCounters();
+ if (ioctl(KVM_RUN) == -1) {
+ if (errno != EINTR)
+ panic("KVM: Failed to start virtual CPU (errno: %i)\n",
+ errno);
+ }
+ stopCounters();
+ runTimer->disarm();
+
+ uint64_t cyclesExecuted(hwCycles.read() - baseCycles);
+ Tick ticksExecuted(runTimer->ticksFromHostCycles(cyclesExecuted));
+
+ if (ticksExecuted < ticks &&
+ timerOverflowed &&
+ _kvmRun->exit_reason == KVM_EXIT_INTR) {
+ // TODO: We should probably do something clever here...
+ warn("KVM: Early timer event, requested %i ticks but got %i ticks.\n",
+ ticks, ticksExecuted);
+ }
+
+ numCycles += cyclesExecuted * hostFactor;
+ ++numVMExits;
+
+ DPRINTF(KvmRun, "KVM: Executed %i instructions in %i cycles (%i ticks, sim cycles: %i).\n",
+ hwInstructions.read() - baseInstrs,
+ cyclesExecuted,
+ ticksExecuted,
+ cyclesExecuted * hostFactor);
+
+ return ticksExecuted + flushCoalescedMMIO();
+}
+
+void
+BaseKvmCPU::kvmNonMaskableInterrupt()
+{
+ ++numInterrupts;
+ if (ioctl(KVM_NMI) == -1)
+ panic("KVM: Failed to deliver NMI to virtual CPU\n");
+}
+
+void
+BaseKvmCPU::kvmInterrupt(const struct kvm_interrupt &interrupt)
+{
+ ++numInterrupts;
+ if (ioctl(KVM_INTERRUPT, (void *)&interrupt) == -1)
+ panic("KVM: Failed to deliver interrupt to virtual CPU\n");
+}
+
+void
+BaseKvmCPU::getRegisters(struct kvm_regs ®s) const
+{
+ if (ioctl(KVM_GET_REGS, ®s) == -1)
+ panic("KVM: Failed to get guest registers\n");
+}
+
+void
+BaseKvmCPU::setRegisters(const struct kvm_regs ®s)
+{
+ if (ioctl(KVM_SET_REGS, (void *)®s) == -1)
+ panic("KVM: Failed to set guest registers\n");
+}
+
+void
+BaseKvmCPU::getSpecialRegisters(struct kvm_sregs ®s) const
+{
+ if (ioctl(KVM_GET_SREGS, ®s) == -1)
+ panic("KVM: Failed to get guest special registers\n");
+}
+
+void
+BaseKvmCPU::setSpecialRegisters(const struct kvm_sregs ®s)
+{
+ if (ioctl(KVM_SET_SREGS, (void *)®s) == -1)
+ panic("KVM: Failed to set guest special registers\n");
+}
+
+void
+BaseKvmCPU::getFPUState(struct kvm_fpu &state) const
+{
+ if (ioctl(KVM_GET_FPU, &state) == -1)
+ panic("KVM: Failed to get guest FPU state\n");
+}
+
+void
+BaseKvmCPU::setFPUState(const struct kvm_fpu &state)
+{
+ if (ioctl(KVM_SET_FPU, (void *)&state) == -1)
+ panic("KVM: Failed to set guest FPU state\n");
+}
+
+
+void
+BaseKvmCPU::setOneReg(uint64_t id, const void *addr)
+{
+#ifdef KVM_SET_ONE_REG
+ struct kvm_one_reg reg;
+ reg.id = id;
+ reg.addr = (uint64_t)addr;
+
+ if (ioctl(KVM_SET_ONE_REG, ®) == -1) {
+ panic("KVM: Failed to set register (0x%x) value (errno: %i)\n",
+ id, errno);
+ }
+#else
+ panic("KVM_SET_ONE_REG is unsupported on this platform.\n");
+#endif
+}
+
+void
+BaseKvmCPU::getOneReg(uint64_t id, void *addr) const
+{
+#ifdef KVM_GET_ONE_REG
+ struct kvm_one_reg reg;
+ reg.id = id;
+ reg.addr = (uint64_t)addr;
+
+ if (ioctl(KVM_GET_ONE_REG, ®) == -1) {
+ panic("KVM: Failed to get register (0x%x) value (errno: %i)\n",
+ id, errno);
+ }
+#else
+ panic("KVM_GET_ONE_REG is unsupported on this platform.\n");
+#endif
+}
+
+std::string
+BaseKvmCPU::getAndFormatOneReg(uint64_t id) const
+{
+#ifdef KVM_GET_ONE_REG
+ std::ostringstream ss;
+
+ ss.setf(std::ios::hex, std::ios::basefield);
+ ss.setf(std::ios::showbase);
+#define HANDLE_INTTYPE(len) \
+ case KVM_REG_SIZE_U ## len: { \
+ uint ## len ## _t value; \
+ getOneReg(id, &value); \
+ ss << value; \
+ } break
+
+#define HANDLE_ARRAY(len) \
+ case KVM_REG_SIZE_U ## len: { \
+ uint8_t value[len / 8]; \
+ getOneReg(id, value); \
+ ss << "[" << value[0]; \
+ for (int i = 1; i < len / 8; ++i) \
+ ss << ", " << value[i]; \
+ ss << "]"; \
+ } break
+
+ switch (id & KVM_REG_SIZE_MASK) {
+ HANDLE_INTTYPE(8);
+ HANDLE_INTTYPE(16);
+ HANDLE_INTTYPE(32);
+ HANDLE_INTTYPE(64);
+ HANDLE_ARRAY(128);
+ HANDLE_ARRAY(256);
+ HANDLE_ARRAY(512);
+ HANDLE_ARRAY(1024);
+ default:
+ ss << "??";
+ }
+
+#undef HANDLE_INTTYPE
+#undef HANDLE_ARRAY
+
+ return ss.str();
+#else
+ panic("KVM_GET_ONE_REG is unsupported on this platform.\n");
+#endif
+}
+
+Tick
+BaseKvmCPU::handleKvmExit()
+{
+ DPRINTF(KvmRun, "handleKvmExit (exit_reason: %i)\n", _kvmRun->exit_reason);
+
+ switch (_kvmRun->exit_reason) {
+ case KVM_EXIT_UNKNOWN:
+ return handleKvmExitUnknown();
+
+ case KVM_EXIT_EXCEPTION:
+ return handleKvmExitException();
+
+ case KVM_EXIT_IO:
+ ++numIO;
+ return handleKvmExitIO();
+
+ case KVM_EXIT_HYPERCALL:
+ ++numHypercalls;
+ return handleKvmExitHypercall();
+
+ case KVM_EXIT_HLT:
+ /* The guest has halted and is waiting for interrupts */
+ DPRINTF(Kvm, "handleKvmExitHalt\n");
+ ++numHalt;
+
+ // Suspend the thread until the next interrupt arrives
+ thread->suspend();
+
+ // This is actually ignored since the thread is suspended.
+ return 0;
+
+ case KVM_EXIT_MMIO:
+ /* Service memory mapped IO requests */
+ DPRINTF(KvmIO, "KVM: Handling MMIO (w: %u, addr: 0x%x, len: %u)\n",
+ _kvmRun->mmio.is_write,
+ _kvmRun->mmio.phys_addr, _kvmRun->mmio.len);
+
+ ++numMMIO;
+ return doMMIOAccess(_kvmRun->mmio.phys_addr, _kvmRun->mmio.data,
+ _kvmRun->mmio.len, _kvmRun->mmio.is_write);
+
+ case KVM_EXIT_IRQ_WINDOW_OPEN:
+ return handleKvmExitIRQWindowOpen();
+
+ case KVM_EXIT_FAIL_ENTRY:
+ return handleKvmExitFailEntry();
+
+ case KVM_EXIT_INTR:
+ /* KVM was interrupted by a signal, restart it in the next
+ * tick. */
+ return 0;
+
+ case KVM_EXIT_INTERNAL_ERROR:
+ panic("KVM: Internal error (suberror: %u)\n",
+ _kvmRun->internal.suberror);
+
+ default:
+ panic("KVM: Unexpected exit (exit_reason: %u)\n", _kvmRun->exit_reason);
+ }
+}
+
+Tick
+BaseKvmCPU::handleKvmExitIO()
+{
+ panic("KVM: Unhandled guest IO (dir: %i, size: %i, port: 0x%x, count: %i)\n",
+ _kvmRun->io.direction, _kvmRun->io.size,
+ _kvmRun->io.port, _kvmRun->io.count);
+}
+
+Tick
+BaseKvmCPU::handleKvmExitHypercall()
+{
+ panic("KVM: Unhandled hypercall\n");
+}
+
+Tick
+BaseKvmCPU::handleKvmExitIRQWindowOpen()
+{
+ warn("KVM: Unhandled IRQ window.\n");
+ return 0;
+}
+
+
+Tick
+BaseKvmCPU::handleKvmExitUnknown()
+{
+ panic("KVM: Unknown error when starting vCPU (hw reason: 0x%llx)\n",
+ _kvmRun->hw.hardware_exit_reason);
+}
+
+Tick
+BaseKvmCPU::handleKvmExitException()
+{
+ panic("KVM: Got exception when starting vCPU "
+ "(exception: %u, error_code: %u)\n",
+ _kvmRun->ex.exception, _kvmRun->ex.error_code);
+}
+
+Tick
+BaseKvmCPU::handleKvmExitFailEntry()
+{
+ panic("KVM: Failed to enter virtualized mode (hw reason: 0x%llx)\n",
+ _kvmRun->fail_entry.hardware_entry_failure_reason);
+}
+
+Tick
+BaseKvmCPU::doMMIOAccess(Addr paddr, void *data, int size, bool write)
+{
+ mmio_req.setPhys(paddr, size,
+ 0, /* flags */
+ dataMasterId());
+
+ const MemCmd cmd(write ? MemCmd::WriteReq : MemCmd::ReadReq);
+ Packet pkt(&mmio_req, cmd);
+ pkt.dataStatic(data);
+ return dataPort.sendAtomic(&pkt);
+}
+
+int
+BaseKvmCPU::ioctl(int request, long p1) const
+{
+ if (vcpuFD == -1)
+ panic("KVM: CPU ioctl called before initialization\n");
+
+ return ::ioctl(vcpuFD, request, p1);
+}
+
+Tick
+BaseKvmCPU::flushCoalescedMMIO()
+{
+ if (!mmioRing)
+ return 0;
+
+ DPRINTF(KvmIO, "KVM: Flushing the coalesced MMIO ring buffer\n");
+
+ // TODO: We might need to do synchronization when we start to
+ // support multiple CPUs
+ Tick ticks(0);
+ while (mmioRing->first != mmioRing->last) {
+ struct kvm_coalesced_mmio &ent(
+ mmioRing->coalesced_mmio[mmioRing->first]);
+
+ DPRINTF(KvmIO, "KVM: Handling coalesced MMIO (addr: 0x%x, len: %u)\n",
+ ent.phys_addr, ent.len);
+
+ ++numCoalescedMMIO;
+ ticks += doMMIOAccess(ent.phys_addr, ent.data, ent.len, true);
+
+ mmioRing->first = (mmioRing->first + 1) % KVM_COALESCED_MMIO_MAX;
+ }
+
+ return ticks;
+}
+
+void
+BaseKvmCPU::setupSignalHandler()
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = onTimerOverflow;
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ if (sigaction(KVM_TIMER_SIGNAL, &sa, NULL) == -1)
+ panic("KVM: Failed to setup vCPU signal handler\n");
+}
+
+void
+BaseKvmCPU::setupCounters()
+{
+ DPRINTF(Kvm, "Attaching cycle counter...\n");
+ PerfKvmCounterConfig cfgCycles(PERF_TYPE_HARDWARE,
+ PERF_COUNT_HW_CPU_CYCLES);
+ cfgCycles.disabled(true)
+ .pinned(true);
+ hwCycles.attach(cfgCycles,
+ 0); // TID (0 => currentThread)
+
+ DPRINTF(Kvm, "Attaching instruction counter...\n");
+ PerfKvmCounterConfig cfgInstructions(PERF_TYPE_HARDWARE,
+ PERF_COUNT_HW_INSTRUCTIONS);
+ hwInstructions.attach(cfgInstructions,
+ 0, // TID (0 => currentThread)
+ hwCycles);
+}
+
+void
+BaseKvmCPU::startCounters()
+{
+ // We only need to start/stop the hwCycles counter since hwCycles
+ // and hwInstructions are a counter group with hwCycles as the
+ // group leader.
+ hwCycles.start();
+}
+
+void
+BaseKvmCPU::stopCounters()
+{
+ hwCycles.stop();
+}
--- /dev/null
+/*
+ * 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 §ion,
+ 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 ®s) const;
+ void setRegisters(const struct kvm_regs ®s);
+ void getSpecialRegisters(struct kvm_sregs ®s) const;
+ void setSpecialRegisters(const struct kvm_sregs ®s);
+ /** @} */
+
+ /** @{ */
+ /**
+ * 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
--- /dev/null
+/*
+ * 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
+ */
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <syscall.h>
+#include <unistd.h>
+
+#include <cassert>
+#include <cerrno>
+#include <csignal>
+#include <cstring>
+
+#include "base/misc.hh"
+#include "perfevent.hh"
+
+PerfKvmCounterConfig::PerfKvmCounterConfig(uint32_t type, uint64_t config)
+{
+ memset(&attr, 0, sizeof(attr));
+
+ attr.size = PERF_ATTR_SIZE_VER0;
+ attr.type = type;
+ attr.config = config;
+}
+
+PerfKvmCounterConfig::~PerfKvmCounterConfig()
+{
+}
+
+
+PerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config, pid_t tid)
+ : fd(-1), ringBuffer(NULL), pageSize(-1)
+{
+ attach(config, tid, -1);
+}
+
+PerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config,
+ pid_t tid, const PerfKvmCounter &parent)
+ : fd(-1), ringBuffer(NULL), pageSize(-1)
+{
+ attach(config, tid, parent);
+}
+
+PerfKvmCounter::PerfKvmCounter()
+ : fd(-1), ringBuffer(NULL), pageSize(-1)
+{
+}
+
+PerfKvmCounter::~PerfKvmCounter()
+{
+ if (attached())
+ detach();
+}
+
+void
+PerfKvmCounter::detach()
+{
+ assert(attached());
+
+ if (munmap(ringBuffer, ringNumPages * pageSize) == -1)
+ warn("PerfKvmCounter: Failed to unmap ring buffer (%i)\n",
+ errno);
+ close(fd);
+
+ fd = -1;
+ ringBuffer = NULL;
+}
+
+void
+PerfKvmCounter::start()
+{
+ if (ioctl(PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1)
+ panic("KVM: Failed to enable performance counters (%i)\n", errno);
+}
+
+void
+PerfKvmCounter::stop()
+{
+ if (ioctl(PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1)
+ panic("KVM: Failed to disable performance counters (%i)\n", errno);
+}
+
+void
+PerfKvmCounter::period(uint64_t period)
+{
+ if (ioctl(PERF_EVENT_IOC_PERIOD, &period) == -1)
+ panic("KVM: Failed to set period of performance counter (%i)\n", errno);
+}
+
+void
+PerfKvmCounter::refresh(int refresh)
+{
+ if (ioctl(PERF_EVENT_IOC_REFRESH, refresh) == -1)
+ panic("KVM: Failed to refresh performance counter (%i)\n", errno);
+}
+
+uint64_t
+PerfKvmCounter::read() const
+{
+ uint64_t value;
+
+ read(&value, sizeof(uint64_t));
+ return value;
+}
+
+void
+PerfKvmCounter::enableSignals(pid_t tid, int signal)
+{
+ struct f_owner_ex sigowner;
+
+ sigowner.type = F_OWNER_TID;
+ sigowner.pid = tid;
+
+ if (fcntl(F_SETOWN_EX, &sigowner) == -1 ||
+ fcntl(F_SETSIG, signal) == -1 ||
+ fcntl(F_SETFL, O_ASYNC) == -1)
+ panic("PerfKvmCounter: Failed to enable signals for counter (%i)\n",
+ errno);
+}
+
+void
+PerfKvmCounter::attach(PerfKvmCounterConfig &config,
+ pid_t tid, int group_fd)
+{
+ assert(!attached());
+
+ fd = syscall(__NR_perf_event_open,
+ &config.attr, tid,
+ -1, // CPU (-1 => Any CPU that the task happens to run on)
+ group_fd,
+ 0); // Flags
+ if (fd == -1)
+ panic("PerfKvmCounter::open failed (%i)\n", errno);
+
+ mmapPerf(1);
+}
+
+pid_t
+PerfKvmCounter::gettid()
+{
+ return syscall(__NR_gettid);
+}
+
+void
+PerfKvmCounter::mmapPerf(int pages)
+{
+ assert(attached());
+ assert(ringBuffer == NULL);
+
+ if (pageSize == -1) {
+ pageSize = sysconf(_SC_PAGE_SIZE);
+ if (pageSize == -1)
+ panic("PerfKvmCounter: Failed to determine page size (%i)\n",
+ errno);
+ }
+
+ ringNumPages = pages + 1;
+ ringBuffer = (struct perf_event_mmap_page *)mmap(
+ NULL, ringNumPages * 4096,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0);
+ if (ringBuffer == MAP_FAILED)
+ panic("PerfKvmCounter: MMAP failed (%i)\n",
+ errno);
+}
+
+int
+PerfKvmCounter::fcntl(int cmd, long p1)
+{
+ assert(attached());
+ return ::fcntl(fd, cmd, p1);
+}
+
+int
+PerfKvmCounter::ioctl(int request, long p1)
+{
+ assert(attached());
+ return ::ioctl(fd, request, p1);
+}
+
+void
+PerfKvmCounter::read(void *buf, size_t size) const
+{
+ char *_buf = (char *)buf;
+ size_t _size = size;
+
+ assert(attached());
+
+ do {
+ ssize_t ret;
+ ret = ::read(fd, _buf, _size);
+ switch (ret) {
+ case -1:
+ if (errno != EAGAIN)
+ panic("PerfKvmCounter::read failed (%i)\n", errno);
+ break;
+
+ case 0:
+ panic("PerfKvmCounter::read unexpected EOF.\n");
+
+ default:
+ _size -= ret;
+ _buf += ret;
+ break;
+ }
+ } while(_size);
+}
--- /dev/null
+/*
+ * 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_PERFEVENT_HH__
+#define __CPU_KVM_PERFEVENT_HH__
+
+#include <linux/perf_event.h>
+#include <sys/types.h>
+
+#include <inttypes.h>
+
+/**
+ * PerfEvent counter configuration.
+ */
+class PerfKvmCounterConfig
+{
+ public:
+ /**
+ * Initialize PerfEvent counter configuration structure
+ *
+ * PerfEvent has the concept of counter types, which is a way to
+ * abstract hardware performance counters or access software
+ * events. The type field in the configuration specifies what type
+ * of counter this is. For hardware performance counters, it's
+ * typically PERF_TYPE_HARDWARE, PERF_TYPE_HW_CACHE, or
+ * PERF_TYPE_RAW.
+ *
+ * The 'config' field has different meanings depending on the type
+ * of counter. Possible values are listed in perf_event.h in the
+ * kernel headers. When using raw counters, the value is the raw
+ * value written to the performance counter configuration register
+ * (some bits dealing with sampling and similar features are
+ * usually masked).
+ *
+ * @param type Counter type.
+ * @param config Counter configuration
+ */
+ PerfKvmCounterConfig(uint32_t type, uint64_t config);
+ ~PerfKvmCounterConfig();
+
+ /**
+ * Set the initial sample period (overflow count) of an event. If
+ * this is set to 0, the event acts as a normal counting event and
+ * does not trigger overflows.
+ *
+ * @param period Number of counter events before the counter
+ * overflows
+ */
+ PerfKvmCounterConfig &samplePeriod(uint64_t period) {
+ attr.freq = 0;
+ attr.sample_period = period;
+ return *this;
+ }
+
+ /**
+ * Set the number of samples that need to be triggered before
+ * reporting data as being available on the perf event
+ * FD. Defaults to 0, which disables overflow reporting.
+ *
+ * @param events Number of overflows before signaling a wake up
+ */
+ PerfKvmCounterConfig &wakeupEvents(uint32_t events) {
+ attr.watermark = 0;
+ attr.wakeup_events = events;
+ return *this;
+ }
+
+ /**
+ * Don't start the performance counter automatically when
+ * attaching it.
+ *
+ * @param val true to disable, false to enable the counter
+ */
+ PerfKvmCounterConfig &disabled(bool val) {
+ attr.disabled = val;
+ return *this;
+ }
+
+ /**
+ * Force the group to be on the active all the time (i.e.,
+ * disallow multiplexing).
+ *
+ * Only applies to group leaders.
+ *
+ * @param val true to pin the counter
+ */
+ PerfKvmCounterConfig &pinned(bool val) {
+ attr.pinned = val;
+ return *this;
+ }
+
+ /** Underlying perf_event_attr structure describing the counter */
+ struct perf_event_attr attr;
+};
+
+/**
+ * An instance of a performance counter.
+ */
+class PerfKvmCounter
+{
+public:
+ /**
+ * Create and attach a new counter group.
+ *
+ * @param config Counter configuration
+ * @param tid Thread to sample (0 indicates current thread)
+ */
+ PerfKvmCounter(PerfKvmCounterConfig &config, pid_t tid);
+ /**
+ * Create and attach a new counter and make it a member of an
+ * exist counter group.
+ *
+ * @param config Counter configuration
+ * @param tid Thread to sample (0 indicates current thread)
+ * @param parent Group leader
+ */
+ PerfKvmCounter(PerfKvmCounterConfig &config,
+ pid_t tid, const PerfKvmCounter &parent);
+ /**
+ * Create a new counter, but don't attach it.
+ */
+ PerfKvmCounter();
+ ~PerfKvmCounter();
+
+
+ /**
+ * Attach a counter.
+ *
+ * @note This operation is only supported if the counter isn't
+ * already attached.
+ *
+ * @param config Counter configuration
+ * @param tid Thread to sample (0 indicates current thread)
+ */
+ void attach(PerfKvmCounterConfig &config, pid_t tid) {
+ attach(config, tid, -1);
+ }
+
+ /**
+ * Attach a counter and make it a member of an existing counter
+ * group.
+ *
+ * @note This operation is only supported if the counter isn't
+ * already attached.
+ *
+ * @param config Counter configuration
+ * @param tid Thread to sample (0 indicates current thread)
+ * @param parent Group leader
+ */
+ void attach(PerfKvmCounterConfig &config,
+ pid_t tid, const PerfKvmCounter &parent) {
+ attach(config, tid, parent.fd);
+ }
+
+ /** Detach a counter from PerfEvent. */
+ void detach();
+
+ /** Check if a counter is attached. */
+ bool attached() const { return fd != -1; }
+
+ /**
+ * Start counting.
+ *
+ * @note If this counter is a group leader, it will start the
+ * entire group.
+ */
+ void start();
+
+ /**
+ * Stop counting.
+ *
+ * @note If this counter is a group leader, it will stop the
+ * entire group.
+ */
+ void stop();
+
+ /**
+ * Update the period of an overflow counter.
+ *
+ * @warning This ioctl has some pretty bizarre semantics. It seems
+ * like the new period isn't effective until after the next
+ * counter overflow. If you use this method to change the sample
+ * period, you will see one sample with the old period and then
+ * start sampling with the new period.
+ *
+ * @warning This method doesn't work at all on some 2.6.3x kernels
+ * since it has inverted check for the return value when copying
+ * parameters from userspace.
+ *
+ * @param period Overflow period in events
+ */
+ void period(uint64_t period);
+
+ /**
+ * Enable a counter for a fixed number of events.
+ *
+ * When this method is called, perf event enables the counter if
+ * it was disabled. It then leaves the counter enabled until it
+ * has overflowed a refresh times.
+ *
+ * @note This does not update the period of the counter.
+ *
+ * @param refresh Number of overflows before disabling the
+ * counter.
+ */
+ void refresh(int refresh);
+
+ /**
+ * Read the current value of a counter.
+ */
+ uint64_t read() const;
+
+ /**
+ * Enable signal delivery to a thread on counter overflow.
+ *
+ * @param tid Thread to deliver signal to
+ * @param signal Signal to send upon overflow
+ */
+ void enableSignals(pid_t tid, int signal);
+
+ /**
+ * Enable signal delivery on counter overflow. Identical to
+ * enableSignals(pid_t) when called with the current TID as its
+ * parameter.
+ *
+ * @param signal Signal to send upon overflow
+ */
+ void enableSignals(int signal) { enableSignals(gettid(), signal); }
+
+private:
+ // Disallow copying
+ PerfKvmCounter(const PerfKvmCounter &that);
+ // Disallow assignment
+ PerfKvmCounter &operator=(const PerfKvmCounter &that);
+
+ void attach(PerfKvmCounterConfig &config, pid_t tid, int group_fd);
+
+ /**
+ * Get the TID of the current thread.
+ *
+ * @return Current thread's TID
+ */
+ pid_t gettid();
+
+ /**
+ * MMAP the PerfEvent file descriptor.
+ *
+ * @note We currently don't use the ring buffer, but PerfEvent
+ * requires this to be mapped for overflow handling to work.
+ *
+ * @note Overflow handling requires at least one buf_page to be
+ * mapped.
+ *
+ * @param pages number of pages in circular sample buffer. Must be
+ * an even power of 2.
+ */
+ void mmapPerf(int pages);
+
+ /** @{ */
+ /**
+ * PerfEvent fnctl interface.
+ *
+ * @param cmd fcntl command
+ * @param p1 Request parameter
+ *
+ * @return -1 on error (error number in errno), ioctl dependent
+ * value otherwise.
+ */
+ int fcntl(int cmd, long p1);
+ int fcntl(int cmd, void *p1) { return fcntl(cmd, (long)p1); }
+ /** @} */
+
+ /** @{ */
+ /**
+ * PerfEvent ioctl interface.
+ *
+ * @param request PerfEvent request
+ * @param p1 Optional request parameter
+ *
+ * @return -1 on error (error number in errno), ioctl dependent
+ * value otherwise.
+ */
+ int ioctl(int request, long p1);
+ int ioctl(int request, void *p1) { return ioctl(request, (long)p1); }
+ int ioctl(int request) { return ioctl(request, 0L); }
+ /** @} */
+
+ /**
+ * Perform a read from the counter file descriptor.
+ *
+ * @param buf Destination buffer
+ * @param size Amount of data to read
+ */
+ void read(void *buf, size_t size) const;
+
+ /**
+ * PerfEvent file descriptor associated with counter. -1 if not
+ * attached to PerfEvent.
+ */
+ int fd;
+
+ /** Memory mapped PerfEvent sample ring buffer */
+ struct perf_event_mmap_page *ringBuffer;
+ /** Total number of pages in ring buffer */
+ int ringNumPages;
+
+ /** Cached host page size */
+ long pageSize;
+};
+
+#endif
--- /dev/null
+/*
+ * 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
+ */
+
+#include <csignal>
+#include <ctime>
+
+#include "base/misc.hh"
+#include "base/trace.hh"
+#include "cpu/kvm/timer.hh"
+#include "debug/KvmTimer.hh"
+
+
+PosixKvmTimer::PosixKvmTimer(int signo, clockid_t clockID,
+ float hostFactor, Tick hostFreq)
+ : BaseKvmTimer(signo, hostFactor, hostFreq),
+ clockID(clockID)
+{
+ struct sigevent sev;
+
+ // TODO: We should request signal delivery to thread instead of
+ // the process here. Unfortunately this seems to be broken, or at
+ // least not work as specified in the man page.
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = signo;
+ sev.sigev_value.sival_ptr = NULL;
+ if (timer_create(clockID, &sev, &timer) == -1)
+ panic("timer_create");
+}
+
+PosixKvmTimer::~PosixKvmTimer()
+{
+ timer_delete(timer);
+}
+
+void
+PosixKvmTimer::arm(Tick ticks)
+{
+ struct itimerspec ts;
+ memset(&ts, 0, sizeof(ts));
+
+ ts.it_interval.tv_sec = 0;
+ ts.it_interval.tv_nsec = 0;
+ ts.it_value.tv_sec = hostNs(ticks) / 1000000000ULL;
+ ts.it_value.tv_nsec = hostNs(ticks) % 1000000000ULL;
+
+ DPRINTF(KvmTimer, "Arming POSIX timer: %i ticks (%is%ins)\n",
+ ticks, ts.it_value.tv_sec, ts.it_value.tv_nsec);
+
+ if (timer_settime(timer, 0, &ts, NULL) == -1)
+ panic("PosixKvmTimer: Failed to arm timer\n");
+}
+
+void
+PosixKvmTimer::disarm()
+{
+ struct itimerspec ts;
+ memset(&ts, 0, sizeof(ts));
+
+ DPRINTF(KvmTimer, "Disarming POSIX timer\n");
+
+ if (timer_settime(timer, 0, &ts, NULL) == -1)
+ panic("PosixKvmTimer: Failed to disarm timer\n");
+}
+
+Tick
+PosixKvmTimer::calcResolution()
+{
+ struct timespec ts;
+
+ if (clock_getres(clockID, &ts) == -1)
+ panic("PosixKvmTimer: Failed to get timer resolution\n");
+
+ Tick resolution(ticksFromHostNs(ts.tv_sec * 1000000000ULL + ts.tv_nsec));
+
+ return resolution;
+}
--- /dev/null
+/*
+ * 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_TIMER_HH__
+#define __CPU_KVM_TIMER_HH__
+
+#include <ctime>
+
+#include "sim/core.hh"
+
+/**
+ * Timer functions to interrupt VM execution after a number of
+ * simulation ticks. The timer allows scaling of the host time to take
+ * performance differences between the simulated and real CPU into
+ * account.
+ *
+ * The performance scaling factor is ratio between the target's CPI
+ * and the host's CPI. It is larger than 1 if the host is faster than
+ * the target and lower than 1 if it is slower.
+ *
+ * When the timer times out, it sends a signal to the thread that
+ * started the timer. The signal forces KVM to drop out of the system
+ * call that started the guest and hands control to gem5.
+ */
+class BaseKvmTimer
+{
+ public:
+ /**
+ * Setup basic timer functionality shared by all timer
+ * implementations.
+ *
+ * @param signo Signal to deliver
+ * @param hostFactor Performance scaling factor
+ * @param hostFreq Clock frequency of the host
+ */
+ BaseKvmTimer(int signo, float hostFactor, Tick hostFreq)
+ : signo(signo), _resolution(0),
+ hostFactor(hostFactor), hostFreq(hostFreq) {};
+ virtual ~BaseKvmTimer() {};
+
+ /**
+ * Arm the timer so that it fires after a certain number of ticks.
+ *
+ * @note A timer implementation is free to convert between
+ * simulation ticks and virtualized time using any method it
+ * chooses. The accuracy of the timer therefore depends on what it
+ * measures, an accurate timer implementation should measure the
+ * number of cycles or instructions executed in the guest. If such
+ * counters are unavailable, it may fallback to wall clock time.
+ *
+ * @param ticks Number of ticks until the timer fires
+ */
+ virtual void arm(Tick ticks) = 0;
+ /**
+ * Disarm the timer.
+ *
+ * When this method has returned, the timer may no longer deliver
+ * signals upon timeout.
+ */
+ virtual void disarm() = 0;
+
+ /**
+ * Determine the resolution of the timer in ticks. This method is
+ * mainly used to determine the smallest number of ticks the timer
+ * can wait before triggering a signal.
+ *
+ * @return Minimum number of ticks the timer can resolve
+ */
+ Tick resolution() {
+ if (_resolution == 0)
+ _resolution = calcResolution();
+ return _resolution;
+ }
+
+ /**
+ * Convert cycles executed on the host into Ticks executed in the
+ * simulator. Scales the results using the hostFactor to take CPU
+ * performance differences into account.
+ *
+ * @return Host cycles executed in VM converted to simulation ticks
+ */
+ Tick ticksFromHostCycles(uint64_t cycles) {
+ return cycles * hostFactor * hostFreq;
+ }
+
+ /**
+ * Convert nanoseconds executed on the host into Ticks executed in
+ * the simulator. Scales the results using the hostFactor to take
+ * CPU performance differences into account.
+ *
+ * @return Nanoseconds executed in VM converted to simulation ticks
+ */
+ Tick ticksFromHostNs(uint64_t ns) {
+ return ns * hostFactor * SimClock::Float::ns;
+ }
+
+ protected:
+ /**
+ * Calculate the timer resolution, used by resolution() which
+ * caches the result.
+ *
+ * @return Minimum number of ticks the timer can resolve
+ */
+ virtual Tick calcResolution() = 0;
+
+ /**
+ * Convert a time in simulator ticks to host nanoseconds.
+ *
+ * @return Simulation ticks converted into nanoseconds on the host
+ */
+ uint64_t hostNs(Tick ticks) {
+ return ticks / (SimClock::Float::ns * hostFactor);
+ }
+
+ /**
+ * Convert a time in simulator ticks to host cycles
+ *
+ *
+ * @return Simulation ticks converted into CPU cycles on the host
+ */
+ uint64_t hostCycles(Tick ticks) {
+ return ticks / (hostFreq * hostFactor);
+ }
+
+ /** Signal to deliver when the timer times out */
+ int signo;
+
+ private:
+ /** Cached resolution */
+ mutable Tick _resolution;
+
+ /** Performance scaling factor */
+ float hostFactor;
+ /** Host frequency */
+ Tick hostFreq;
+};
+
+/**
+ * Timer based on standard POSIX timers. The POSIX timer API supports
+ * several different clock with different characteristics.
+ *
+ * @note It might be tempting to use
+ * CLOCK_(THREAD|PROCESS)_CPUTIME_ID, however, this clock usually has
+ * much lower resolution than the real-time clocks.
+ */
+class PosixKvmTimer : public BaseKvmTimer
+{
+ public:
+ /**
+ * @param signo Signal to deliver
+ * @param clockID ID of the clock to use
+ * @param hostFactor Performance scaling factor
+ * @param hostFreq Clock frequency of the host
+ */
+ PosixKvmTimer(int signo, clockid_t clockID,
+ float hostFactor, Tick hostFreq);
+ ~PosixKvmTimer();
+
+ void arm(Tick ticks);
+ void disarm();
+
+ protected:
+ Tick calcResolution();
+
+ private:
+ clockid_t clockID;
+ timer_t timer;
+};
+
+#endif
--- /dev/null
+/*
+ * 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
+ */
+
+#include <linux/kvm.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cerrno>
+
+#include "cpu/kvm/vm.hh"
+#include "debug/Kvm.hh"
+#include "params/KvmVM.hh"
+#include "sim/system.hh"
+
+#define EXPECTED_KVM_API_VERSION 12
+
+#if EXPECTED_KVM_API_VERSION != KVM_API_VERSION
+#error Unsupported KVM version
+#endif
+
+Kvm *Kvm::instance = NULL;
+
+Kvm::Kvm()
+ : kvmFD(-1), apiVersion(-1), vcpuMMapSize(0)
+{
+ kvmFD = ::open("/dev/kvm", O_RDWR);
+ if (kvmFD == -1)
+ fatal("KVM: Failed to open /dev/kvm\n");
+
+ apiVersion = ioctl(KVM_GET_API_VERSION);
+ if (apiVersion != EXPECTED_KVM_API_VERSION)
+ fatal("KVM: Incompatible API version\n");
+
+ vcpuMMapSize = ioctl(KVM_GET_VCPU_MMAP_SIZE);
+ if (vcpuMMapSize == -1)
+ panic("KVM: Failed to get virtual CPU MMAP size\n");
+}
+
+Kvm::~Kvm()
+{
+ close(kvmFD);
+}
+
+Kvm *
+Kvm::create()
+{
+ if (!instance)
+ instance = new Kvm();
+
+ return instance;
+}
+
+bool
+Kvm::capUserMemory() const
+{
+ return checkExtension(KVM_CAP_USER_MEMORY) != 0;
+}
+
+bool
+Kvm::capSetTSSAddress() const
+{
+ return checkExtension(KVM_CAP_SET_TSS_ADDR) != 0;
+}
+
+bool
+Kvm::capExtendedCPUID() const
+{
+ return checkExtension(KVM_CAP_EXT_CPUID) != 0;
+}
+
+bool
+Kvm::capUserNMI() const
+{
+#ifdef KVM_CAP_USER_NMI
+ return checkExtension(KVM_CAP_USER_NMI) != 0;
+#else
+ return false;
+#endif
+}
+
+int
+Kvm::capCoalescedMMIO() const
+{
+ return checkExtension(KVM_CAP_COALESCED_MMIO);
+}
+
+bool
+Kvm::capOneReg() const
+{
+#ifdef KVM_CAP_ONE_REG
+ return checkExtension(KVM_CAP_ONE_REG) != 0;
+#else
+ return false;
+#endif
+}
+
+bool
+Kvm::capIRQChip() const
+{
+ return checkExtension(KVM_CAP_IRQCHIP) != 0;
+}
+
+bool
+Kvm::getSupportedCPUID(struct kvm_cpuid2 &cpuid) const
+{
+#if defined(__i386__) || defined(__x86_64__)
+ if (ioctl(KVM_GET_SUPPORTED_CPUID, (void *)&cpuid) == -1) {
+ if (errno == E2BIG)
+ return false;
+ else
+ panic("KVM: Failed to get supported CPUID (errno: %i)\n", errno);
+ } else
+ return true;
+#else
+ panic("KVM: getSupportedCPUID is unsupported on this platform.\n");
+#endif
+}
+
+int
+Kvm::checkExtension(int extension) const
+{
+ int ret = ioctl(KVM_CHECK_EXTENSION, extension);
+ if (ret == -1)
+ panic("KVM: ioctl failed when checking for extension\n");
+ return ret;
+}
+
+int
+Kvm::ioctl(int request, long p1) const
+{
+ assert(kvmFD != -1);
+
+ return ::ioctl(kvmFD, request, p1);
+}
+
+int
+Kvm::createVM()
+{
+ int vmFD;
+
+ vmFD = ioctl(KVM_CREATE_VM);
+ if (vmFD == -1)
+ panic("Failed to create KVM VM\n");
+
+ return vmFD;
+}
+
+
+KvmVM::KvmVM(KvmVMParams *params)
+ : SimObject(params),
+ kvm(), system(params->system),
+ vmFD(kvm.createVM()),
+ started(false),
+ nextVCPUID(0)
+{
+ /* Setup the coalesced MMIO regions */
+ for (int i = 0; i < params->coalescedMMIO.size(); ++i)
+ coalesceMMIO(params->coalescedMMIO[i]);
+}
+
+KvmVM::~KvmVM()
+{
+ close(vmFD);
+}
+
+void
+KvmVM::cpuStartup()
+{
+ if (started)
+ return;
+ started = true;
+
+ delayedStartup();
+}
+
+void
+KvmVM::delayedStartup()
+{
+ const std::vector<std::pair<AddrRange, uint8_t*> >&memories(
+ system->getPhysMem().getBackingStore());
+
+ DPRINTF(Kvm, "Mapping %i memory region(s)\n", memories.size());
+ for (int slot(0); slot < memories.size(); ++slot) {
+ const AddrRange &range(memories[slot].first);
+ void *pmem(memories[slot].second);
+
+ if (pmem) {
+ DPRINTF(Kvm, "Mapping region: 0x%p -> 0x%llx [size: 0x%llx]\n",
+ pmem, range.start(), range.size());
+
+ setUserMemoryRegion(slot, pmem, range, 0 /* flags */);
+ } else {
+ DPRINTF(Kvm, "Zero-region not mapped: [0x%llx]\n", range.start());
+ hack("KVM: Zero memory handled as IO\n");
+ }
+ }
+}
+
+void
+KvmVM::setUserMemoryRegion(uint32_t slot,
+ void *host_addr, AddrRange guest_range,
+ uint32_t flags)
+{
+ if (guest_range.interleaved())
+ panic("Tried to map an interleaved memory range into a KVM VM.\n");
+
+ setUserMemoryRegion(slot, host_addr,
+ guest_range.start(), guest_range.size(),
+ flags);
+}
+
+void
+KvmVM::setUserMemoryRegion(uint32_t slot,
+ void *host_addr, Addr guest_addr,
+ uint64_t len, uint32_t flags)
+{
+ struct kvm_userspace_memory_region m;
+
+ memset(&m, 0, sizeof(m));
+ m.slot = slot;
+ m.flags = flags;
+ m.guest_phys_addr = (uint64_t)guest_addr;
+ m.memory_size = len;
+ m.userspace_addr = (__u64)host_addr;
+
+ if (ioctl(KVM_SET_USER_MEMORY_REGION, (void *)&m) == -1) {
+ panic("Failed to setup KVM memory region:\n"
+ "\tHost Address: 0x%p\n"
+ "\tGuest Address: 0x%llx\n",
+ "\tSize: %ll\n",
+ "\tFlags: 0x%x\n",
+ m.userspace_addr, m.guest_phys_addr,
+ m.memory_size, m.flags);
+ }
+}
+
+void
+KvmVM::coalesceMMIO(const AddrRange &range)
+{
+ coalesceMMIO(range.start(), range.size());
+}
+
+void
+KvmVM::coalesceMMIO(Addr start, int size)
+{
+ struct kvm_coalesced_mmio_zone zone;
+
+ zone.addr = start;
+ zone.size = size;
+ zone.pad = 0;
+
+ DPRINTF(Kvm, "KVM: Registering coalesced MMIO region [0x%x, 0x%x]\n",
+ zone.addr, zone.addr + zone.size - 1);
+ if (ioctl(KVM_REGISTER_COALESCED_MMIO, (void *)&zone) == -1)
+ panic("KVM: Failed to register coalesced MMIO region (%i)\n",
+ errno);
+}
+
+void
+KvmVM::setTSSAddress(Addr tss_address)
+{
+ if (ioctl(KVM_SET_TSS_ADDR, (unsigned long)tss_address) == -1)
+ panic("KVM: Failed to set VM TSS address\n");
+}
+
+void
+KvmVM::createIRQChip()
+{
+ if (_hasKernelIRQChip)
+ panic("KvmVM::createIRQChip called twice.\n");
+
+ if (ioctl(KVM_CREATE_IRQCHIP) != -1) {
+ _hasKernelIRQChip = true;
+ } else {
+ warn("KVM: Failed to create in-kernel IRQ chip (errno: %i)\n",
+ errno);
+ _hasKernelIRQChip = false;
+ }
+}
+
+void
+KvmVM::setIRQLine(uint32_t irq, bool high)
+{
+ struct kvm_irq_level kvm_level;
+
+ kvm_level.irq = irq;
+ kvm_level.level = high ? 1 : 0;
+
+ if (ioctl(KVM_IRQ_LINE, &kvm_level) == -1)
+ panic("KVM: Failed to set IRQ line level (errno: %i)\n",
+ errno);
+}
+
+int
+KvmVM::createVCPU(long vcpuID)
+{
+ int fd;
+
+ fd = ioctl(KVM_CREATE_VCPU, vcpuID);
+ if (fd == -1)
+ panic("KVM: Failed to create virtual CPU");
+
+ return fd;
+}
+
+long
+KvmVM::allocVCPUID()
+{
+ return nextVCPUID++;
+}
+
+int
+KvmVM::ioctl(int request, long p1) const
+{
+ assert(vmFD != -1);
+
+ return ::ioctl(vmFD, request, p1);
+}
+
+
+KvmVM *
+KvmVMParams::create()
+{
+ static bool created = false;
+ if (created)
+ warn_once("Use of multiple KvmVMs is currently untested!\n");
+
+ created = true;
+
+ return new KvmVM(this);
+}
--- /dev/null
+/*
+ * 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_KVMVM_HH__
+#define __CPU_KVM_KVMVM_HH__
+
+#include "base/addr_range.hh"
+#include "sim/sim_object.hh"
+
+// forward declarations
+struct KvmVMParams;
+class System;
+
+/**
+ * @defgroup KvmInterrupts KVM Interrupt handling.
+ *
+ * These methods control interrupt delivery to the guest system.
+ */
+
+/**
+ * @defgroup KvmIoctl KVM low-level ioctl interface.
+ *
+ * These methods provide a low-level interface to the underlying KVM
+ * layer.
+ */
+
+/**
+ * KVM parent interface
+ *
+ * The main Kvm object is used to provide functionality that is not
+ * specific to a VM or CPU. For example, it allows checking of the
+ * optional features and creation of VM containers.
+ */
+class Kvm
+{
+ friend class KvmVM;
+
+ public:
+ virtual ~Kvm();
+
+ Kvm *create();
+
+ /** Get the version of the KVM API implemented by the kernel. */
+ int getAPIVersion() const { return apiVersion; }
+ /**
+ * Get the size of the MMAPed parameter area used to communicate
+ * vCPU parameters between the kernel and userspace. This area,
+ * amongst other things, contains the kvm_run data structure.
+ */
+ int getVCPUMMapSize() const { return vcpuMMapSize; }
+
+ /** @{ */
+ /** Support for KvmVM::setUserMemoryRegion() */
+ bool capUserMemory() const;
+ /** Support for KvmVM::setTSSAddress() */
+ bool capSetTSSAddress() const;
+ /** Support for BaseKvmCPU::setCPUID2 and getSupportedCPUID(). */
+ bool capExtendedCPUID() const;
+ /** Support for BaseKvmCPU::kvmNonMaskableInterrupt(). */
+ bool capUserNMI() const;
+
+ /**
+ * Check if coalesced MMIO is supported and which page in the
+ * MMAP'ed structure it stores requests in.
+ *
+ * @return Offset (in pages) into the mmap'ed vCPU area where the
+ * MMIO buffer is stored. 0 if unsupported.
+ */
+ int capCoalescedMMIO() const;
+
+ /**
+ * Support for reading and writing single registers.
+ *
+ * @see BaseKvmCPU::getOneReg(), and BaseKvmCPU::setOneReg()
+ */
+ bool capOneReg() const;
+
+ /**
+ * Support for creating an in-kernel IRQ chip model.
+ *
+ * @see KvmVM::createIRQChip()
+ */
+ bool capIRQChip() const;
+ /** @} */
+
+ /**
+ * Get the CPUID features supported by the hardware and Kvm.
+ *
+ * @note Requires capExtendedCPUID().
+ *
+ * @return False if the allocation is too small, true on success.
+ */
+ bool getSupportedCPUID(struct kvm_cpuid2 &cpuid) const;
+
+ protected:
+ /**
+ * Check for the presence of an extension to the KVM API.
+ *
+ * The return value depends on the extension, but is always zero
+ * if it is unsupported or positive otherwise. Some extensions use
+ * the return value provide additional data about the extension.
+ *
+ * @return 0 if the extension is unsupported, positive integer
+ * otherwise.
+ */
+ int checkExtension(int extension) const;
+
+ /**
+ * @addtogroup KvmIoctl
+ * @{
+ */
+ /**
+ * Main VM ioctl interface.
+ *
+ * @param request KVM 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);
+ }
+ /** @} */
+
+ private:
+ // This object is a singleton, so prevent instantiation.
+ Kvm();
+
+ // Prevent copying
+ Kvm(const Kvm &kvm);
+ // Prevent assignment
+ Kvm &operator=(const Kvm &kvm);
+
+ /**
+ * Create a KVM Virtual Machine
+ *
+ * @return File descriptor pointing to the VM
+ */
+ int createVM();
+
+ /** KVM VM file descriptor */
+ int kvmFD;
+ /** KVM API version */
+ int apiVersion;
+ /** Size of the MMAPed vCPU parameter area. */
+ int vcpuMMapSize;
+
+ /** Singleton instance */
+ static Kvm *instance;
+};
+
+/**
+ * KVM VM container
+ *
+ * A KVM VM container normally contains all the CPUs in a shared
+ * memory machine. The VM container handles things like physical
+ * memory and to some extent interrupts. Normally, the VM API is only
+ * used for interrupts when the PIC is emulated by the kernel, which
+ * is a feature we do not use. However, some architectures (notably
+ * ARM) use the VM interface to deliver interrupts to specific CPUs as
+ * well.
+ *
+ * VM initialization is a bit different from that of other
+ * SimObjects. When we initialize the VM, we discover all physical
+ * memory mappings in the system. Since AbstractMem::unserialize
+ * re-maps the guests memory, we need to make sure that this is done
+ * after the memory has been re-mapped, but before the vCPUs are
+ * initialized (KVM requires memory mappings to be setup before CPUs
+ * can be created). Normally, we would just initialize the VM in
+ * init() or startup(), however, we can not use init() since this is
+ * called before AbstractMem::unserialize() and we can not use
+ * startup() since it must be called before BaseKvmCPU::startup() and
+ * the simulator framework does not guarantee call order. We therefore
+ * call cpuStartup() from BaseKvmCPU::startup() instead and execute
+ * the initialization code once when the first CPU in the VM is
+ * starting.
+ */
+class KvmVM : public SimObject
+{
+ friend class BaseKvmCPU;
+
+ public:
+ KvmVM(KvmVMParams *params);
+ virtual ~KvmVM();
+
+ /**
+ * Setup a shared three-page memory region used by the internals
+ * of KVM. This is currently only needed by x86 implementations.
+ *
+ * @param tss_address Physical address of the start of the TSS
+ */
+ void setTSSAddress(Addr tss_address);
+
+ /** @{ */
+ /**
+ * Request coalescing MMIO for a memory range.
+ *
+ * @param start Physical start address in guest
+ * @param size Size of the MMIO region
+ */
+ void coalesceMMIO(Addr start, int size);
+
+ /**
+ * Request coalescing MMIO for a memory range.
+ *
+ * @param range Coalesced MMIO range
+ */
+ void coalesceMMIO(const AddrRange &range);
+ /** @} */
+
+ /**
+ * @addtogroup KvmInterrupts
+ * @{
+ */
+ /**
+ * Create an in-kernel interrupt controller
+ *
+ * @note This functionality depends on Kvm::capIRQChip().
+ */
+ void createIRQChip();
+
+ /**
+ * Set the status of an IRQ line using KVM_IRQ_LINE.
+ *
+ * @note This ioctl is usually only used if the interrupt
+ * controller is emulated by the kernel (i.e., after calling
+ * createIRQChip()). Some architectures (e.g., ARM) use it instead
+ * of BaseKvmCPU::kvmInterrupt().
+ *
+ * @param irq Interrupt number
+ * @param high Line level (true for high, false for low)
+ */
+ void setIRQLine(uint32_t irq, bool high);
+
+ /**
+ * Is in-kernel IRQ chip emulation enabled?
+ */
+ bool hasKernelIRQChip() const { return _hasKernelIRQChip; }
+ /** @} */
+
+ /** Global KVM interface */
+ Kvm kvm;
+
+ protected:
+ /**
+ * VM CPU initialization code.
+ *
+ * This method is called from BaseKvmCPU::startup() when a CPU in
+ * the VM executes its BaseKvmCPU::startup() method. The first
+ * time method is executed on a VM, it calls the delayedStartup()
+ * method.
+ */
+ void cpuStartup();
+
+ /**
+ * Delayed initialization, executed once before the first CPU
+ * starts.
+ *
+ * This method provides a way to do VM initialization once before
+ * the first CPU in a VM starts. It is needed since some resources
+ * (e.g., memory mappings) can change in the normal
+ * SimObject::startup() path. Since the call order of
+ * SimObject::startup() is not guaranteed, we simply defer some
+ * initialization until a CPU is about to start.
+ */
+ void delayedStartup();
+
+
+ /** @{ */
+ /**
+ * Setup a region of physical memory in the guest
+ *
+ * @param slot KVM memory slot ID (must be unique)
+ * @param host_addr Memory allocation backing the memory
+ * @param guest_addr Address in the guest
+ * @param guest_range Address range used by guest.
+ * @param len Size of the allocation in bytes
+ * @param flags Flags (see the KVM API documentation)
+ */
+ void setUserMemoryRegion(uint32_t slot,
+ void *host_addr, Addr guest_addr,
+ uint64_t len, uint32_t flags);
+ void setUserMemoryRegion(uint32_t slot,
+ void *host_addr, AddrRange guest_range,
+ uint32_t flags);
+ /** @} */
+
+ /**
+ * Create a new vCPU within a VM.
+ *
+ * @param vcpuID ID of the new CPU within the VM.
+ * @return File descriptor referencing the CPU.
+ */
+ int createVCPU(long vcpuID);
+
+ /**
+ * Allocate a new vCPU ID within the VM.
+ *
+ * The returned vCPU ID is guaranteed to be unique within the
+ * VM. New IDs are allocated sequentially starting from 0.
+ *
+ * @return ID of the new vCPU
+ */
+ long allocVCPUID();
+
+ /**
+ * @addtogroup KvmIoctl
+ * @{
+ */
+ /**
+ * KVM VM ioctl interface.
+ *
+ * @param request KVM VM 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);
+ }
+ /**@}*/
+
+ private:
+ // Prevent copying
+ KvmVM(const KvmVM &vm);
+ // Prevent assignment
+ KvmVM &operator=(const KvmVM &vm);
+
+ System *system;
+
+ /** KVM VM file descriptor */
+ const int vmFD;
+
+ /** Has delayedStartup() already been called? */
+ bool started;
+
+ /** Do we have in-kernel IRQ-chip emulation enabled? */
+ bool _hasKernelIRQChip;
+
+ /** Next unallocated vCPU ID */
+ long nextVCPUID;
+};
+
+#endif