efi: Add a function to look up existing IO memory mappings
authorJosh Triplett <josh@joshtriplett.org>
Sat, 29 Sep 2012 00:56:08 +0000 (17:56 -0700)
committerH. Peter Anvin <hpa@linux.intel.com>
Sat, 29 Sep 2012 19:21:02 +0000 (12:21 -0700)
The EFI initialization creates virtual mappings for EFI boot services
memory, so if a driver wants to access EFI boot services memory, it
cannot call ioremap itself; doing so will trip the WARN about mapping
RAM twice.  Thus, a driver accessing EFI boot services memory must do so
via the existing mapping already created during EFI intiialization.
Since the EFI code already maintains a memory map for that memory, add a
function efi_lookup_mapped_addr to look up mappings in that memory map.

Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Link: http://lkml.kernel.org/r/0eb48ae012797912874919110660ad420b90268b.1348876882.git.josh@joshtriplett.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
arch/x86/platform/efi/efi.c
include/linux/efi.h

index b3dbbdbd2a42b4b4a2658dd7b850b1fdb7acafbc..f7f928c315da1659b755f23fe63b3c49b2b5e525 100644 (file)
@@ -776,6 +776,34 @@ static void __init runtime_code_page_mkexec(void)
        }
 }
 
+/*
+ * We can't ioremap data in EFI boot services RAM, because we've already mapped
+ * it as RAM.  So, look it up in the existing EFI memory map instead.  Only
+ * callable after efi_enter_virtual_mode and before efi_free_boot_services.
+ */
+void __iomem *efi_lookup_mapped_addr(u64 phys_addr)
+{
+       void *p;
+       if (WARN_ON(!memmap.map))
+               return NULL;
+       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+               efi_memory_desc_t *md = p;
+               u64 size = md->num_pages << EFI_PAGE_SHIFT;
+               u64 end = md->phys_addr + size;
+               if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+                   md->type != EFI_BOOT_SERVICES_CODE &&
+                   md->type != EFI_BOOT_SERVICES_DATA)
+                       continue;
+               if (!md->virt_addr)
+                       continue;
+               if (phys_addr >= md->phys_addr && phys_addr < end) {
+                       phys_addr += md->virt_addr - md->phys_addr;
+                       return (__force void __iomem *)(unsigned long)phys_addr;
+               }
+       }
+       return NULL;
+}
+
 /*
  * This function will switch the EFI runtime services to virtual mode.
  * Essentially, look through the EFI memmap and map every region that
index 5782114f4838658fc75802ba5e26e68e64ff2ed7..fff135d9375e05274afbc046bf4d04339d7c845f 100644 (file)
@@ -501,6 +501,7 @@ extern void efi_free_boot_services(void);
 #else
 static inline void efi_free_boot_services(void) {}
 #endif
+extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
 extern u64 efi_get_iobase (void);
 extern u32 efi_mem_type (unsigned long phys_addr);
 extern u64 efi_mem_attributes (unsigned long phys_addr);