blob: 4ad312054217cd967941e6a47613d17ed2cd4e6c [file] [log] [blame]
// 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 <arch/x86/apic.h>
#include <arch/x86/feature.h>
#include <hypervisor/guest_physical_address_space.h>
#include <zircon/syscalls/hypervisor.h>
#include "vmx_cpu_state_priv.h"
static const zx_vaddr_t kIoApicPhysBase = 0xfec00000;
static void ignore_msr(VmxPage* msr_bitmaps_page, uint32_t msr) {
// From Volume 3, Section 24.6.9.
uint8_t* msr_bitmaps = msr_bitmaps_page->VirtualAddress<uint8_t>();
if (msr >= 0xc0000000)
msr_bitmaps += 1 << 10;
uint16_t msr_low = msr & 0x1fff;
uint16_t msr_byte = msr_low / 8;
uint8_t msr_bit = msr_low % 8;
// Ignore reads to the MSR.
msr_bitmaps[msr_byte] &= (uint8_t) ~(1 << msr_bit);
// Ignore writes to the MSR.
msr_bitmaps += 2 << 10;
msr_bitmaps[msr_byte] &= (uint8_t) ~(1 << msr_bit);
}
// static
zx_status_t Guest::Create(fbl::RefPtr<VmObject> physmem, fbl::unique_ptr<Guest>* out) {
fbl::AllocChecker ac;
fbl::unique_ptr<Guest> guest(new (&ac) Guest);
if (!ac.check())
return ZX_ERR_NO_MEMORY;
zx_status_t status = GuestPhysicalAddressSpace::Create(fbl::move(physmem), &guest->gpas_);
if (status != ZX_OK)
return status;
// We ensure the page containing the IO APIC address is not mapped so that
// we VM exit with an EPT violation when the guest accesses the page.
status = guest->gpas_->UnmapRange(kIoApicPhysBase, PAGE_SIZE);
if (status != ZX_OK)
return status;
// Setup common APIC access.
VmxInfo vmx_info;
status = guest->apic_access_page_.Alloc(vmx_info, 0);
if (status != ZX_OK)
return status;
status = guest->gpas_->MapApicPage(APIC_PHYS_BASE,
guest->apic_access_page_.PhysicalAddress());
if (status != ZX_OK)
return status;
// Setup common MSR bitmaps.
status = guest->msr_bitmaps_page_.Alloc(vmx_info, UINT8_MAX);
if (status != ZX_OK)
return status;
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_PAT);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_EFER);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_FS_BASE);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_GS_BASE);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_KERNEL_GS_BASE);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_STAR);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_LSTAR);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_FMASK);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_TSC_ADJUST);
ignore_msr(&guest->msr_bitmaps_page_, X86_MSR_IA32_TSC_AUX);
*out = fbl::move(guest);
return ZX_OK;
}
Guest::~Guest() {
__UNUSED zx_status_t status = gpas_->UnmapRange(APIC_PHYS_BASE, PAGE_SIZE);
DEBUG_ASSERT(status == ZX_OK);
}
zx_status_t Guest::SetTrap(uint32_t kind, zx_vaddr_t addr, size_t len,
fbl::RefPtr<PortDispatcher> port, uint64_t key) {
if (len == 0)
return ZX_ERR_INVALID_ARGS;
if (SIZE_MAX - len < addr)
return ZX_ERR_OUT_OF_RANGE;
switch (kind) {
case ZX_GUEST_TRAP_MEM:
if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(len))
return ZX_ERR_INVALID_ARGS;
return gpas_->UnmapRange(addr, len);
case ZX_GUEST_TRAP_IO:
if (addr + len > UINT16_MAX)
return ZX_ERR_OUT_OF_RANGE;
return mux_.AddPortRange(addr, len, fbl::move(port), key);
default:
return ZX_ERR_INVALID_ARGS;
}
}
zx_status_t arch_guest_create(fbl::RefPtr<VmObject> physmem, fbl::unique_ptr<Guest>* guest) {
// Check that the CPU supports VZX.
if (!x86_feature_test(X86_FEATURE_VMX))
return ZX_ERR_NOT_SUPPORTED;
return Guest::Create(fbl::move(physmem), guest);
}
zx_status_t arch_guest_set_trap(Guest* guest, uint32_t kind, zx_vaddr_t addr, size_t len,
fbl::RefPtr<PortDispatcher> port, uint64_t key) {
return guest->SetTrap(kind, addr, len, port, key);
}