1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <arch/x86/apic.h>
8#include <arch/x86/feature.h>
9#include <zircon/syscalls/hypervisor.h>
10
11#include "vmx_cpu_state_priv.h"
12
13static void ignore_msr(VmxPage* msr_bitmaps_page, bool ignore_writes, uint32_t msr) {
14    // From Volume 3, Section 24.6.9.
15    uint8_t* msr_bitmaps = msr_bitmaps_page->VirtualAddress<uint8_t>();
16    if (msr >= 0xc0000000) {
17        msr_bitmaps += 1 << 10;
18    }
19
20    uint16_t msr_low = msr & 0x1fff;
21    uint16_t msr_byte = msr_low / 8;
22    uint8_t msr_bit = msr_low % 8;
23
24    // Ignore reads to the MSR.
25    msr_bitmaps[msr_byte] &= (uint8_t) ~(1 << msr_bit);
26
27    if (ignore_writes) {
28        // Ignore writes to the MSR.
29        msr_bitmaps += 2 << 10;
30        msr_bitmaps[msr_byte] &= (uint8_t) ~(1 << msr_bit);
31    }
32}
33
34// static
35zx_status_t Guest::Create(fbl::unique_ptr<Guest>* out) {
36    // Check that the CPU supports VMX.
37    if (!x86_feature_test(X86_FEATURE_VMX)) {
38        return ZX_ERR_NOT_SUPPORTED;
39    }
40
41    zx_status_t status = alloc_vmx_state();
42    if (status != ZX_OK) {
43        return status;
44    }
45
46    fbl::AllocChecker ac;
47    fbl::unique_ptr<Guest> guest(new (&ac) Guest);
48    if (!ac.check()) {
49        return ZX_ERR_NO_MEMORY;
50    }
51
52    status = hypervisor::GuestPhysicalAddressSpace::Create(&guest->gpas_);
53    if (status != ZX_OK) {
54        return status;
55    }
56
57    // Setup common MSR bitmaps.
58    VmxInfo vmx_info;
59    status = guest->msr_bitmaps_page_.Alloc(vmx_info, UINT8_MAX);
60    if (status != ZX_OK) {
61        return status;
62    }
63
64    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_PAT);
65    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_EFER);
66    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_FS_BASE);
67    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_GS_BASE);
68    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_KERNEL_GS_BASE);
69    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_STAR);
70    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_LSTAR);
71    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_FMASK);
72    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_TSC_ADJUST);
73    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_TSC_AUX);
74    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_SYSENTER_CS);
75    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_SYSENTER_ESP);
76    ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_SYSENTER_EIP);
77
78    // Setup VPID allocator
79    fbl::AutoLock lock(&guest->vcpu_mutex_);
80    status = guest->vpid_allocator_.Init();
81    if (status != ZX_OK) {
82        return status;
83    }
84
85    *out = fbl::move(guest);
86    return ZX_OK;
87}
88
89Guest::~Guest() {
90    free_vmx_state();
91}
92
93zx_status_t Guest::SetTrap(uint32_t kind, zx_vaddr_t addr, size_t len,
94                           fbl::RefPtr<PortDispatcher> port, uint64_t key) {
95    if (len == 0) {
96        return ZX_ERR_INVALID_ARGS;
97    } else if (SIZE_MAX - len < addr) {
98        return ZX_ERR_OUT_OF_RANGE;
99    }
100
101    switch (kind) {
102    case ZX_GUEST_TRAP_MEM:
103        if (port) {
104            return ZX_ERR_INVALID_ARGS;
105        }
106        break;
107    case ZX_GUEST_TRAP_BELL:
108        if (!port) {
109            return ZX_ERR_INVALID_ARGS;
110        }
111        break;
112    case ZX_GUEST_TRAP_IO:
113        if (port) {
114            return ZX_ERR_INVALID_ARGS;
115        } else if (addr + len > UINT16_MAX) {
116            return ZX_ERR_OUT_OF_RANGE;
117        }
118        return traps_.InsertTrap(kind, addr, len, fbl::move(port), key);
119    default:
120        return ZX_ERR_INVALID_ARGS;
121    }
122
123    // Common logic for memory-based traps.
124    if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(len)) {
125        return ZX_ERR_INVALID_ARGS;
126    }
127    zx_status_t status = gpas_->UnmapRange(addr, len);
128    if (status != ZX_OK) {
129        return status;
130    }
131    return traps_.InsertTrap(kind, addr, len, fbl::move(port), key);
132}
133
134zx_status_t Guest::AllocVpid(uint16_t* vpid) {
135    fbl::AutoLock lock(&vcpu_mutex_);
136    return vpid_allocator_.AllocId(vpid);
137}
138
139zx_status_t Guest::FreeVpid(uint16_t vpid) {
140    fbl::AutoLock lock(&vcpu_mutex_);
141    return vpid_allocator_.FreeId(vpid);
142}
143