| // 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); |
| } |