blob: e5e9d80f846ff48cfcbd1e01dae87e1d0c1fbb99 [file] [log] [blame] [edit]
/*
* Copyright 2014 Google, Inc.
* Copyright (c) 2012, 2015 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.
*/
#ifndef __CPU_KVM_KVMVM_HH__
#define __CPU_KVM_KVMVM_HH__
#include <vector>
#include "base/addr_range.hh"
#include "sim/sim_object.hh"
struct kvm_cpuid_entry2;
struct kvm_cpuid2;
struct kvm_msr_list;
struct kvm_vcpu_init;
namespace gem5
{
// forward declarations
struct KvmVMParams;
class BaseKvmCPU;
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;
/**
* Attempt to determine how many memory slots are available. If it can't
* be determined, this function returns 0.
*/
int capNumMemSlots() 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;
/** Support for getting and setting the kvm_vcpu_events structure. */
bool capVCPUEvents() const;
/** Support for getting and setting the kvm_debugregs structure. */
bool capDebugRegs() const;
/** Support for getting and setting the x86 XCRs. */
bool capXCRs() const;
/** Support for getting and setting the kvm_xsave structure. */
bool capXSave() const;
/** Support for ARM IRQ line layout 2 **/
bool capIRQLineLayout2() const;
/** @} */
#if defined(__i386__) || defined(__x86_64__)
public: // x86-specific
/**
* @{
* @name X86-specific APIs
*/
typedef std::vector<struct kvm_cpuid_entry2> CPUIDVector;
typedef std::vector<uint32_t> MSRIndexVector;
/**
* 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;
/**
* Get the CPUID features supported by the hardware and Kvm.
*
* @note Requires capExtendedCPUID().
*
* @note This method uses an internal cache to minimize the number
* of calls into the kernel.
*
* @return Reference to cached MSR index list.
*/
const CPUIDVector &getSupportedCPUID() const;
/**
* Get the MSRs supported by the hardware and Kvm.
*
* @return False if the allocation is too small, true on success.
*/
bool getSupportedMSRs(struct kvm_msr_list &msrs) const;
/**
* Get the MSRs supported by the hardware and Kvm.
*
* @note This method uses an internal cache to minimize the number
* of calls into the kernel.
*
* @return Reference to cached MSR index list.
*/
const MSRIndexVector &getSupportedMSRs() const;
private: // x86-specific
/** Cached vector of supported CPUID entries. */
mutable CPUIDVector supportedCPUIDCache;
/** Cached vector of supported MSRs. */
mutable MSRIndexVector supportedMSRCache;
/** @} */
#endif
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(const KvmVMParams &params);
virtual ~KvmVM();
void notifyFork();
/**
* 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; }
/**
* Tell the VM and VCPUs to use an in-kernel IRQ chip for
* interrupt delivery.
*
* @note This is set automatically if the IRQ chip is created
* using the KvmVM::createIRQChip() API.
*/
void enableKernelIRQChip() { _hasKernelIRQChip = true; }
/** @} */
struct MemSlot
{
MemSlot(uint32_t _num) : num(_num)
{}
MemSlot() : num(-1)
{}
int32_t num;
};
/**
* Allocate a memory slot within the VM.
*/
const MemSlot allocMemSlot(uint64_t size);
/**
* Setup a region of physical memory in the guest
*
* @param slot KVM memory slot ID returned by allocMemSlot
* @param host_addr Memory allocation backing the memory
* @param guest_addr Address in the guest
* @param flags Flags (see the KVM API documentation)
*/
void setupMemSlot(const MemSlot slot, void *host_addr, Addr guest_addr,
uint32_t flags);
/**
* Disable a memory slot.
*/
void disableMemSlot(const MemSlot slot);
/**
* Free a previously allocated memory slot.
*/
void freeMemSlot(const MemSlot slot);
/**
* Create an in-kernel device model.
*
* @param type Device type (KVM_DEV_TYPE_xxx)
* @param flags Creation flags (KVM_CREATE_DEVICE_xxx)
* @return Device file descriptor
*/
int createDevice(uint32_t type, uint32_t flags = 0);
/** Global KVM interface */
Kvm *kvm;
/** Verify gem5 configuration will support KVM emulation */
bool validEnvironment() const;
/**
* Get the VCPUID for a given context
*/
long contextIdToVCpuId(ContextID ctx) const;
#if defined(__aarch64__)
public: // ARM-specific
/**
* Ask the kernel for the preferred CPU target to simulate.
*
* When creating an ARM vCPU in Kvm, we need to initialize it with
* a call to BaseArmKvmCPU::kvmArmVCpuInit(). When calling this
* function, we need to know what type of CPU the host has. This
* call sets up the kvm_vcpu_init structure with the values the
* kernel wants.
*
* @param[out] target Target structure to initialize.
*/
void kvmArmPreferredTarget(struct kvm_vcpu_init &target) const;
#endif
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 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);
/** @} */
/**
* 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 */
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;
/**
* Structures tracking memory slots.
*/
class MemorySlot
{
public:
uint64_t size;
uint32_t slot;
bool active;
};
std::vector<MemorySlot> memorySlots;
uint32_t maxMemorySlot;
};
} // namespace gem5
#endif