blob: 377619eb4194d4b2c088d18fa9384b36ca93ff76 [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 <align.h>
#include <zircon/syscalls/hypervisor.h>
#include <arch/hypervisor.h>
#include <dev/interrupt/arm_gic_hw_interface.h>
#include <hypervisor/aspace.h>
#include <vm/pmm.h>
#include "el2_cpu_state_priv.h"
static constexpr zx_gpaddr_t kGicvAddress = 0x800001000;
static constexpr size_t kGicvSize = 0x2000;
// static
zx::result<ktl::unique_ptr<Guest>> Guest::Create() {
if (arm64_get_boot_el() < 2) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
auto vmid = alloc_vmid();
if (vmid.is_error()) {
return vmid.take_error();
}
fbl::AllocChecker ac;
ktl::unique_ptr<Guest> guest(new (&ac) Guest(*vmid));
if (!ac.check()) {
auto result = free_vmid(*vmid);
ZX_ASSERT(result.is_ok());
return zx::error(ZX_ERR_NO_MEMORY);
}
auto gpa = hypervisor::GuestPhysicalAspace::Create();
if (gpa.is_error()) {
return gpa.take_error();
}
guest->gpa_ = ktl::move(*gpa);
guest->gpa_.arch_aspace().arch_set_asid(*vmid);
zx_paddr_t gicv_paddr;
zx_status_t status = gic_get_gicv(&gicv_paddr);
// If `status` is ZX_OK, we are running GICv2. We then need to map GICV.
// If `status is ZX_ERR_NOT_FOUND, we are running GICv3.
// Otherwise, return `status`.
if (status == ZX_OK) {
if (auto result = guest->gpa_.MapInterruptController(kGicvAddress, gicv_paddr, kGicvSize);
result.is_error()) {
return result.take_error();
}
} else if (status != ZX_ERR_NOT_FOUND) {
return zx::error(status);
}
return zx::ok(ktl::move(guest));
}
Guest::Guest(uint16_t vmid) : vmid_(vmid) {}
Guest::~Guest() {
auto result = free_vmid(vmid_);
ZX_ASSERT(result.is_ok());
}
zx::result<> Guest::SetTrap(uint32_t kind, zx_gpaddr_t addr, size_t len,
fbl::RefPtr<PortDispatcher> port, uint64_t key) {
switch (kind) {
case ZX_GUEST_TRAP_MEM:
if (port) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
break;
case ZX_GUEST_TRAP_BELL:
if (!port) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
break;
case ZX_GUEST_TRAP_IO:
return zx::error(ZX_ERR_NOT_SUPPORTED);
default:
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (!IS_PAGE_ROUNDED(addr) || !IS_PAGE_ROUNDED(len)) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (auto result = gpa_.UnmapRange(addr, len); result.is_error()) {
return result;
}
return traps_.InsertTrap(kind, addr, len, ktl::move(port), key);
}