[hypervisor] Extract common CPU state logic from VmxCpuState.

This is so we can reuse some of this logic in El2CpuState, and
customise it for allocating VMIDs.

Change-Id: I05b76d9694b8ed4446763604239cb78f77786e7f
Esse commit está contido em:
Abdulla Kamar
2017-09-11 12:29:15 +10:00
commit de CQ bot account: commit-bot@chromium.org
commit 776d4eed1b
6 arquivos alterados com 134 adições e 84 exclusões
+2 -2
Ver Arquivo
@@ -569,7 +569,7 @@ mx_status_t Vcpu::Create(mx_vaddr_t ip, mx_vaddr_t cr3, fbl::RefPtr<VmObject> ap
mx_status_t status = alloc_vpid(&vpid);
if (status != MX_OK)
return status;
auto auto_call = fbl::MakeAutoCall([=]() { release_vpid(vpid); });
auto auto_call = fbl::MakeAutoCall([=]() { free_vpid(vpid); });
// When we create a VCPU, we bind it to the current thread and a CPU based
// on the VPID. The VCPU must always be run on the current thread and the
@@ -641,7 +641,7 @@ Vcpu::~Vcpu() {
// pin the current thread to the same CPU as the VCPU.
AutoPin pin(this);
vmclear(vmcs_page_.PhysicalAddress());
__UNUSED mx_status_t status = release_vpid(vpid_);
__UNUSED mx_status_t status = free_vpid(vpid_);
DEBUG_ASSERT(status == MX_OK);
}
+39 -72
Ver Arquivo
@@ -4,6 +4,8 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "vmx_cpu_state_priv.h"
#include <assert.h>
#include <bits.h>
#include <string.h>
@@ -14,10 +16,6 @@
#include <fbl/mutex.h>
#include "vmx_cpu_state_priv.h"
using fbl::AutoLock;
static fbl::Mutex vmx_mutex;
static size_t vcpus TA_GUARDED(vmx_mutex) = 0;
static fbl::unique_ptr<VmxCpuState> vmx_cpu_state TA_GUARDED(vmx_mutex);
@@ -81,6 +79,12 @@ EptInfo::EptInfo() {
BIT_SHIFT(ept_info, 26);
}
VmxPage::~VmxPage() {
vm_page_t* page = paddr_to_vm_page(pa_);
if (page != nullptr)
pmm_free_page(page);
}
mx_status_t VmxPage::Alloc(const VmxInfo& vmx_info, uint8_t fill) {
// From Volume 3, Appendix A.1: Bits 44:32 report the number of bytes that
// software should allocate for the VMXON region and any VMCS region. It is
@@ -112,55 +116,40 @@ void* VmxPage::VirtualAddress() const {
return paddr_to_kvaddr(pa_);
}
VmxPage::~VmxPage() {
vm_page_t* page = paddr_to_vm_page(pa_);
if (page != nullptr)
pmm_free_page(page);
}
struct vmxon_context {
fbl::Array<VmxPage>* vmxon_pages;
fbl::atomic<mp_cpu_mask_t> cpu_mask;
vmxon_context(fbl::Array<VmxPage>* vp)
: vmxon_pages(vp), cpu_mask(0) {}
};
static void vmxon_task(void* arg) {
auto ctx = static_cast<vmxon_context*>(arg);
uint cpu_num = arch_curr_cpu_num();
VmxPage& page = (*ctx->vmxon_pages)[cpu_num];
static mx_status_t vmxon_task(void* context, uint cpu_num) {
auto pages = static_cast<fbl::Array<VmxPage>*>(context);
VmxPage& page = (*pages)[cpu_num];
// Check that we have instruction information when we VM exit on IO.
VmxInfo vmx_info;
if (!vmx_info.io_exit_info)
return;
return MX_ERR_NOT_SUPPORTED;
// Check that full VMX controls are supported.
if (!vmx_info.vmx_controls)
return;
return MX_ERR_NOT_SUPPORTED;
// Check that a page-walk length of 4 is supported.
EptInfo ept_info;
if (!ept_info.page_walk_4)
return;
return MX_ERR_NOT_SUPPORTED;
// Check use write-back memory for EPT is supported.
if (!ept_info.write_back)
return;
return MX_ERR_NOT_SUPPORTED;
// Check that accessed and dirty flags for EPT are supported.
if (!ept_info.ept_flags)
return;
return MX_ERR_NOT_SUPPORTED;
// Check that the INVEPT instruction is supported.
if (!ept_info.invept)
return;
return MX_ERR_NOT_SUPPORTED;
// Check that wait for startup IPI is a supported activity state.
MiscInfo misc_info;
if (!misc_info.wait_for_sipi)
return;
return MX_ERR_NOT_SUPPORTED;
// Enable VMXON, if required.
uint64_t feature_control = read_msr(X86_MSR_IA32_FEATURE_CONTROL);
@@ -168,7 +157,7 @@ static void vmxon_task(void* arg) {
!(feature_control & X86_MSR_IA32_FEATURE_CONTROL_VMXON)) {
if ((feature_control & X86_MSR_IA32_FEATURE_CONTROL_LOCK) &&
!(feature_control & X86_MSR_IA32_FEATURE_CONTROL_VMXON)) {
return;
return MX_ERR_NOT_SUPPORTED;
}
feature_control |= X86_MSR_IA32_FEATURE_CONTROL_LOCK;
feature_control |= X86_MSR_IA32_FEATURE_CONTROL_VMXON;
@@ -178,10 +167,10 @@ static void vmxon_task(void* arg) {
// Check control registers are in a VMX-friendly state.
uint64_t cr0 = x86_get_cr0();
if (cr_is_invalid(cr0, X86_MSR_IA32_VMX_CR0_FIXED0, X86_MSR_IA32_VMX_CR0_FIXED1))
return;
return MX_ERR_BAD_STATE;
uint64_t cr4 = x86_get_cr4() | X86_CR4_VMXE;
if (cr_is_invalid(cr4, X86_MSR_IA32_VMX_CR4_FIXED0, X86_MSR_IA32_VMX_CR4_FIXED1))
return;
return MX_ERR_BAD_STATE;
// Enable VMX using the VMXE bit.
x86_set_cr4(cr4);
@@ -194,10 +183,10 @@ static void vmxon_task(void* arg) {
mx_status_t status = vmxon(page.PhysicalAddress());
if (status != MX_OK) {
dprintf(CRITICAL, "Failed to turn on VMX on CPU %u\n", cpu_num);
return;
return status;
}
ctx->cpu_mask.fetch_or(1 << cpu_num);
return MX_OK;
}
static void vmxoff_task(void* arg) {
@@ -214,9 +203,16 @@ static void vmxoff_task(void* arg) {
// static
mx_status_t VmxCpuState::Create(fbl::unique_ptr<VmxCpuState>* out) {
fbl::AllocChecker ac;
fbl::unique_ptr<VmxCpuState> vmx_cpu_state(new (&ac) VmxCpuState);
if (!ac.check())
return MX_ERR_NO_MEMORY;
mx_status_t status = vmx_cpu_state->Init();
if (status != MX_OK)
return status;
// Allocate a VMXON page for each CPU.
size_t num_cpus = arch_max_num_cpus();
fbl::AllocChecker ac;
VmxPage* pages = new (&ac) VmxPage[num_cpus];
if (!ac.check())
return MX_ERR_NO_MEMORY;
@@ -229,64 +225,35 @@ mx_status_t VmxCpuState::Create(fbl::unique_ptr<VmxCpuState>* out) {
}
// Enable VMX for all online CPUs.
vmxon_context vmxon_ctx(&vmxon_pages);
mp_cpu_mask_t online_mask = mp_get_online_mask();
mp_sync_exec(MP_IPI_TARGET_MASK, online_mask, vmxon_task, &vmxon_ctx);
mp_cpu_mask_t cpu_mask = vmxon_ctx.cpu_mask.load();
if (cpu_mask != online_mask) {
mp_cpu_mask_t cpu_mask = percpu_exec(vmxon_task, &vmxon_pages);
if (cpu_mask != mp_get_online_mask()) {
mp_sync_exec(MP_IPI_TARGET_MASK, cpu_mask, vmxoff_task, nullptr);
return MX_ERR_NOT_SUPPORTED;
}
fbl::unique_ptr<VmxCpuState> vmx_cpu_state(new (&ac) VmxCpuState(fbl::move(vmxon_pages)));
if (!ac.check())
return MX_ERR_NO_MEMORY;
mx_status_t status = vmx_cpu_state->vpid_bitmap_.Reset(kNumVpids);
if (status != MX_OK)
return status;
vmx_cpu_state->vmxon_pages_ = fbl::move(vmxon_pages);
*out = fbl::move(vmx_cpu_state);
return MX_OK;
}
VmxCpuState::VmxCpuState(fbl::Array<VmxPage> vmxon_pages)
: vmxon_pages_(fbl::move(vmxon_pages)) {}
VmxCpuState::~VmxCpuState() {
mp_sync_exec(MP_IPI_TARGET_ALL, 0, vmxoff_task, nullptr);
}
mx_status_t VmxCpuState::AllocVpid(uint16_t* vpid) {
size_t first_unset;
bool all_set = vpid_bitmap_.Get(0, kNumVpids, &first_unset);
if (all_set)
return MX_ERR_NO_RESOURCES;
if (first_unset > UINT16_MAX)
return MX_ERR_OUT_OF_RANGE;
*vpid = (first_unset + 1) & UINT16_MAX;
return vpid_bitmap_.SetOne(first_unset);
}
mx_status_t VmxCpuState::ReleaseVpid(uint16_t vpid) {
if (vpid == 0 || !vpid_bitmap_.GetOne(vpid - 1))
return MX_ERR_INVALID_ARGS;
return vpid_bitmap_.ClearOne(vpid - 1);
}
mx_status_t alloc_vpid(uint16_t* vpid) {
AutoLock lock(&vmx_mutex);
fbl::AutoLock lock(&vmx_mutex);
if (vcpus == 0) {
mx_status_t status = VmxCpuState::Create(&vmx_cpu_state);
if (status != MX_OK)
return status;
}
vcpus++;
return vmx_cpu_state->AllocVpid(vpid);
return vmx_cpu_state->AllocId(vpid);
}
mx_status_t release_vpid(uint16_t vpid) {
AutoLock lock(&vmx_mutex);
mx_status_t status = vmx_cpu_state->ReleaseVpid(vpid);
mx_status_t free_vpid(uint16_t vpid) {
fbl::AutoLock lock(&vmx_mutex);
mx_status_t status = vmx_cpu_state->FreeId(vpid);
if (status != MX_OK)
return status;
vcpus--;
+4 -9
Ver Arquivo
@@ -7,6 +7,7 @@
#pragma once
#include <arch/hypervisor.h>
#include <hypervisor/cpu_state.h>
// clang-format off
@@ -30,8 +31,6 @@
// clang-format on
static const uint16_t kNumVpids = 64;
/* Stores VMX info from the IA32_VMX_BASIC MSR. */
struct VmxInfo {
uint32_t revision_id;
@@ -70,22 +69,18 @@ struct VmxRegion {
};
/* Maintains the VMX state for each CPU. */
class VmxCpuState {
class VmxCpuState : public hypervisor::CpuState<uint16_t, 64> {
public:
static mx_status_t Create(fbl::unique_ptr<VmxCpuState>* out);
~VmxCpuState();
DISALLOW_COPY_ASSIGN_AND_MOVE(VmxCpuState);
mx_status_t AllocVpid(uint16_t* vpid);
mx_status_t ReleaseVpid(uint16_t vpid);
private:
bitmap::RawBitmapGeneric<bitmap::FixedStorage<kNumVpids>> vpid_bitmap_;
fbl::Array<VmxPage> vmxon_pages_;
explicit VmxCpuState(fbl::Array<VmxPage> vmxon_pages);
VmxCpuState() = default;
};
mx_status_t alloc_vpid(uint16_t* vpid);
mx_status_t release_vpid(uint16_t vpid);
mx_status_t free_vpid(uint16_t vpid);
bool cr_is_invalid(uint64_t cr_value, uint32_t fixed0_msr, uint32_t fixed1_msr);
+31
Ver Arquivo
@@ -0,0 +1,31 @@
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <fbl/atomic.h>
#include <hypervisor/cpu_state.h>
struct percpu_state {
fbl::atomic<mp_cpu_mask_t> cpu_mask;
percpu_task_t task;
void* context;
percpu_state(percpu_task_t _task, void* _context)
: cpu_mask(0), task(_task), context(_context) {}
};
static void percpu_task(void* arg) {
auto state = static_cast<percpu_state*>(arg);
uint cpu_num = arch_curr_cpu_num();
mx_status_t status = state->task(state->context, cpu_num);
if (status == MX_OK)
state->cpu_mask.fetch_or(1 << cpu_num);
}
mp_cpu_mask_t percpu_exec(percpu_task_t task, void* context) {
percpu_state state(task, context);
mp_sync_exec(MP_IPI_TARGET_ALL, 0, percpu_task, &state);
return state.cpu_mask.load();
}
@@ -0,0 +1,55 @@
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
#include <bitmap/raw-bitmap.h>
#include <bitmap/storage.h>
#include <kernel/mp.h>
namespace hypervisor {
/* Provides a base class for arch-specific CPU state logic.
*
* |T| is the type of the ID, and is an integral type.
* |N| is the maximum value of an ID.
*/
template <typename T, T N>
class CpuState {
public:
mx_status_t AllocId(T* id) {
size_t first_unset;
bool all_set = id_bitmap_.Get(0, N, &first_unset);
if (all_set)
return MX_ERR_NO_RESOURCES;
if (first_unset >= N)
return MX_ERR_OUT_OF_RANGE;
*id = static_cast<T>(first_unset + 1);
return id_bitmap_.SetOne(first_unset);
}
mx_status_t FreeId(T id) {
if (id == 0 || !id_bitmap_.GetOne(id - 1))
return MX_ERR_INVALID_ARGS;
return id_bitmap_.ClearOne(id - 1);
}
protected:
mx_status_t Init() {
return id_bitmap_.Reset(N);
}
private:
bitmap::RawBitmapGeneric<bitmap::FixedStorage<N>> id_bitmap_;
};
} // namespace hypervisor
typedef mx_status_t (* percpu_task_t)(void* context, uint cpu_num);
/* Executes a task on each online CPU, and returns a CPU mask containing each
* CPU the task was successfully run on. */
mp_cpu_mask_t percpu_exec(percpu_task_t task, void* context);
+3 -1
Ver Arquivo
@@ -9,13 +9,15 @@ LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS := \
$(LOCAL_DIR)/guest_physical_address_space.cpp \
$(LOCAL_DIR)/cpu_state.cpp \
$(LOCAL_DIR)/fault.cpp \
$(LOCAL_DIR)/guest_physical_address_space.cpp \
$(LOCAL_DIR)/hypervisor_unittest.cpp \
$(LOCAL_DIR)/packet_mux.cpp \
MODULE_DEPS := \
kernel/arch/$(ARCH)/hypervisor \
kernel/lib/bitmap \
kernel/lib/fbl \
kernel/lib/unittest \
kernel/object \