Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: Add function to create identity mappings

At boot time, before switching to a virtual UEFI memory map, firmware
expects UEFI memory and IO regions to be identity mapped whenever
kernel makes runtime services calls. The existing early boot code
creates an identity map of kernel text/data but this is not sufficient
for UEFI. This patch adds a create_id_mapping() function which reuses
the core code of the existing create_mapping().

Signed-off-by: Mark Salter <msalter@redhat.com>
[ Fixed error message formatting (%pa). ]
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>

authored by

Mark Salter and committed by
Matt Fleming
d7ecbddf 0302f71c

+49 -18
+2
arch/arm64/include/asm/mmu.h
··· 28 28 extern void setup_mm_for_reboot(void); 29 29 extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); 30 30 extern void init_mem_pgprot(void); 31 + /* create an identity mapping for memory (or io if map_io is true) */ 32 + extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io); 31 33 32 34 #endif
+47 -18
arch/arm64/mm/mmu.c
··· 168 168 } 169 169 170 170 static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, 171 - unsigned long end, unsigned long pfn) 171 + unsigned long end, unsigned long pfn, 172 + pgprot_t prot) 172 173 { 173 174 pte_t *pte; 174 175 ··· 181 180 182 181 pte = pte_offset_kernel(pmd, addr); 183 182 do { 184 - set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); 183 + set_pte(pte, pfn_pte(pfn, prot)); 185 184 pfn++; 186 185 } while (pte++, addr += PAGE_SIZE, addr != end); 187 186 } 188 187 189 188 static void __init alloc_init_pmd(pud_t *pud, unsigned long addr, 190 - unsigned long end, phys_addr_t phys) 189 + unsigned long end, phys_addr_t phys, 190 + int map_io) 191 191 { 192 192 pmd_t *pmd; 193 193 unsigned long next; 194 + pmdval_t prot_sect; 195 + pgprot_t prot_pte; 196 + 197 + if (map_io) { 198 + prot_sect = PMD_TYPE_SECT | PMD_SECT_AF | 199 + PMD_ATTRINDX(MT_DEVICE_nGnRE); 200 + prot_pte = __pgprot(PROT_DEVICE_nGnRE); 201 + } else { 202 + prot_sect = prot_sect_kernel; 203 + prot_pte = PAGE_KERNEL_EXEC; 204 + } 194 205 195 206 /* 196 207 * Check for initial section mappings in the pgd/pud and remove them. ··· 218 205 /* try section mapping first */ 219 206 if (((addr | next | phys) & ~SECTION_MASK) == 0) { 220 207 pmd_t old_pmd =*pmd; 221 - set_pmd(pmd, __pmd(phys | prot_sect_kernel)); 208 + set_pmd(pmd, __pmd(phys | prot_sect)); 222 209 /* 223 210 * Check for previous table entries created during 224 211 * boot (__create_page_tables) and flush them. ··· 226 213 if (!pmd_none(old_pmd)) 227 214 flush_tlb_all(); 228 215 } else { 229 - alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys)); 216 + alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys), 217 + prot_pte); 230 218 } 231 219 phys += next - addr; 232 220 } while (pmd++, addr = next, addr != end); 233 221 } 234 222 235 223 static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, 236 - unsigned long end, unsigned long phys) 224 + unsigned long end, unsigned long phys, 225 + int map_io) 237 226 { 238 227 pud_t *pud = pud_offset(pgd, addr); 239 228 unsigned long next; 240 229 241 230 do { 242 231 next = pud_addr_end(addr, end); 243 - alloc_init_pmd(pud, addr, next, phys); 232 + alloc_init_pmd(pud, addr, next, phys, map_io); 244 233 phys += next - addr; 245 234 } while (pud++, addr = next, addr != end); 246 235 } ··· 251 236 * Create the page directory entries and any necessary page tables for the 252 237 * mapping specified by 'md'. 253 238 */ 254 - static void __init create_mapping(phys_addr_t phys, unsigned long virt, 255 - phys_addr_t size) 239 + static void __init __create_mapping(pgd_t *pgd, phys_addr_t phys, 240 + unsigned long virt, phys_addr_t size, 241 + int map_io) 256 242 { 257 243 unsigned long addr, length, end, next; 258 - pgd_t *pgd; 259 - 260 - if (virt < VMALLOC_START) { 261 - pr_warning("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n", 262 - phys, virt); 263 - return; 264 - } 265 244 266 245 addr = virt & PAGE_MASK; 267 246 length = PAGE_ALIGN(size + (virt & ~PAGE_MASK)); 268 247 269 - pgd = pgd_offset_k(addr); 270 248 end = addr + length; 271 249 do { 272 250 next = pgd_addr_end(addr, end); 273 - alloc_init_pud(pgd, addr, next, phys); 251 + alloc_init_pud(pgd, addr, next, phys, map_io); 274 252 phys += next - addr; 275 253 } while (pgd++, addr = next, addr != end); 254 + } 255 + 256 + static void __init create_mapping(phys_addr_t phys, unsigned long virt, 257 + phys_addr_t size) 258 + { 259 + if (virt < VMALLOC_START) { 260 + pr_warn("BUG: not creating mapping for %pa at 0x%016lx - outside kernel range\n", 261 + &phys, virt); 262 + return; 263 + } 264 + __create_mapping(pgd_offset_k(virt & PAGE_MASK), phys, virt, size, 0); 265 + } 266 + 267 + void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io) 268 + { 269 + if ((addr >> PGDIR_SHIFT) >= ARRAY_SIZE(idmap_pg_dir)) { 270 + pr_warn("BUG: not creating id mapping for %pa\n", &addr); 271 + return; 272 + } 273 + __create_mapping(&idmap_pg_dir[pgd_index(addr)], 274 + addr, addr, size, map_io); 276 275 } 277 276 278 277 static void __init map_mem(void)