GIMKvm.cpp revision fa35e2dfd910e18dbb7f136bfe56030e5116d51c
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/* $Id$ */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/** @file
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * GIM - Guest Interface Manager, KVM implementation.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Copyright (C) 2015 Oracle Corporation
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * This file is part of VirtualBox Open Source Edition (OSE), as
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * available from http://www.virtualbox.org. This file is free software;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * you can redistribute it and/or modify it under the terms of the GNU
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * General Public License (GPL) as published by the Free Software
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Foundation, in version 2 as it comes in the "COPYING" file of the
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/*******************************************************************************
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg* Header Files *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg*******************************************************************************/
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#define LOG_GROUP LOG_GROUP_GIM
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include "GIMInternal.h"
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <iprt/asm-math.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <iprt/assert.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <iprt/err.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <iprt/string.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <iprt/mem.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <iprt/spinlock.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <VBox/vmm/cpum.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <VBox/disopcode.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <VBox/vmm/ssm.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <VBox/vmm/vm.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <VBox/vmm/hm.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <VBox/vmm/pdmapi.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#include <VBox/version.h>
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/*******************************************************************************
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg* Defined Constants And Macros *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg*******************************************************************************/
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/**
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * GIM KVM saved-state version.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#define GIM_KVM_SAVED_STATE_VERSION UINT32_C(1)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/*******************************************************************************
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg* Global Variables *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg*******************************************************************************/
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#ifdef VBOX_WITH_STATISTICS
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg# define GIMKVM_MSRRANGE(a_uFirst, a_uLast, a_szName) \
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName, { 0 }, { 0 }, { 0 }, { 0 } }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#else
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg# define GIMKVM_MSRRANGE(a_uFirst, a_uLast, a_szName) \
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#endif
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/**
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Array of MSR ranges supported by KVM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankgstatic CPUMMSRRANGE const g_aMsrRanges_Kvm[] =
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg{
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg GIMKVM_MSRRANGE(MSR_GIM_KVM_RANGE0_START, MSR_GIM_KVM_RANGE0_END, "KVM range 0"),
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg GIMKVM_MSRRANGE(MSR_GIM_KVM_RANGE1_START, MSR_GIM_KVM_RANGE1_END, "KVM range 1")
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg};
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg#undef GIMKVM_MSRRANGE
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/**
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Initializes the KVM GIM provider.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @returns VBox status code.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @param pVM Pointer to the VM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @param uVersion The interface version this VM should use.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankgVMMR3_INT_DECL(int) gimR3KvmInit(PVM pVM)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg{
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg AssertReturn(pVM, VERR_INVALID_PARAMETER);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg AssertReturn(pVM->gim.s.enmProviderId == GIMPROVIDERID_KVM, VERR_INTERNAL_ERROR_5);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg int rc;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Determine interface capabilities based on the version.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg if (!pVM->gim.s.u32Version)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg {
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /* Basic features. */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->uBaseFeat = 0
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg | GIM_KVM_BASE_FEAT_CLOCK_OLD
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg //| GIM_KVM_BASE_FEAT_NOP_IO_DELAY
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg //| GIM_KVM_BASE_FEAT_MMU_OP
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg | GIM_KVM_BASE_FEAT_CLOCK
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg //| GIM_KVM_BASE_FEAT_ASYNC_PF
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg //| GIM_KVM_BASE_FEAT_STEAL_TIME
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg //| GIM_KVM_BASE_FEAT_PV_EOI
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg | GIM_KVM_BASE_FEAT_PV_UNHALT
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg ;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /* Rest of the features are determined in gimR3KvmInitCompleted(). */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Expose HVP (Hypervisor Present) bit to the guest.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg CPUMSetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_HVP);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Modify the standard hypervisor leaves for KVM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg CPUMCPUIDLEAF HyperLeaf;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg RT_ZERO(HyperLeaf);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uLeaf = UINT32_C(0x40000000);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEax = UINT32_C(0x40000001); /* Minimum value for KVM is 0x40000001. */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEbx = 0x4B4D564B; /* 'KVMK' */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEcx = 0x564B4D56; /* 'VMKV' */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEdx = 0x0000004D; /* 'M000' */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg AssertLogRelRCReturn(rc, rc);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Add KVM specific leaves.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uLeaf = UINT32_C(0x40000001);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEax = pKvm->uBaseFeat;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEbx = 0; /* Reserved */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEcx = 0; /* Reserved */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEdx = 0; /* Reserved */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg AssertLogRelRCReturn(rc, rc);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Insert all MSR ranges of KVM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg for (unsigned i = 0; i < RT_ELEMENTS(g_aMsrRanges_Kvm); i++)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg {
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg rc = CPUMR3MsrRangesInsert(pVM, &g_aMsrRanges_Kvm[i]);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg AssertLogRelRCReturn(rc, rc);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Setup #UD and hypercall behaviour.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg VMMHypercallsEnable(pVM);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg if (ASMIsAmdCpu())
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg {
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->fTrapXcptUD = true;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->uOpCodeNative = OP_VMMCALL;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg else
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg {
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg Assert(ASMIsIntelCpu() || ASMIsViaCentaurCpu());
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->fTrapXcptUD = false;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->uOpCodeNative = OP_VMCALL;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /* We always need to trap VMCALL/VMMCALL hypercall using #UDs for raw-mode VMs. */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg if (!HMIsEnabled(pVM))
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->fTrapXcptUD = true;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg return VINF_SUCCESS;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg}
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/**
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Initializes remaining bits of the KVM provider.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * This is called after initializing HM and almost all other VMM components.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @returns VBox status code.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @param pVM Pointer to the VM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankgVMMR3_INT_DECL(int) gimR3KvmInitCompleted(PVM pVM)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg{
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg if (TMR3CpuTickIsFixedRateMonotonic(pVM, true /* fWithParavirtEnabled */))
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg {
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->uBaseFeat |= GIM_KVM_BASE_FEAT_TSC_STABLE;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg CPUMCPUIDLEAF HyperLeaf;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg RT_ZERO(HyperLeaf);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uLeaf = UINT32_C(0x40000001);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEax = pKvm->uBaseFeat;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEbx = 0;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEcx = 0;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg HyperLeaf.uEdx = 0;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg int rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg AssertLogRelRCReturn(rc, rc);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg return VINF_SUCCESS;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg}
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/**
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Terminates the KVM GIM provider.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @returns VBox status code.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @param pVM Pointer to the VM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankgVMMR3_INT_DECL(int) gimR3KvmTerm(PVM pVM)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg{
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg gimR3KvmReset(pVM);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg return VINF_SUCCESS;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg}
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/**
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Applies relocations to data and code managed by this component.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * This function will be called at init and whenever the VMM need to relocate
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * itself inside the GC.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @param pVM Pointer to the VM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @param offDelta Relocation delta relative to old location.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankgVMMR3_INT_DECL(void) gimR3KvmRelocate(PVM pVM, RTGCINTPTR offDelta)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg{
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg NOREF(pVM); NOREF(offDelta);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg}
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg/**
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * This resets KVM provider MSRs and unmaps whatever KVM regions that
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * the guest may have mapped.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * This is called when the VM is being reset.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg *
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @param pVM Pointer to the VM.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * @thread EMT(0).
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankgVMMR3_INT_DECL(void) gimR3KvmReset(PVM pVM)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg{
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg VM_ASSERT_EMT0(pVM);
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg /*
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg * Reset MSRs.
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg */
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvm->u64WallClockMsr = 0;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg for (VMCPUID iCpu = 0; iCpu < pVM->cCpus; iCpu++)
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg {
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg PGIMKVMCPU pKvmCpu = &pVM->aCpus[iCpu].gim.s.u.KvmCpu;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg pKvmCpu->u64SystemTimeMsr = 0;
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg }
ef06a12023b00bdbe0b983192e4afdbdb21139e3fuankg}
/**
* KVM state-save operation.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pSSM Pointer to the SSM handle.
*/
VMMR3_INT_DECL(int) gimR3KvmSave(PVM pVM, PSSMHANDLE pSSM)
{
PCGIMKVM pcKvm = &pVM->gim.s.u.Kvm;
/*
* Save the KVM SSM version.
*/
SSMR3PutU32(pSSM, GIM_KVM_SAVED_STATE_VERSION);
/*
* Save per-VCPU data.
*/
for (uint32_t i = 0; i < pVM->cCpus; i++)
{
PCGIMKVMCPU pcKvmCpu = &pVM->aCpus[i].gim.s.u.KvmCpu;
/* Guest may alter flags (namely GIM_KVM_SYSTEM_TIME_FLAGS_GUEST_PAUSED bit). So re-read them from guest-memory. */
GIMKVMSYSTEMTIME SystemTime;
RT_ZERO(SystemTime);
if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pcKvmCpu->u64SystemTimeMsr))
{
int rc = PGMPhysSimpleReadGCPhys(pVM, &SystemTime, pcKvmCpu->GCPhysSystemTime, sizeof(GIMKVMSYSTEMTIME));
AssertRCReturn(rc, rc);
}
SSMR3PutU64(pSSM, pcKvmCpu->u64SystemTimeMsr);
SSMR3PutU64(pSSM, pcKvmCpu->uTsc);
SSMR3PutU64(pSSM, pcKvmCpu->uVirtNanoTS);
SSMR3PutGCPhys(pSSM, pcKvmCpu->GCPhysSystemTime);
SSMR3PutU32(pSSM, pcKvmCpu->u32SystemTimeVersion);
SSMR3PutU8(pSSM, SystemTime.fFlags);
}
/*
* Save per-VM data.
*/
SSMR3PutU64(pSSM, pcKvm->u64WallClockMsr);
return SSMR3PutU32(pSSM, pcKvm->uBaseFeat);
}
/**
* KVM state-load operation, final pass.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pSSM Pointer to the SSM handle.
* @param uSSMVersion The GIM saved-state version.
*/
VMMR3_INT_DECL(int) gimR3KvmLoad(PVM pVM, PSSMHANDLE pSSM, uint32_t uSSMVersion)
{
/*
* Load the KVM SSM version first.
*/
uint32_t uKvmSavedStatVersion;
int rc = SSMR3GetU32(pSSM, &uKvmSavedStatVersion);
AssertRCReturn(rc, rc);
if (uKvmSavedStatVersion != GIM_KVM_SAVED_STATE_VERSION)
return SSMR3SetLoadError(pSSM, VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION, RT_SRC_POS,
N_("Unsupported KVM saved-state version %u (expected %u)."), uKvmSavedStatVersion,
GIM_KVM_SAVED_STATE_VERSION);
/*
* Load per-VCPU data.
*/
for (uint32_t i = 0; i < pVM->cCpus; i++)
{
PVMCPU pVCpu = &pVM->aCpus[i];
PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu;
uint8_t fSystemTimeFlags = 0;
SSMR3GetU64(pSSM, &pKvmCpu->u64SystemTimeMsr);
SSMR3GetU64(pSSM, &pKvmCpu->uTsc);
SSMR3GetU64(pSSM, &pKvmCpu->uVirtNanoTS);
SSMR3GetGCPhys(pSSM, &pKvmCpu->GCPhysSystemTime);
SSMR3GetU32(pSSM, &pKvmCpu->u32SystemTimeVersion);
SSMR3GetU8(pSSM, &fSystemTimeFlags);
/* Enable the system-time struct. if necessary. */
if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr))
{
Assert(!TMVirtualIsTicking(pVM)); /* paranoia. */
Assert(!TMCpuTickIsTicking(pVCpu));
rc = gimR3KvmEnableSystemTime(pVM, pVCpu, pKvmCpu, fSystemTimeFlags);
AssertRCReturn(rc, rc);
}
}
/*
* Load per-VM data.
*/
PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
SSMR3GetU64(pSSM, &pKvm->u64WallClockMsr);
rc = SSMR3GetU32(pSSM, &pKvm->uBaseFeat);
AssertRCReturn(rc, rc);
return VINF_SUCCESS;
}
/**
* Enables the KVM VCPU system-time structure.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pKvmCpu Pointer to the GIMKVMCPU with all fields
* populated by the caller.
* @param fFlags The system-time struct. flags.
*
* @remarks Don't do any release assertions here, these can be triggered by
* guest R0 code.
*/
VMMR3_INT_DECL(int) gimR3KvmEnableSystemTime(PVM pVM, PVMCPU pVCpu, PGIMKVMCPU pKvmCpu, uint8_t fFlags)
{
GIMKVMSYSTEMTIME SystemTime;
RT_ZERO(SystemTime);
SystemTime.u32Version = pKvmCpu->u32SystemTimeVersion;
SystemTime.u64NanoTS = pKvmCpu->uVirtNanoTS;
SystemTime.u64Tsc = pKvmCpu->uTsc;
SystemTime.fFlags = fFlags | GIM_KVM_SYSTEM_TIME_FLAGS_TSC_STABLE;
/*
* How the guest calculates the system time (nanoseconds):
*
* tsc = rdtsc - SysTime.u64Tsc
* if (SysTime.i8TscShift >= 0)
* tsc <<= i8TscShift;
* else
* tsc >>= -i8TscShift;
* time = ((tsc * SysTime.u32TscScale) >> 32) + SysTime.u64NanoTS
*/
uint64_t u64TscFreq = TMCpuTicksPerSecond(pVM);
SystemTime.i8TscShift = 0;
while (u64TscFreq > 2 * RT_NS_1SEC_64)
{
u64TscFreq >>= 1;
SystemTime.i8TscShift--;
}
uint32_t uTscFreqLo = (uint32_t)u64TscFreq;
while (uTscFreqLo <= RT_NS_1SEC)
{
uTscFreqLo <<= 1;
SystemTime.i8TscShift++;
}
SystemTime.u32TscScale = ASMDivU64ByU32RetU32(RT_NS_1SEC_64 << 32, uTscFreqLo);
Assert(!(SystemTime.u32Version & UINT32_C(1)));
Assert(PGMPhysIsGCPhysNormal(pVM, pKvmCpu->GCPhysSystemTime));
int rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime, &SystemTime, sizeof(GIMKVMSYSTEMTIME));
if (RT_SUCCESS(rc))
{
LogRel(("GIM: KVM: VCPU%3d: Enabled system-time struct. at %#RGp - u32TscScale=%#RX32 i8TscShift=%d uVersion=%#RU32 "
"fFlags=%#x uTsc=%#RX64 uVirtNanoTS=%#RX64\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, SystemTime.u32TscScale,
SystemTime.i8TscShift, SystemTime.u32Version, SystemTime.fFlags, pKvmCpu->uTsc, pKvmCpu->uVirtNanoTS));
TMR3CpuTickParavirtEnable(pVM);
}
else
LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. at %#RGp. rc=%Rrc\n", pKvmCpu->GCPhysSystemTime, rc));
return rc;
}
/**
* Disables the KVM system-time struct.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
VMMR3_INT_DECL(int) gimR3KvmDisableSystemTime(PVM pVM)
{
TMR3CpuTickParavirtDisable(pVM);
return VINF_SUCCESS;
}
/**
* Enables the KVM wall-clock structure.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param GCPhysWallClock Where the guest wall-clock structure is located.
* @param uVersion The version (sequence number) value to use.
*
* @remarks Don't do any release assertions here, these can be triggered by
* guest R0 code.
*/
VMMR3_INT_DECL(int) gimR3KvmEnableWallClock(PVM pVM, RTGCPHYS GCPhysWallClock, uint32_t uVersion)
{
RTTIMESPEC TimeSpec;
int32_t iSec;
int32_t iNano;
TMR3UtcNow(pVM, &TimeSpec);
RTTimeSpecGetSecondsAndNano(&TimeSpec, &iSec, &iNano);
GIMKVMWALLCLOCK WallClock;
RT_ZERO(WallClock);
WallClock.u32Version = uVersion;
WallClock.u32Sec = iSec;
WallClock.u32Nano = iNano;
Assert(PGMPhysIsGCPhysNormal(pVM, GCPhysWallClock));
Assert(!(WallClock.u32Version & UINT32_C(1)));
int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysWallClock, &WallClock, sizeof(GIMKVMWALLCLOCK));
if (RT_SUCCESS(rc))
{
LogRel(("GIM: KVM: Enabled wall-clock struct. at %#RGp - u32Sec=%u u32Nano=%u uVersion=%#RU32\n", GCPhysWallClock,
WallClock.u32Sec, WallClock.u32Nano, WallClock.u32Version));
}
else
LogRel(("GIM: KVM: Failed to write wall-clock struct. at %#RGp. rc=%Rrc\n", GCPhysWallClock, rc));
return rc;
}