Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | // SPDX-License-Identifier: GPL-2.0 /* * Extensible Firmware Interface * * Based on Extensible Firmware Interface Specification version 2.4 * * Copyright (C) 2013, 2014 Linaro Ltd. */ #include <linux/dmi.h> #include <linux/efi.h> #include <linux/io.h> #include <linux/memblock.h> #include <linux/mm_types.h> #include <linux/preempt.h> #include <linux/rbtree.h> #include <linux/rwsem.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/pgtable.h> #include <asm/cacheflush.h> #include <asm/efi.h> #include <asm/mmu.h> #include <asm/pgalloc.h> #if defined(CONFIG_PTDUMP_DEBUGFS) || defined(CONFIG_ARM_PTDUMP_DEBUGFS) #include <asm/ptdump.h> static struct ptdump_info efi_ptdump_info = { .mm = &efi_mm, .markers = (struct addr_marker[]){ { 0, "UEFI runtime start" }, { EFI_RUNTIME_MAP_END, "UEFI runtime end" }, { -1, NULL } }, .base_addr = 0, }; static int __init ptdump_init(void) { if (efi_enabled(EFI_RUNTIME_SERVICES)) ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables"); return 0; } device_initcall(ptdump_init); #endif static bool __init efi_virtmap_init(void) { efi_memory_desc_t *md; efi_mm.pgd = pgd_alloc(&efi_mm); mm_init_cpumask(&efi_mm); init_new_context(NULL, &efi_mm); for_each_efi_memory_desc(md) { phys_addr_t phys = md->phys_addr; int ret; if (!(md->attribute & EFI_MEMORY_RUNTIME)) continue; if (md->virt_addr == U64_MAX) return false; ret = efi_create_mapping(&efi_mm, md); if (ret) { pr_warn(" EFI remap %pa: failed to create mapping (%d)\n", &phys, ret); return false; } } if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions)) return false; return true; } /* * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., * non-early mapping of the UEFI system table and virtual mappings for all * EFI_MEMORY_RUNTIME regions. */ static int __init arm_enable_runtime_services(void) { u64 mapsize; if (!efi_enabled(EFI_BOOT)) { pr_info("EFI services will not be available.\n"); return 0; } efi_memmap_unmap(); mapsize = efi.memmap.desc_size * efi.memmap.nr_map; if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) { pr_err("Failed to remap EFI memory map\n"); return 0; } if (efi_soft_reserve_enabled()) { efi_memory_desc_t *md; for_each_efi_memory_desc(md) { int md_size = md->num_pages << EFI_PAGE_SHIFT; struct resource *res; if (!(md->attribute & EFI_MEMORY_SP)) continue; res = kzalloc(sizeof(*res), GFP_KERNEL); if (WARN_ON(!res)) break; res->start = md->phys_addr; res->end = md->phys_addr + md_size - 1; res->name = "Soft Reserved"; res->flags = IORESOURCE_MEM; res->desc = IORES_DESC_SOFT_RESERVED; insert_resource(&iomem_resource, res); } } if (efi_runtime_disabled()) { pr_info("EFI runtime services will be disabled.\n"); return 0; } if (efi_enabled(EFI_RUNTIME_SERVICES)) { pr_info("EFI runtime services access via paravirt.\n"); return 0; } pr_info("Remapping and enabling EFI services.\n"); if (!efi_virtmap_init()) { pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n"); return -ENOMEM; } /* Set up runtime services function pointers */ efi_native_runtime_setup(); set_bit(EFI_RUNTIME_SERVICES, &efi.flags); return 0; } early_initcall(arm_enable_runtime_services); void efi_virtmap_load(void) { preempt_disable(); efi_set_pgd(&efi_mm); } void efi_virtmap_unload(void) { efi_set_pgd(current->active_mm); preempt_enable(); } static int __init arm_dmi_init(void) { /* * On arm64/ARM, DMI depends on UEFI, and dmi_setup() needs to * be called early because dmi_id_init(), which is an arch_initcall * itself, depends on dmi_scan_machine() having been called already. */ dmi_setup(); return 0; } core_initcall(arm_dmi_init); |