RISC-V: Add Sv57 page table support
[linux-2.6-microblaze.git] / arch / riscv / mm / init.c
index 332a319..6ddc7c6 100644 (file)
@@ -38,14 +38,16 @@ EXPORT_SYMBOL(kernel_map);
 #endif
 
 #ifdef CONFIG_64BIT
-u64 satp_mode __ro_after_init = !IS_ENABLED(CONFIG_XIP_KERNEL) ? SATP_MODE_48 : SATP_MODE_39;
+u64 satp_mode __ro_after_init = !IS_ENABLED(CONFIG_XIP_KERNEL) ? SATP_MODE_57 : SATP_MODE_39;
 #else
 u64 satp_mode __ro_after_init = SATP_MODE_32;
 #endif
 EXPORT_SYMBOL(satp_mode);
 
 bool pgtable_l4_enabled = IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_XIP_KERNEL);
+bool pgtable_l5_enabled = IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_XIP_KERNEL);
 EXPORT_SYMBOL(pgtable_l4_enabled);
+EXPORT_SYMBOL(pgtable_l5_enabled);
 
 phys_addr_t phys_ram_base __ro_after_init;
 EXPORT_SYMBOL(phys_ram_base);
@@ -227,6 +229,7 @@ pgd_t trampoline_pg_dir[PTRS_PER_PGD] __page_aligned_bss;
 static pte_t fixmap_pte[PTRS_PER_PTE] __page_aligned_bss;
 
 pgd_t early_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE);
+static p4d_t __maybe_unused early_dtb_p4d[PTRS_PER_P4D] __initdata __aligned(PAGE_SIZE);
 static pud_t __maybe_unused early_dtb_pud[PTRS_PER_PUD] __initdata __aligned(PAGE_SIZE);
 static pmd_t __maybe_unused early_dtb_pmd[PTRS_PER_PMD] __initdata __aligned(PAGE_SIZE);
 
@@ -317,6 +320,16 @@ static pmd_t early_pmd[PTRS_PER_PMD] __initdata __aligned(PAGE_SIZE);
 #define early_pmd      ((pmd_t *)XIP_FIXUP(early_pmd))
 #endif /* CONFIG_XIP_KERNEL */
 
+static p4d_t trampoline_p4d[PTRS_PER_P4D] __page_aligned_bss;
+static p4d_t fixmap_p4d[PTRS_PER_P4D] __page_aligned_bss;
+static p4d_t early_p4d[PTRS_PER_P4D] __initdata __aligned(PAGE_SIZE);
+
+#ifdef CONFIG_XIP_KERNEL
+#define trampoline_p4d ((p4d_t *)XIP_FIXUP(trampoline_p4d))
+#define fixmap_p4d     ((p4d_t *)XIP_FIXUP(fixmap_p4d))
+#define early_p4d      ((p4d_t *)XIP_FIXUP(early_p4d))
+#endif /* CONFIG_XIP_KERNEL */
+
 static pud_t trampoline_pud[PTRS_PER_PUD] __page_aligned_bss;
 static pud_t fixmap_pud[PTRS_PER_PUD] __page_aligned_bss;
 static pud_t early_pud[PTRS_PER_PUD] __initdata __aligned(PAGE_SIZE);
@@ -431,6 +444,44 @@ static phys_addr_t alloc_pud_late(uintptr_t va)
        return __pa(vaddr);
 }
 
+static p4d_t *__init get_p4d_virt_early(phys_addr_t pa)
+{
+       return (p4d_t *)((uintptr_t)pa);
+}
+
+static p4d_t *__init get_p4d_virt_fixmap(phys_addr_t pa)
+{
+       clear_fixmap(FIX_P4D);
+       return (p4d_t *)set_fixmap_offset(FIX_P4D, pa);
+}
+
+static p4d_t *__init get_p4d_virt_late(phys_addr_t pa)
+{
+       return (p4d_t *)__va(pa);
+}
+
+static phys_addr_t __init alloc_p4d_early(uintptr_t va)
+{
+       /* Only one P4D is available for early mapping */
+       BUG_ON((va - kernel_map.virt_addr) >> PGDIR_SHIFT);
+
+       return (uintptr_t)early_p4d;
+}
+
+static phys_addr_t __init alloc_p4d_fixmap(uintptr_t va)
+{
+       return memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
+}
+
+static phys_addr_t alloc_p4d_late(uintptr_t va)
+{
+       unsigned long vaddr;
+
+       vaddr = __get_free_page(GFP_KERNEL);
+       BUG_ON(!vaddr);
+       return __pa(vaddr);
+}
+
 static void __init create_pud_mapping(pud_t *pudp,
                                      uintptr_t va, phys_addr_t pa,
                                      phys_addr_t sz, pgprot_t prot)
@@ -458,21 +509,55 @@ static void __init create_pud_mapping(pud_t *pudp,
        create_pmd_mapping(nextp, va, pa, sz, prot);
 }
 
-#define pgd_next_t             pud_t
-#define alloc_pgd_next(__va)   (pgtable_l4_enabled ?                   \
-               pt_ops.alloc_pud(__va) : pt_ops.alloc_pmd(__va))
-#define get_pgd_next_virt(__pa)        (pgtable_l4_enabled ?                   \
-               pt_ops.get_pud_virt(__pa) : (pgd_next_t *)pt_ops.get_pmd_virt(__pa))
+static void __init create_p4d_mapping(p4d_t *p4dp,
+                                     uintptr_t va, phys_addr_t pa,
+                                     phys_addr_t sz, pgprot_t prot)
+{
+       pud_t *nextp;
+       phys_addr_t next_phys;
+       uintptr_t p4d_index = p4d_index(va);
+
+       if (sz == P4D_SIZE) {
+               if (p4d_val(p4dp[p4d_index]) == 0)
+                       p4dp[p4d_index] = pfn_p4d(PFN_DOWN(pa), prot);
+               return;
+       }
+
+       if (p4d_val(p4dp[p4d_index]) == 0) {
+               next_phys = pt_ops.alloc_pud(va);
+               p4dp[p4d_index] = pfn_p4d(PFN_DOWN(next_phys), PAGE_TABLE);
+               nextp = pt_ops.get_pud_virt(next_phys);
+               memset(nextp, 0, PAGE_SIZE);
+       } else {
+               next_phys = PFN_PHYS(_p4d_pfn(p4dp[p4d_index]));
+               nextp = pt_ops.get_pud_virt(next_phys);
+       }
+
+       create_pud_mapping(nextp, va, pa, sz, prot);
+}
+
+#define pgd_next_t             p4d_t
+#define alloc_pgd_next(__va)   (pgtable_l5_enabled ?                   \
+               pt_ops.alloc_p4d(__va) : (pgtable_l4_enabled ?          \
+               pt_ops.alloc_pud(__va) : pt_ops.alloc_pmd(__va)))
+#define get_pgd_next_virt(__pa)        (pgtable_l5_enabled ?                   \
+               pt_ops.get_p4d_virt(__pa) : (pgd_next_t *)(pgtable_l4_enabled ? \
+               pt_ops.get_pud_virt(__pa) : (pud_t *)pt_ops.get_pmd_virt(__pa)))
 #define create_pgd_next_mapping(__nextp, __va, __pa, __sz, __prot)     \
+                               (pgtable_l5_enabled ?                   \
+               create_p4d_mapping(__nextp, __va, __pa, __sz, __prot) : \
                                (pgtable_l4_enabled ?                   \
-               create_pud_mapping(__nextp, __va, __pa, __sz, __prot) : \
-               create_pmd_mapping((pmd_t *)__nextp, __va, __pa, __sz, __prot))
-#define fixmap_pgd_next                (pgtable_l4_enabled ?                   \
-               (uintptr_t)fixmap_pud : (uintptr_t)fixmap_pmd)
-#define trampoline_pgd_next    (pgtable_l4_enabled ?                   \
-               (uintptr_t)trampoline_pud : (uintptr_t)trampoline_pmd)
-#define early_dtb_pgd_next     (pgtable_l4_enabled ?                   \
-               (uintptr_t)early_dtb_pud : (uintptr_t)early_dtb_pmd)
+               create_pud_mapping((pud_t *)__nextp, __va, __pa, __sz, __prot) :        \
+               create_pmd_mapping((pmd_t *)__nextp, __va, __pa, __sz, __prot)))
+#define fixmap_pgd_next                (pgtable_l5_enabled ?                   \
+               (uintptr_t)fixmap_p4d : (pgtable_l4_enabled ?           \
+               (uintptr_t)fixmap_pud : (uintptr_t)fixmap_pmd))
+#define trampoline_pgd_next    (pgtable_l5_enabled ?                   \
+               (uintptr_t)trampoline_p4d : (pgtable_l4_enabled ?       \
+               (uintptr_t)trampoline_pud : (uintptr_t)trampoline_pmd))
+#define early_dtb_pgd_next     (pgtable_l5_enabled ?                   \
+               (uintptr_t)early_dtb_p4d : (pgtable_l4_enabled ?        \
+               (uintptr_t)early_dtb_pud : (uintptr_t)early_dtb_pmd))
 #else
 #define pgd_next_t             pte_t
 #define alloc_pgd_next(__va)   pt_ops.alloc_pte(__va)
@@ -481,6 +566,7 @@ static void __init create_pud_mapping(pud_t *pudp,
        create_pte_mapping(__nextp, __va, __pa, __sz, __prot)
 #define fixmap_pgd_next                ((uintptr_t)fixmap_pte)
 #define early_dtb_pgd_next     ((uintptr_t)early_dtb_pmd)
+#define create_p4d_mapping(__pmdp, __va, __pa, __sz, __prot)
 #define create_pud_mapping(__pmdp, __va, __pa, __sz, __prot)
 #define create_pmd_mapping(__pmdp, __va, __pa, __sz, __prot)
 #endif /* __PAGETABLE_PMD_FOLDED */
@@ -573,6 +659,13 @@ static __init pgprot_t pgprot_from_va(uintptr_t va)
 #endif /* CONFIG_STRICT_KERNEL_RWX */
 
 #ifdef CONFIG_64BIT
+static void __init disable_pgtable_l5(void)
+{
+       pgtable_l5_enabled = false;
+       kernel_map.page_offset = PAGE_OFFSET_L4;
+       satp_mode = SATP_MODE_48;
+}
+
 static void __init disable_pgtable_l4(void)
 {
        pgtable_l4_enabled = false;
@@ -589,12 +682,12 @@ static void __init disable_pgtable_l4(void)
 static __init void set_satp_mode(void)
 {
        u64 identity_satp, hw_satp;
-       uintptr_t set_satp_mode_pmd;
+       uintptr_t set_satp_mode_pmd = ((unsigned long)set_satp_mode) & PMD_MASK;
+       bool check_l4 = false;
 
-       set_satp_mode_pmd = ((unsigned long)set_satp_mode) & PMD_MASK;
-       create_pgd_mapping(early_pg_dir,
-                          set_satp_mode_pmd, (uintptr_t)early_pud,
-                          PGDIR_SIZE, PAGE_TABLE);
+       create_p4d_mapping(early_p4d,
+                       set_satp_mode_pmd, (uintptr_t)early_pud,
+                       P4D_SIZE, PAGE_TABLE);
        create_pud_mapping(early_pud,
                           set_satp_mode_pmd, (uintptr_t)early_pmd,
                           PUD_SIZE, PAGE_TABLE);
@@ -606,6 +699,11 @@ static __init void set_satp_mode(void)
                           set_satp_mode_pmd + PMD_SIZE,
                           set_satp_mode_pmd + PMD_SIZE,
                           PMD_SIZE, PAGE_KERNEL_EXEC);
+retry:
+       create_pgd_mapping(early_pg_dir,
+                          set_satp_mode_pmd,
+                          check_l4 ? (uintptr_t)early_pud : (uintptr_t)early_p4d,
+                          PGDIR_SIZE, PAGE_TABLE);
 
        identity_satp = PFN_DOWN((uintptr_t)&early_pg_dir) | satp_mode;
 
@@ -614,10 +712,17 @@ static __init void set_satp_mode(void)
        hw_satp = csr_swap(CSR_SATP, 0ULL);
        local_flush_tlb_all();
 
-       if (hw_satp != identity_satp)
+       if (hw_satp != identity_satp) {
+               if (!check_l4) {
+                       disable_pgtable_l5();
+                       check_l4 = true;
+                       goto retry;
+               }
                disable_pgtable_l4();
+       }
 
        memset(early_pg_dir, 0, PAGE_SIZE);
+       memset(early_p4d, 0, PAGE_SIZE);
        memset(early_pud, 0, PAGE_SIZE);
        memset(early_pmd, 0, PAGE_SIZE);
 }
@@ -691,10 +796,13 @@ static void __init create_fdt_early_page_table(pgd_t *pgdir, uintptr_t dtb_pa)
                           PGDIR_SIZE,
                           IS_ENABLED(CONFIG_64BIT) ? PAGE_TABLE : PAGE_KERNEL);
 
-       if (pgtable_l4_enabled) {
+       if (pgtable_l5_enabled)
+               create_p4d_mapping(early_dtb_p4d, DTB_EARLY_BASE_VA,
+                                  (uintptr_t)early_dtb_pud, P4D_SIZE, PAGE_TABLE);
+
+       if (pgtable_l4_enabled)
                create_pud_mapping(early_dtb_pud, DTB_EARLY_BASE_VA,
                                   (uintptr_t)early_dtb_pmd, PUD_SIZE, PAGE_TABLE);
-       }
 
        if (IS_ENABLED(CONFIG_64BIT)) {
                create_pmd_mapping(early_dtb_pmd, DTB_EARLY_BASE_VA,
@@ -730,6 +838,8 @@ void __init pt_ops_set_early(void)
        pt_ops.get_pmd_virt = get_pmd_virt_early;
        pt_ops.alloc_pud = alloc_pud_early;
        pt_ops.get_pud_virt = get_pud_virt_early;
+       pt_ops.alloc_p4d = alloc_p4d_early;
+       pt_ops.get_p4d_virt = get_p4d_virt_early;
 #endif
 }
 
@@ -750,6 +860,8 @@ void __init pt_ops_set_fixmap(void)
        pt_ops.get_pmd_virt = kernel_mapping_pa_to_va((uintptr_t)get_pmd_virt_fixmap);
        pt_ops.alloc_pud = kernel_mapping_pa_to_va((uintptr_t)alloc_pud_fixmap);
        pt_ops.get_pud_virt = kernel_mapping_pa_to_va((uintptr_t)get_pud_virt_fixmap);
+       pt_ops.alloc_p4d = kernel_mapping_pa_to_va((uintptr_t)alloc_p4d_fixmap);
+       pt_ops.get_p4d_virt = kernel_mapping_pa_to_va((uintptr_t)get_p4d_virt_fixmap);
 #endif
 }
 
@@ -766,6 +878,8 @@ void __init pt_ops_set_late(void)
        pt_ops.get_pmd_virt = get_pmd_virt_late;
        pt_ops.alloc_pud = alloc_pud_late;
        pt_ops.get_pud_virt = get_pud_virt_late;
+       pt_ops.alloc_p4d = alloc_p4d_late;
+       pt_ops.get_p4d_virt = get_p4d_virt_late;
 #endif
 }
 
@@ -826,6 +940,10 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
                           fixmap_pgd_next, PGDIR_SIZE, PAGE_TABLE);
 
 #ifndef __PAGETABLE_PMD_FOLDED
+       /* Setup fixmap P4D and PUD */
+       if (pgtable_l5_enabled)
+               create_p4d_mapping(fixmap_p4d, FIXADDR_START,
+                                  (uintptr_t)fixmap_pud, P4D_SIZE, PAGE_TABLE);
        /* Setup fixmap PUD and PMD */
        if (pgtable_l4_enabled)
                create_pud_mapping(fixmap_pud, FIXADDR_START,
@@ -835,6 +953,9 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
        /* Setup trampoline PGD and PMD */
        create_pgd_mapping(trampoline_pg_dir, kernel_map.virt_addr,
                           trampoline_pgd_next, PGDIR_SIZE, PAGE_TABLE);
+       if (pgtable_l5_enabled)
+               create_p4d_mapping(trampoline_p4d, kernel_map.virt_addr,
+                                  (uintptr_t)trampoline_pud, P4D_SIZE, PAGE_TABLE);
        if (pgtable_l4_enabled)
                create_pud_mapping(trampoline_pud, kernel_map.virt_addr,
                                   (uintptr_t)trampoline_pmd, PUD_SIZE, PAGE_TABLE);
@@ -936,6 +1057,7 @@ static void __init setup_vm_final(void)
        clear_fixmap(FIX_PTE);
        clear_fixmap(FIX_PMD);
        clear_fixmap(FIX_PUD);
+       clear_fixmap(FIX_P4D);
 
        /* Move to swapper page table */
        csr_write(CSR_SATP, PFN_DOWN(__pa_symbol(swapper_pg_dir)) | satp_mode);