Runtime services may (and do) use device mappings on some UEFI implementations.
authorkettenis <kettenis@openbsd.org>
Fri, 12 Jan 2018 14:52:55 +0000 (14:52 +0000)
committerkettenis <kettenis@openbsd.org>
Fri, 12 Jan 2018 14:52:55 +0000 (14:52 +0000)
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
sys/arch/arm64/arm64/pmap.c
sys/arch/arm64/dev/efi.c
sys/arch/arm64/include/pmap.h

index d3ad1ef..702f12e 100644 (file)
@@ -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 <patrick@blueri.se>
  *
@@ -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];
 
index 22319d3..cfa70ba 100644 (file)
@@ -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 <drahn@dalerahn.com>
  *
@@ -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 */
index ea1aaf3..9a84863 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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);
index 0c16ae9..62dc6bf 100644 (file)
@@ -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 <drahn@dalerahn.com>
  *
@@ -19,8 +19,9 @@
 
 #include <arm64/pte.h>
 
-#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();