1d4c8e725SNicholas Piggin // SPDX-License-Identifier: GPL-2.0-only 2d4c8e725SNicholas Piggin /* 3d4c8e725SNicholas Piggin * Radix MMU support 4d4c8e725SNicholas Piggin * 5d4c8e725SNicholas Piggin * Copyright (C) 2024, IBM Inc, Nicholas Piggin <npiggin@gmail.com> 6d4c8e725SNicholas Piggin * 7d4c8e725SNicholas Piggin * Derived from Linux kernel MMU code. 8d4c8e725SNicholas Piggin */ 9d4c8e725SNicholas Piggin #include <asm/mmu.h> 10d4c8e725SNicholas Piggin #include <asm/setup.h> 11d4c8e725SNicholas Piggin #include <asm/smp.h> 12d4c8e725SNicholas Piggin #include <asm/page.h> 13d4c8e725SNicholas Piggin #include <asm/io.h> 14d4c8e725SNicholas Piggin #include <asm/processor.h> 15d4c8e725SNicholas Piggin #include <asm/hcall.h> 16d4c8e725SNicholas Piggin 17d4c8e725SNicholas Piggin #include "alloc_page.h" 18d4c8e725SNicholas Piggin #include "vmalloc.h" 19d4c8e725SNicholas Piggin #include <asm/pgtable-hwdef.h> 20d4c8e725SNicholas Piggin #include <asm/pgtable.h> 21d4c8e725SNicholas Piggin 22d4c8e725SNicholas Piggin #include <linux/compiler.h> 23d4c8e725SNicholas Piggin 24d4c8e725SNicholas Piggin static pgd_t *identity_pgd; 25d4c8e725SNicholas Piggin 26*b9289d76SNicholas Piggin bool vm_available(void) /* weak override */ 27d4c8e725SNicholas Piggin { 28d4c8e725SNicholas Piggin return cpu_has_radix; 29d4c8e725SNicholas Piggin } 30d4c8e725SNicholas Piggin 31d4c8e725SNicholas Piggin bool mmu_enabled(void) 32d4c8e725SNicholas Piggin { 33d4c8e725SNicholas Piggin return current_cpu()->pgtable != NULL; 34d4c8e725SNicholas Piggin } 35d4c8e725SNicholas Piggin 36d4c8e725SNicholas Piggin void mmu_enable(pgd_t *pgtable) 37d4c8e725SNicholas Piggin { 38d4c8e725SNicholas Piggin struct cpu *cpu = current_cpu(); 39d4c8e725SNicholas Piggin 40d4c8e725SNicholas Piggin if (!pgtable) 41d4c8e725SNicholas Piggin pgtable = identity_pgd; 42d4c8e725SNicholas Piggin 43d4c8e725SNicholas Piggin cpu->pgtable = pgtable; 44d4c8e725SNicholas Piggin 45d4c8e725SNicholas Piggin mtmsr(mfmsr() | (MSR_IR|MSR_DR)); 46d4c8e725SNicholas Piggin } 47d4c8e725SNicholas Piggin 48d4c8e725SNicholas Piggin void mmu_disable(void) 49d4c8e725SNicholas Piggin { 50d4c8e725SNicholas Piggin struct cpu *cpu = current_cpu(); 51d4c8e725SNicholas Piggin 52d4c8e725SNicholas Piggin cpu->pgtable = NULL; 53d4c8e725SNicholas Piggin 54d4c8e725SNicholas Piggin mtmsr(mfmsr() & ~(MSR_IR|MSR_DR)); 55d4c8e725SNicholas Piggin } 56d4c8e725SNicholas Piggin 57d4c8e725SNicholas Piggin static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr) 58d4c8e725SNicholas Piggin { 59d4c8e725SNicholas Piggin pgd_t *pgd = pgd_offset(pgtable, vaddr); 60d4c8e725SNicholas Piggin pud_t *pud = pud_alloc(pgd, vaddr); 61d4c8e725SNicholas Piggin pmd_t *pmd = pmd_alloc(pud, vaddr); 62d4c8e725SNicholas Piggin pte_t *pte = pte_alloc(pmd, vaddr); 63d4c8e725SNicholas Piggin 64d4c8e725SNicholas Piggin return &pte_val(*pte); 65d4c8e725SNicholas Piggin } 66d4c8e725SNicholas Piggin 67d4c8e725SNicholas Piggin static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte) 68d4c8e725SNicholas Piggin { 69d4c8e725SNicholas Piggin pteval_t *p_pte = get_pte(pgtable, vaddr); 70d4c8e725SNicholas Piggin 71d4c8e725SNicholas Piggin if (READ_ONCE(*p_pte) & cpu_to_be64(_PAGE_VALID)) { 72d4c8e725SNicholas Piggin WRITE_ONCE(*p_pte, 0); 73d4c8e725SNicholas Piggin flush_tlb_page(vaddr); 74d4c8e725SNicholas Piggin } 75d4c8e725SNicholas Piggin 76d4c8e725SNicholas Piggin WRITE_ONCE(*p_pte, cpu_to_be64(pte)); 77d4c8e725SNicholas Piggin 78d4c8e725SNicholas Piggin return p_pte; 79d4c8e725SNicholas Piggin } 80d4c8e725SNicholas Piggin 81d4c8e725SNicholas Piggin static pteval_t *install_page_prot(pgd_t *pgtable, phys_addr_t phys, 82d4c8e725SNicholas Piggin uintptr_t vaddr, pgprot_t prot) 83d4c8e725SNicholas Piggin { 84d4c8e725SNicholas Piggin pteval_t pte = phys; 85d4c8e725SNicholas Piggin pte |= _PAGE_VALID | _PAGE_PTE; 86d4c8e725SNicholas Piggin pte |= pgprot_val(prot); 87d4c8e725SNicholas Piggin return install_pte(pgtable, vaddr, pte); 88d4c8e725SNicholas Piggin } 89d4c8e725SNicholas Piggin 90d4c8e725SNicholas Piggin pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt) 91d4c8e725SNicholas Piggin { 92d4c8e725SNicholas Piggin if (!pgtable) 93d4c8e725SNicholas Piggin pgtable = identity_pgd; 94d4c8e725SNicholas Piggin 95d4c8e725SNicholas Piggin return install_page_prot(pgtable, phys, (uintptr_t)virt, 96d4c8e725SNicholas Piggin __pgprot(_PAGE_VALID | _PAGE_PTE | 97d4c8e725SNicholas Piggin _PAGE_READ | _PAGE_WRITE | 98d4c8e725SNicholas Piggin _PAGE_EXEC | _PAGE_ACCESSED | 99d4c8e725SNicholas Piggin _PAGE_DIRTY)); 100d4c8e725SNicholas Piggin } 101d4c8e725SNicholas Piggin 102d4c8e725SNicholas Piggin static pteval_t *follow_pte(pgd_t *pgtable, uintptr_t vaddr) 103d4c8e725SNicholas Piggin { 104d4c8e725SNicholas Piggin pgd_t *pgd; 105d4c8e725SNicholas Piggin pud_t *pud; 106d4c8e725SNicholas Piggin pmd_t *pmd; 107d4c8e725SNicholas Piggin pte_t *pte; 108d4c8e725SNicholas Piggin 109d4c8e725SNicholas Piggin pgd = pgd_offset(pgtable, vaddr); 110d4c8e725SNicholas Piggin if (!pgd_valid(*pgd)) 111d4c8e725SNicholas Piggin return NULL; 112d4c8e725SNicholas Piggin 113d4c8e725SNicholas Piggin pud = pud_offset(pgd, vaddr); 114d4c8e725SNicholas Piggin if (!pud_valid(*pud)) 115d4c8e725SNicholas Piggin return NULL; 116d4c8e725SNicholas Piggin 117d4c8e725SNicholas Piggin pmd = pmd_offset(pud, vaddr); 118d4c8e725SNicholas Piggin if (!pmd_valid(*pmd)) 119d4c8e725SNicholas Piggin return NULL; 120d4c8e725SNicholas Piggin if (pmd_huge(*pmd)) 121d4c8e725SNicholas Piggin return &pmd_val(*pmd); 122d4c8e725SNicholas Piggin 123d4c8e725SNicholas Piggin pte = pte_offset(pmd, vaddr); 124d4c8e725SNicholas Piggin if (!pte_valid(*pte)) 125d4c8e725SNicholas Piggin return NULL; 126d4c8e725SNicholas Piggin 127d4c8e725SNicholas Piggin return &pte_val(*pte); 128d4c8e725SNicholas Piggin } 129d4c8e725SNicholas Piggin 130d4c8e725SNicholas Piggin phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *virt) 131d4c8e725SNicholas Piggin { 132d4c8e725SNicholas Piggin phys_addr_t mask; 133d4c8e725SNicholas Piggin pteval_t *pteval; 134d4c8e725SNicholas Piggin 135d4c8e725SNicholas Piggin if (!pgtable) 136d4c8e725SNicholas Piggin pgtable = identity_pgd; 137d4c8e725SNicholas Piggin 138d4c8e725SNicholas Piggin pteval = follow_pte(pgtable, (uintptr_t)virt); 139d4c8e725SNicholas Piggin if (!pteval) { 140d4c8e725SNicholas Piggin install_page(pgtable, (phys_addr_t)(unsigned long)virt, virt); 141d4c8e725SNicholas Piggin return (phys_addr_t)(unsigned long)virt; 142d4c8e725SNicholas Piggin } 143d4c8e725SNicholas Piggin 144d4c8e725SNicholas Piggin if (pmd_huge(__pmd(*pteval))) 145d4c8e725SNicholas Piggin mask = PMD_MASK; 146d4c8e725SNicholas Piggin else 147d4c8e725SNicholas Piggin mask = PAGE_MASK; 148d4c8e725SNicholas Piggin 149d4c8e725SNicholas Piggin return (be64_to_cpu(*pteval) & PHYS_MASK & mask) | 150d4c8e725SNicholas Piggin ((phys_addr_t)(unsigned long)virt & ~mask); 151d4c8e725SNicholas Piggin } 152d4c8e725SNicholas Piggin 153d4c8e725SNicholas Piggin struct partition_table_entry { 154d4c8e725SNicholas Piggin uint64_t dw0; 155d4c8e725SNicholas Piggin uint64_t dw1; 156d4c8e725SNicholas Piggin }; 157d4c8e725SNicholas Piggin 158d4c8e725SNicholas Piggin static struct partition_table_entry *partition_table; 159d4c8e725SNicholas Piggin 160d4c8e725SNicholas Piggin struct process_table_entry { 161d4c8e725SNicholas Piggin uint64_t dw0; 162d4c8e725SNicholas Piggin uint64_t dw1; 163d4c8e725SNicholas Piggin }; 164d4c8e725SNicholas Piggin 165d4c8e725SNicholas Piggin static struct process_table_entry *process_table; 166d4c8e725SNicholas Piggin 167d4c8e725SNicholas Piggin void *setup_mmu(phys_addr_t phys_end, void *unused) 168d4c8e725SNicholas Piggin { 169d4c8e725SNicholas Piggin phys_addr_t addr; 170d4c8e725SNicholas Piggin uint64_t dw0, dw1; 171d4c8e725SNicholas Piggin 172d4c8e725SNicholas Piggin if (identity_pgd) 173d4c8e725SNicholas Piggin goto enable; 174d4c8e725SNicholas Piggin 175d4c8e725SNicholas Piggin assert_msg(cpu_has_radix, "MMU support requires radix MMU."); 176d4c8e725SNicholas Piggin 177d4c8e725SNicholas Piggin /* 32G address is reserved for vmalloc, cap phys_end at 31G */ 178d4c8e725SNicholas Piggin if (phys_end > (31ul << 30)) { 179d4c8e725SNicholas Piggin /* print warning */ 180d4c8e725SNicholas Piggin phys_end = 31ul << 30; 181d4c8e725SNicholas Piggin } 182d4c8e725SNicholas Piggin 183d4c8e725SNicholas Piggin init_alloc_vpage((void *)(32ul << 30)); 184d4c8e725SNicholas Piggin 185d4c8e725SNicholas Piggin process_table = memalign_pages(SZ_4K, SZ_4K); 186d4c8e725SNicholas Piggin memset(process_table, 0, SZ_4K); 187d4c8e725SNicholas Piggin 188d4c8e725SNicholas Piggin identity_pgd = pgd_alloc_one(); 189d4c8e725SNicholas Piggin 190d4c8e725SNicholas Piggin dw0 = (unsigned long)identity_pgd; 191d4c8e725SNicholas Piggin dw0 |= 16UL - 3; /* 64K pgd size */ 192d4c8e725SNicholas Piggin dw0 |= (0x2UL << 61) | (0x5UL << 5); /* 52-bit virt */ 193d4c8e725SNicholas Piggin process_table[1].dw0 = cpu_to_be64(dw0); 194d4c8e725SNicholas Piggin 195d4c8e725SNicholas Piggin if (machine_is_pseries()) { 196d4c8e725SNicholas Piggin int ret; 197d4c8e725SNicholas Piggin 198d4c8e725SNicholas Piggin ret = hcall(H_REGISTER_PROCESS_TABLE, PTBL_NEW | PTBL_RADIX | PTBL_GTSE, process_table, 0, 0 /* 4K size */); 199d4c8e725SNicholas Piggin assert_msg(!ret, "H_REGISTER_PROCESS_TABLE failed! err=%d\n", ret); 200d4c8e725SNicholas Piggin } else if (machine_is_powernv()) { 201d4c8e725SNicholas Piggin partition_table = memalign_pages(SZ_4K, SZ_4K); 202d4c8e725SNicholas Piggin memset(partition_table, 0, SZ_4K); 203d4c8e725SNicholas Piggin 204d4c8e725SNicholas Piggin /* Reuse dw0 for partition table */ 205d4c8e725SNicholas Piggin dw0 |= 1ULL << 63; /* Host radix */ 206d4c8e725SNicholas Piggin dw1 = (unsigned long)process_table; /* 4K size */ 207d4c8e725SNicholas Piggin partition_table[0].dw0 = cpu_to_be64(dw0); 208d4c8e725SNicholas Piggin partition_table[0].dw1 = cpu_to_be64(dw1); 209d4c8e725SNicholas Piggin 210d4c8e725SNicholas Piggin } else { 211d4c8e725SNicholas Piggin /* Only pseries and powernv support radix so far */ 212d4c8e725SNicholas Piggin assert(0); 213d4c8e725SNicholas Piggin } 214d4c8e725SNicholas Piggin 215d4c8e725SNicholas Piggin /* 216d4c8e725SNicholas Piggin * Avoid mapping page 0 so NULL dereferences fault. Test images 217d4c8e725SNicholas Piggin * run relocated well above 0, so nothing useful here except 218d4c8e725SNicholas Piggin * real-mode interrupt entry code. 219d4c8e725SNicholas Piggin */ 220d4c8e725SNicholas Piggin for (addr = PAGE_SIZE; addr < phys_end; addr += PAGE_SIZE) 221d4c8e725SNicholas Piggin install_page(identity_pgd, addr, __va(addr)); 222d4c8e725SNicholas Piggin 223d4c8e725SNicholas Piggin enable: 224d4c8e725SNicholas Piggin if (machine_is_powernv()) { 225d4c8e725SNicholas Piggin mtspr(SPR_PTCR, (unsigned long)partition_table); /* 4KB size */ 226d4c8e725SNicholas Piggin 227d4c8e725SNicholas Piggin mtspr(SPR_LPIDR, 0); 228d4c8e725SNicholas Piggin /* Set LPCR[UPRT] and LPCR[HR] for radix */ 229d4c8e725SNicholas Piggin mtspr(SPR_LPCR, mfspr(SPR_LPCR) | (1ULL << 22) | (1ULL << 20)); 230d4c8e725SNicholas Piggin } 231d4c8e725SNicholas Piggin 232d4c8e725SNicholas Piggin /* PID=1 is used because PID=0 is also mapped in quadrant 3 */ 233d4c8e725SNicholas Piggin mtspr(SPR_PIDR, 1); 234d4c8e725SNicholas Piggin 235d4c8e725SNicholas Piggin mmu_enable(identity_pgd); 236d4c8e725SNicholas Piggin 237d4c8e725SNicholas Piggin return identity_pgd; 238d4c8e725SNicholas Piggin } 239d4c8e725SNicholas Piggin 240d4c8e725SNicholas Piggin phys_addr_t __virt_to_phys(unsigned long addr) 241d4c8e725SNicholas Piggin { 242d4c8e725SNicholas Piggin if (mmu_enabled()) { 243d4c8e725SNicholas Piggin pgd_t *pgtable = current_cpu()->pgtable; 244d4c8e725SNicholas Piggin return virt_to_pte_phys(pgtable, (void *)addr); 245d4c8e725SNicholas Piggin } 246d4c8e725SNicholas Piggin return addr; 247d4c8e725SNicholas Piggin } 248d4c8e725SNicholas Piggin 249d4c8e725SNicholas Piggin unsigned long __phys_to_virt(phys_addr_t addr) 250d4c8e725SNicholas Piggin { 251d4c8e725SNicholas Piggin /* 252d4c8e725SNicholas Piggin * We don't guarantee that phys_to_virt(virt_to_phys(vaddr)) == vaddr, but 253d4c8e725SNicholas Piggin * the default page tables do identity map all physical addresses, which 254d4c8e725SNicholas Piggin * means phys_to_virt(virt_to_phys((void *)paddr)) == paddr. 255d4c8e725SNicholas Piggin */ 256d4c8e725SNicholas Piggin assert(!mmu_enabled() || __virt_to_phys(addr) == addr); 257d4c8e725SNicholas Piggin return addr; 258d4c8e725SNicholas Piggin } 259