| /* |
| * 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. |
| * |
| * Authors: Andreas Sandberg |
| */ |
| |
| #ifndef __CPU_KVM_KVMVM_HH__ |
| #define __CPU_KVM_KVMVM_HH__ |
| |
| #include <vector> |
| |
| #include "base/addr_range.hh" |
| #include "sim/sim_object.hh" |
| |
| // 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; |
| /** @} */ |
| |
| #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(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; |
| |
| /** |
| * Initialize system pointer. Invoked by system object. |
| */ |
| void setSystem(System *s); |
| |
| /** |
| * 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; |
| }; |
| |
| #endif |