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/hypervisor.h>
8#include <dev/interrupt/arm_gic_hw_interface.h>
9#include <hypervisor/guest_physical_address_space.h>
10#include <vm/pmm.h>
11#include <zircon/syscalls/hypervisor.h>
12
13#include "el2_cpu_state_priv.h"
14
15static constexpr zx_gpaddr_t kGicvAddress = 0x800002000;
16static constexpr size_t kGicvSize = 0x2000;
17
18// static
19zx_status_t Guest::Create(fbl::unique_ptr<Guest>* out) {
20    if (arm64_get_boot_el() < 2) {
21        return ZX_ERR_NOT_SUPPORTED;
22    }
23
24    uint8_t vmid;
25    zx_status_t status = alloc_vmid(&vmid);
26    if (status != ZX_OK) {
27        return status;
28    }
29
30    fbl::AllocChecker ac;
31    fbl::unique_ptr<Guest> guest(new (&ac) Guest(vmid));
32    if (!ac.check()) {
33        free_vmid(vmid);
34        return ZX_ERR_NO_MEMORY;
35    }
36
37    fbl::AutoLock lock(&guest->vcpu_mutex_);
38    status = guest->vpid_allocator_.Init();
39    if (status != ZX_OK) {
40        return status;
41    }
42
43    status = hypervisor::GuestPhysicalAddressSpace::Create(vmid, &guest->gpas_);
44    if (status != ZX_OK) {
45        return status;
46    }
47
48    zx_paddr_t gicv_paddr;
49    status = gic_get_gicv(&gicv_paddr);
50
51    // If status == ZX_ERR_NOT_FOUND, we are running GICv3
52    // There is no need to map GICV to the guest
53    // Handle other cases below
54    if (status == ZX_OK) {
55        status = guest->gpas_->MapInterruptController(kGicvAddress, gicv_paddr, kGicvSize);
56        if (status != ZX_OK) {
57            return status;
58        }
59    } else if (status == ZX_ERR_NOT_SUPPORTED) {
60        return status;
61    }
62
63    *out = fbl::move(guest);
64    return ZX_OK;
65}
66
67Guest::Guest(uint8_t vmid)
68    : vmid_(vmid) {}
69
70Guest::~Guest() {
71    free_vmid(vmid_);
72}
73
74zx_status_t Guest::SetTrap(uint32_t kind, zx_gpaddr_t addr, size_t len,
75                           fbl::RefPtr<PortDispatcher> port, uint64_t key) {
76    switch (kind) {
77    case ZX_GUEST_TRAP_MEM:
78        if (port) {
79            return ZX_ERR_INVALID_ARGS;
80        }
81        break;
82    case ZX_GUEST_TRAP_BELL:
83        if (!port) {
84            return ZX_ERR_INVALID_ARGS;
85        }
86        break;
87    case ZX_GUEST_TRAP_IO:
88        return ZX_ERR_NOT_SUPPORTED;
89    default:
90        return ZX_ERR_INVALID_ARGS;
91    }
92
93    if (SIZE_MAX - len < addr) {
94        return ZX_ERR_OUT_OF_RANGE;
95    } else if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(len) || len == 0) {
96        return ZX_ERR_INVALID_ARGS;
97    }
98    zx_status_t status = gpas_->UnmapRange(addr, len);
99    if (status != ZX_OK) {
100        return status;
101    }
102    return traps_.InsertTrap(kind, addr, len, fbl::move(port), key);
103}
104
105zx_status_t Guest::AllocVpid(uint8_t* vpid) {
106    fbl::AutoLock lock(&vcpu_mutex_);
107    return vpid_allocator_.AllocId(vpid);
108}
109
110zx_status_t Guest::FreeVpid(uint8_t vpid) {
111    fbl::AutoLock lock(&vcpu_mutex_);
112    return vpid_allocator_.FreeId(vpid);
113}
114