RISC-V: relocate DTB if it's outside memory region
[linux-2.6-microblaze.git] / arch / riscv / mm / init.c
index b0793dc..05ed641 100644 (file)
@@ -208,8 +208,25 @@ static void __init setup_bootmem(void)
         * early_init_fdt_reserve_self() since __pa() does
         * not work for DTB pointers that are fixmap addresses
         */
-       if (!IS_ENABLED(CONFIG_BUILTIN_DTB))
-               memblock_reserve(dtb_early_pa, fdt_totalsize(dtb_early_va));
+       if (!IS_ENABLED(CONFIG_BUILTIN_DTB)) {
+               /*
+                * In case the DTB is not located in a memory region we won't
+                * be able to locate it later on via the linear mapping and
+                * get a segfault when accessing it via __va(dtb_early_pa).
+                * To avoid this situation copy DTB to a memory region.
+                * Note that memblock_phys_alloc will also reserve DTB region.
+                */
+               if (!memblock_is_memory(dtb_early_pa)) {
+                       size_t fdt_size = fdt_totalsize(dtb_early_va);
+                       phys_addr_t new_dtb_early_pa = memblock_phys_alloc(fdt_size, PAGE_SIZE);
+                       void *new_dtb_early_va = early_memremap(new_dtb_early_pa, fdt_size);
+
+                       memcpy(new_dtb_early_va, dtb_early_va, fdt_size);
+                       early_memunmap(new_dtb_early_va, fdt_size);
+                       _dtb_early_pa = new_dtb_early_pa;
+               } else
+                       memblock_reserve(dtb_early_pa, fdt_totalsize(dtb_early_va));
+       }
 
        early_init_fdt_scan_reserved_mem();
        dma_contiguous_reserve(dma32_phys_limit);