| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/virtualization/tests/hypervisor/arch.h" |
| |
| #include <lib/page-table/builder.h> |
| #include <lib/stdcompat/bit.h> |
| #include <lib/stdcompat/span.h> |
| |
| #include <fbl/algorithm.h> |
| |
| #include "lib/page-table/arch/arm64/builder.h" |
| #include "lib/page-table/arch/arm64/mmu.h" |
| #include "lib/page-table/types.h" |
| #include "src/virtualization/tests/hypervisor/constants.h" |
| #include "src/virtualization/tests/hypervisor/hypervisor_tests.h" |
| |
| // The page_table library physical addresses and virtual addresses are from |
| // the perspective of the guest. |
| using GuestPaddr = page_table::Paddr; |
| using GuestVaddr = page_table::Vaddr; |
| |
| // Maps guest virtual/physical memory to host, and allocates guest physical memory |
| // for page tables. |
| class GuestMemoryManager : public page_table::MemoryManager { |
| public: |
| GuestMemoryManager(cpp20::span<uint8_t> guest_memory, GuestPaddr allocation_addr, |
| size_t free_region_size) |
| : guest_memory_(guest_memory), |
| next_allocation_(allocation_addr), |
| free_region_end_(allocation_addr + free_region_size) {} |
| |
| // Get the physical address of the given pointer. |
| GuestPaddr PtrToPhys(std::byte* ptr) override { |
| auto ptr_addr = reinterpret_cast<uintptr_t>(ptr); |
| auto guest_addr = reinterpret_cast<uintptr_t>(guest_memory_.data()); |
| ZX_ASSERT(ptr_addr >= guest_addr); |
| ZX_ASSERT(ptr_addr - guest_addr < guest_memory_.size()); |
| return GuestPaddr{ptr_addr - guest_addr}; |
| } |
| |
| // Get a pointer to the given physical address. |
| std::byte* PhysToPtr(GuestPaddr phys) override { |
| ZX_ASSERT(phys.value() < guest_memory_.size()); |
| return reinterpret_cast<std::byte*>(guest_memory_.data() + phys.value()); |
| } |
| |
| // Allocate memory with the given size / alignment. |
| std::byte* Allocate(size_t size, size_t alignment) override { |
| // Align to requested alignment. |
| GuestPaddr allocation_start = GuestPaddr(fbl::round_up(next_allocation_.value(), alignment)); |
| GuestPaddr allocation_end = allocation_start + size; |
| |
| // Ensure we didn't overflow during either the alignment step or the addition. |
| if (allocation_start < next_allocation_ || allocation_end < allocation_start) { |
| return nullptr; |
| } |
| |
| // Ensure we haven't run out of space. |
| if (allocation_end > free_region_end_) { |
| return nullptr; |
| } |
| |
| // Record that the memory has been used. |
| next_allocation_ = allocation_end; |
| |
| return PhysToPtr(allocation_start); |
| } |
| |
| private: |
| cpp20::span<uint8_t> guest_memory_; |
| GuestPaddr next_allocation_; |
| GuestPaddr free_region_end_; |
| }; |
| |
| void SetUpGuestPageTable(cpp20::span<uint8_t> guest_memory) { |
| page_table::arm64::PageTableLayout layout = { |
| .granule_size = page_table::arm64::GranuleSize::k4KiB, |
| .region_size_bits = REGION_SIZE_BITS, |
| }; |
| |
| // Set up a page table builder. |
| GuestMemoryManager manager(guest_memory, /*allocation_addr=*/GuestPaddr(PAGE_TABLE_PADDR), |
| /*free_region_size=*/PAGE_TABLE_SIZE); |
| auto builder = page_table::arm64::AddressSpaceBuilder::Create(manager, layout); |
| ZX_ASSERT(builder.has_value()); |
| ZX_ASSERT(builder->root_paddr().value() == PAGE_TABLE_PADDR); |
| |
| // Map virtual memory 1:1 to physical memory. |
| zx_status_t status = |
| builder->MapRegion(GuestVaddr{0}, GuestPaddr{0}, cpp20::bit_ceil(guest_memory.size()), |
| page_table::CacheAttributes::kNormal); |
| ZX_ASSERT(status == ZX_OK); |
| } |