From 92b45dfeacc25505e7ee31ac33c7dd7e47388b68 Mon Sep 17 00:00:00 2001 From: kettenis Date: Fri, 12 Jan 2018 14:52:55 +0000 Subject: [PATCH] Runtime services may (and do) use device mappings on some UEFI implementations. Skip these mappings during the remap-pahse as they are likely to be in a different 512G bloch as memory and SetVirtualAddressMap() shouldn't need them. But do assign a new virtual address and let efi(4) create a mapping. Add a PMAP_DEVICE flag such that pmap_enter() can continue to be used to create these mappings. ok patrick@ --- sys/arch/arm64/arm64/machdep.c | 156 +++++++++++++++++++-------------- sys/arch/arm64/arm64/pmap.c | 10 ++- sys/arch/arm64/dev/efi.c | 11 ++- sys/arch/arm64/include/pmap.h | 6 +- 4 files changed, 112 insertions(+), 71 deletions(-) diff --git a/sys/arch/arm64/arm64/machdep.c b/sys/arch/arm64/arm64/machdep.c index d3ad1effb6d..702f12e6929 100644 --- a/sys/arch/arm64/arm64/machdep.c +++ b/sys/arch/arm64/arm64/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.25 2018/01/10 23:27:18 kettenis Exp $ */ +/* $OpenBSD: machdep.c,v 1.26 2018/01/12 14:52:55 kettenis Exp $ */ /* * Copyright (c) 2014 Patrick Wildt * @@ -746,6 +746,8 @@ uint32_t mmap_desc_ver; EFI_MEMORY_DESCRIPTOR *mmap; +void remap_efi_runtime(EFI_PHYSICAL_ADDRESS); + void collect_kernel_args(char *); void process_kernel_args(void); @@ -821,9 +823,6 @@ initarm(struct arm64_bootparams *abp) process_kernel_args(); - // XXX - paddr_t pmap_steal_avail(size_t size, int align, void **kva); - void _start(void); long kernbase = (long)&_start & ~0x00fff; @@ -922,67 +921,8 @@ initarm(struct arm64_bootparams *abp) arm64_a4x_bs_tag._space_map = map_a4x_func_save; /* Remap EFI runtime. */ - if (mmap_start != 0 && system_table != 0) { - EFI_SYSTEM_TABLE *st = (EFI_SYSTEM_TABLE *)system_table; - EFI_RUNTIME_SERVICES *rs; - EFI_STATUS status; - EFI_MEMORY_DESCRIPTOR *src; - EFI_MEMORY_DESCRIPTOR *dst; - EFI_PHYSICAL_ADDRESS phys_start = ~0ULL; - EFI_VIRTUAL_ADDRESS virt_start; - vsize_t space; - int i, count = 0; - paddr_t pa; - - /* - * Pick a random address somewhere in the lower half - * of the usable virtual address space. - */ - space = 3 * (VM_MAX_ADDRESS - VM_MIN_ADDRESS) / 4; - virt_start = VM_MIN_ADDRESS + - ((vsize_t)arc4random_uniform(space >> PAGE_SHIFT) << PAGE_SHIFT); - - /* Make sure the EFI system table is mapped. */ - pmap_map_early(system_table, sizeof(EFI_SYSTEM_TABLE)); - rs = st->RuntimeServices; - - /* Make sure memory for EFI runtime services is mapped. */ - src = mmap; - for (i = 0; i < mmap_size / mmap_desc_size; i++) { - if (src->Attribute & EFI_MEMORY_RUNTIME) { - pmap_map_early(src->PhysicalStart, - src->NumberOfPages * PAGE_SIZE); - if (phys_start > src->PhysicalStart) - phys_start = src->PhysicalStart; - count++; - } - src = NextMemoryDescriptor(src, mmap_desc_size); - } - - /* Allocate memory descriptors for new mappings. */ - pa = pmap_steal_avail(count * mmap_desc_size, - mmap_desc_size, NULL); - memset((void *)pa, 0, count * mmap_desc_size); - - src = mmap; - dst = (EFI_MEMORY_DESCRIPTOR *)pa; - for (i = 0; i < mmap_size / mmap_desc_size; i++) { - if (src->Attribute & EFI_MEMORY_RUNTIME) { - src->VirtualStart = virt_start + - (src->PhysicalStart - phys_start); - memcpy(dst, src, mmap_desc_size); - dst = NextMemoryDescriptor(dst, mmap_desc_size); - } - src = NextMemoryDescriptor(src, mmap_desc_size); - } - - /* Install new mappings. */ - dst = (EFI_MEMORY_DESCRIPTOR *)pa; - status = rs->SetVirtualAddressMap(count * mmap_desc_size, - mmap_desc_size, mmap_desc_ver, dst); - if (status != EFI_SUCCESS) - printf("SetVirtualAddressMap failed: %lu\n", status); - } + if (mmap_start != 0 && system_table != 0) + remap_efi_runtime(system_table); /* XXX */ pmap_avail_fixup(); @@ -1077,6 +1017,92 @@ initarm(struct arm64_bootparams *abp) splraise(IPL_IPI); } +void +remap_efi_runtime(EFI_PHYSICAL_ADDRESS system_table) +{ + EFI_SYSTEM_TABLE *st = (EFI_SYSTEM_TABLE *)system_table; + EFI_RUNTIME_SERVICES *rs; + EFI_STATUS status; + EFI_MEMORY_DESCRIPTOR *src; + EFI_MEMORY_DESCRIPTOR *dst; + EFI_PHYSICAL_ADDRESS phys_start = ~0ULL; + EFI_PHYSICAL_ADDRESS phys_end = 0; + EFI_VIRTUAL_ADDRESS virt_start; + vsize_t space; + int i, count = 0; + paddr_t pa; + + /* + * Pick a random address somewhere in the lower half of the + * usable virtual address space. + */ + space = 3 * (VM_MAX_ADDRESS - VM_MIN_ADDRESS) / 4; + virt_start = VM_MIN_ADDRESS + + ((vsize_t)arc4random_uniform(space >> PAGE_SHIFT) << PAGE_SHIFT); + + /* Make sure the EFI system table is mapped. */ + pmap_map_early(system_table, sizeof(EFI_SYSTEM_TABLE)); + rs = st->RuntimeServices; + + /* + * Make sure memory for EFI runtime services is mapped. We + * only map normal memory at this point and pray that the + * SetVirtualAddressMap call doesn't need anything else. + */ + src = mmap; + for (i = 0; i < mmap_size / mmap_desc_size; i++) { + if (src->Attribute & EFI_MEMORY_RUNTIME) { + if (src->Attribute & EFI_MEMORY_WB) { + pmap_map_early(src->PhysicalStart, + src->NumberOfPages * PAGE_SIZE); + phys_start = MIN(phys_start, + src->PhysicalStart); + phys_end = MAX(phys_end, src->PhysicalStart + + src->NumberOfPages * PAGE_SIZE); + } + count++; + } + src = NextMemoryDescriptor(src, mmap_desc_size); + } + + /* Allocate memory descriptors for new mappings. */ + pa = pmap_steal_avail(count * mmap_desc_size, + mmap_desc_size, NULL); + memset((void *)pa, 0, count * mmap_desc_size); + + /* + * Establish new mappings. Apparently some EFI code relies on + * the offset between code and data remaining the same so pick + * virtual addresses for normal memory that meet that + * constraint. Other mappings are simply tagged to the end of + * the last normal memory mapping. + */ + src = mmap; + dst = (EFI_MEMORY_DESCRIPTOR *)pa; + for (i = 0; i < mmap_size / mmap_desc_size; i++) { + if (src->Attribute & EFI_MEMORY_RUNTIME) { + if (src->Attribute & EFI_MEMORY_WB) { + src->VirtualStart = virt_start + + (src->PhysicalStart - phys_start); + } else { + src->VirtualStart = virt_start + + (phys_end - phys_start); + phys_end += src->NumberOfPages * PAGE_SIZE; + } + memcpy(dst, src, mmap_desc_size); + dst = NextMemoryDescriptor(dst, mmap_desc_size); + } + src = NextMemoryDescriptor(src, mmap_desc_size); + } + + /* Install new mappings. */ + dst = (EFI_MEMORY_DESCRIPTOR *)pa; + status = rs->SetVirtualAddressMap(count * mmap_desc_size, + mmap_desc_size, mmap_desc_ver, dst); + if (status != EFI_SUCCESS) + printf("SetVirtualAddressMap failed: %lu\n", status); +} + int comcnspeed = B115200; char bootargs[MAX_BOOT_STRING]; diff --git a/sys/arch/arm64/arm64/pmap.c b/sys/arch/arm64/arm64/pmap.c index 22319d35aae..cfa70ba46ec 100644 --- a/sys/arch/arm64/arm64/pmap.c +++ b/sys/arch/arm64/arm64/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.43 2018/01/10 23:27:18 kettenis Exp $ */ +/* $OpenBSD: pmap.c,v 1.44 2018/01/12 14:52:55 kettenis Exp $ */ /* * Copyright (c) 2008-2009,2014-2016 Dale Rahn * @@ -454,10 +454,14 @@ pmap_enter(pmap_t pm, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags) { struct pte_desc *pted; struct vm_page *pg; - int s, cache, error; + int s, error; + int cache = PMAP_CACHE_WB; int need_sync = 0; - cache = (pa & PMAP_NOCACHE) ? PMAP_CACHE_CI : PMAP_CACHE_WB; + if (pa & PMAP_NOCACHE) + cache = PMAP_CACHE_CI; + if (pa & PMAP_DEVICE) + cache = PMAP_CACHE_DEV; pg = PHYS_TO_VM_PAGE(pa); /* MP - Acquire lock for this pmap */ diff --git a/sys/arch/arm64/dev/efi.c b/sys/arch/arm64/dev/efi.c index ea1aaf35775..9a84863046f 100644 --- a/sys/arch/arm64/dev/efi.c +++ b/sys/arch/arm64/dev/efi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: efi.c,v 1.2 2018/01/10 23:27:18 kettenis Exp $ */ +/* $OpenBSD: efi.c,v 1.3 2018/01/12 14:52:55 kettenis Exp $ */ /* * Copyright (c) 2017 Mark Kettenis @@ -134,10 +134,19 @@ efi_attach(struct device *parent, struct device *self, void *aux) desc->Attribute); #endif + /* + * Normal memory is expected to be "write + * back" cachable. Everything else is mapped + * as device memory. + */ + if ((desc->Attribute & EFI_MEMORY_WB) == 0) + pa |= PMAP_DEVICE; + if (desc->Type == EfiRuntimeServicesCode) prot |= PROT_EXEC; else prot |= PROT_WRITE; + while (npages--) { pmap_enter(sc->sc_pm, va, pa, prot, prot | PMAP_WIRED); diff --git a/sys/arch/arm64/include/pmap.h b/sys/arch/arm64/include/pmap.h index 0c16ae9eaaf..62dc6bfd246 100644 --- a/sys/arch/arm64/include/pmap.h +++ b/sys/arch/arm64/include/pmap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.h,v 1.8 2018/01/04 14:30:08 kettenis Exp $ */ +/* $OpenBSD: pmap.h,v 1.9 2018/01/12 14:52:55 kettenis Exp $ */ /* * Copyright (c) 2008,2009,2014 Dale Rahn * @@ -19,8 +19,9 @@ #include -#define PMAP_PA_MASK ~((paddr_t)PAGE_MASK) /* to remove the flags */ +#define PMAP_PA_MASK ~((paddr_t)PAGE_MASK) /* to remove the flags */ #define PMAP_NOCACHE 0x1 /* non-cacheable memory */ +#define PMAP_DEVICE 0x2 /* device memory */ typedef struct pmap *pmap_t; @@ -87,6 +88,7 @@ vaddr_t pmap_bootstrap(long kvo, paddr_t lpt1, long kernelstart, long kernelend, long ram_start, long ram_end); void pmap_page_ro(pmap_t pm, vaddr_t va, vm_prot_t prot); +paddr_t pmap_steal_avail(size_t size, int align, void **kva); void pmap_avail_fixup(); void pmap_physload_avail(); -- 2.20.1