Merge branch 'for-4.2/sg' of git://git.kernel.dk/linux-block
[firefly-linux-kernel-4.4.55.git] / arch / arm64 / mm / mmu.c
index 5b8b664422d369a00dc6f6bc78b6f28ffd32da1b..82d3435bf14ffdf91fccb2e4857a1028c22750b6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/libfdt.h>
 #include <linux/mman.h>
 #include <linux/nodemask.h>
 #include <linux/memblock.h>
@@ -643,3 +644,68 @@ void __set_fixmap(enum fixed_addresses idx,
                flush_tlb_kernel_range(addr, addr+PAGE_SIZE);
        }
 }
+
+void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
+{
+       const u64 dt_virt_base = __fix_to_virt(FIX_FDT);
+       pgprot_t prot = PAGE_KERNEL | PTE_RDONLY;
+       int granularity, size, offset;
+       void *dt_virt;
+
+       /*
+        * Check whether the physical FDT address is set and meets the minimum
+        * alignment requirement. Since we are relying on MIN_FDT_ALIGN to be
+        * at least 8 bytes so that we can always access the size field of the
+        * FDT header after mapping the first chunk, double check here if that
+        * is indeed the case.
+        */
+       BUILD_BUG_ON(MIN_FDT_ALIGN < 8);
+       if (!dt_phys || dt_phys % MIN_FDT_ALIGN)
+               return NULL;
+
+       /*
+        * Make sure that the FDT region can be mapped without the need to
+        * allocate additional translation table pages, so that it is safe
+        * to call create_mapping() this early.
+        *
+        * On 64k pages, the FDT will be mapped using PTEs, so we need to
+        * be in the same PMD as the rest of the fixmap.
+        * On 4k pages, we'll use section mappings for the FDT so we only
+        * have to be in the same PUD.
+        */
+       BUILD_BUG_ON(dt_virt_base % SZ_2M);
+
+       if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) {
+               BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> PMD_SHIFT !=
+                            __fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT);
+
+               granularity = PAGE_SIZE;
+       } else {
+               BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> PUD_SHIFT !=
+                            __fix_to_virt(FIX_BTMAP_BEGIN) >> PUD_SHIFT);
+
+               granularity = PMD_SIZE;
+       }
+
+       offset = dt_phys % granularity;
+       dt_virt = (void *)dt_virt_base + offset;
+
+       /* map the first chunk so we can read the size from the header */
+       create_mapping(round_down(dt_phys, granularity), dt_virt_base,
+                      granularity, prot);
+
+       if (fdt_check_header(dt_virt) != 0)
+               return NULL;
+
+       size = fdt_totalsize(dt_virt);
+       if (size > MAX_FDT_SIZE)
+               return NULL;
+
+       if (offset + size > granularity)
+               create_mapping(round_down(dt_phys, granularity), dt_virt_base,
+                              round_up(offset + size, granularity), prot);
+
+       memblock_reserve(dt_phys, size);
+
+       return dt_virt;
+}