| // Copyright 2018 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 <arch/arm64/mmu.h> |
| #include <arch/arm64/periphmap.h> |
| #include <vm/vm.h> |
| #include <vm/vm_aspace.h> |
| |
| #define PERIPH_RANGE_MAX 4 |
| |
| typedef struct { |
| uint64_t base_phys; |
| uint64_t base_virt; |
| uint64_t length; |
| } periph_range_t; |
| |
| static periph_range_t periph_ranges[PERIPH_RANGE_MAX] = {}; |
| |
| zx_status_t add_periph_range(paddr_t base_phys, size_t length) { |
| // peripheral ranges are allocated below the kernel image. |
| uint64_t base_virt = (uint64_t)__code_start; |
| |
| DEBUG_ASSERT(IS_PAGE_ALIGNED(base_phys)); |
| DEBUG_ASSERT(IS_PAGE_ALIGNED(length)); |
| |
| for (auto& range : periph_ranges) { |
| if (range.length == 0) { |
| base_virt -= length; |
| auto status = arm64_boot_map_v(base_virt, base_phys, length, MMU_INITIAL_MAP_DEVICE); |
| if (status == ZX_OK) { |
| range.base_phys = base_phys; |
| range.base_virt = base_virt; |
| range.length = length; |
| } |
| return status; |
| } else { |
| base_virt -= range.length; |
| } |
| } |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| void reserve_periph_ranges() { |
| for (auto& range : periph_ranges) { |
| if (range.length == 0) { |
| break; |
| } |
| VmAspace::kernel_aspace()->ReserveSpace("periph", range.length, range.base_virt); |
| } |
| } |
| |
| vaddr_t periph_paddr_to_vaddr(paddr_t paddr) { |
| for (auto& range : periph_ranges) { |
| if (range.length == 0) { |
| break; |
| } else if (paddr >= range.base_phys) { |
| uint64_t offset = paddr - range.base_phys; |
| if (offset < range.length) { |
| return range.base_virt + offset; |
| } |
| } |
| } |
| return 0; |
| } |