This change [1] requires performing KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 check. However, checkExtension() is only available within the Kvm class and the KvmVM class. A new function, Kvm::capIRQLineLayout2(), is added for checking the status of KVM_CAP_ARM_IRQ_LINE_LAYOUT_2. This fixes a compilation error on Arm systems. [1] https://gem5-review.googlesource.com/c/public/gem5/+/55964 Change-Id: Ia190e06ab451e0ff8d1c4833cd23b7de8852c6dd Signed-off-by: Hoa Nguyen <hoanguyen@ucdavis.edu> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/59310 Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
565 lines
16 KiB
C++
565 lines
16 KiB
C++
/*
|
|
* 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 ¶ms);
|
|
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
|