| // 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/guest_physical_address_space.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_status_t Guest::Create(ktl::unique_ptr<Guest>* out) { |
| if (arm64_get_boot_el() < 2) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| uint8_t vmid; |
| zx_status_t status = alloc_vmid(&vmid); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| fbl::AllocChecker ac; |
| ktl::unique_ptr<Guest> guest(new (&ac) Guest(vmid)); |
| if (!ac.check()) { |
| free_vmid(vmid); |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| Guard<Mutex> lock{&guest->vcpu_mutex_}; |
| status = guest->vpid_allocator_.Init(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = hypervisor::GuestPhysicalAddressSpace::Create(vmid, &guest->gpas_); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| zx_paddr_t gicv_paddr; |
| status = gic_get_gicv(&gicv_paddr); |
| |
| // If status == ZX_ERR_NOT_FOUND, we are running GICv3 |
| // There is no need to map GICV to the guest |
| // Handle other cases below |
| if (status == ZX_OK) { |
| status = guest->gpas_->MapInterruptController(kGicvAddress, gicv_paddr, kGicvSize); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } else if (status == ZX_ERR_NOT_SUPPORTED) { |
| return status; |
| } |
| |
| *out = ktl::move(guest); |
| return ZX_OK; |
| } |
| |
| Guest::Guest(uint8_t vmid) : vmid_(vmid) {} |
| |
| Guest::~Guest() { free_vmid(vmid_); } |
| |
| zx_status_t 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_ERR_INVALID_ARGS; |
| } |
| break; |
| case ZX_GUEST_TRAP_BELL: |
| if (!port) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| break; |
| case ZX_GUEST_TRAP_IO: |
| return ZX_ERR_NOT_SUPPORTED; |
| default: |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (SIZE_MAX - len < addr) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } else if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(len) || len == 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zx_status_t status = gpas_->UnmapRange(addr, len); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return traps_.InsertTrap(kind, addr, len, ktl::move(port), key); |
| } |
| |
| zx_status_t Guest::AllocVpid(uint8_t* vpid) { |
| Guard<Mutex> lock{&vcpu_mutex_}; |
| return vpid_allocator_.AllocId(vpid); |
| } |
| |
| zx_status_t Guest::FreeVpid(uint8_t vpid) { |
| Guard<Mutex> lock{&vcpu_mutex_}; |
| return vpid_allocator_.FreeId(vpid); |
| } |