1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * s390x MMU 4 * 5 * Copyright (c) 2017 Red Hat Inc 6 * 7 * Authors: 8 * David Hildenbrand <david@redhat.com> 9 */ 10 11 #include <libcflat.h> 12 #include <asm/pgtable.h> 13 #include <asm/arch_def.h> 14 #include <asm/barrier.h> 15 #include <vmalloc.h> 16 #include "mmu.h" 17 18 /* 19 * The naming convention used here is the same as used in the Linux kernel; 20 * this is the correspondence between the s390x architectural names and the 21 * Linux ones: 22 * 23 * pgd - region 1 table entry 24 * p4d - region 2 table entry 25 * pud - region 3 table entry 26 * pmd - segment table entry 27 * pte - page table entry 28 */ 29 30 static pgd_t *table_root; 31 32 static void mmu_enable(pgd_t *pgtable) 33 { 34 const uint64_t asce = __pa(pgtable) | ASCE_DT_REGION1 | 35 REGION_TABLE_LENGTH; 36 37 /* set primary asce */ 38 lctlg(1, asce); 39 assert(stctg(1) == asce); 40 41 /* enable dat (primary == 0 set as default) */ 42 enable_dat(); 43 44 /* we can now also use DAT unconditionally in our PGM handler */ 45 lowcore.pgm_new_psw.mask |= PSW_MASK_DAT; 46 } 47 48 /* 49 * Get the pud (region 3) DAT table entry for the given address and root, 50 * allocating it if necessary 51 */ 52 static inline pud_t *get_pud(pgd_t *pgtable, uintptr_t vaddr) 53 { 54 pgd_t *pgd = pgd_offset(pgtable, vaddr); 55 p4d_t *p4d = p4d_alloc(pgd, vaddr); 56 pud_t *pud = pud_alloc(p4d, vaddr); 57 58 return pud; 59 } 60 61 /* 62 * Get the pmd (segment) DAT table entry for the given address and pud, 63 * allocating it if necessary. 64 * The pud must not be huge. 65 */ 66 static inline pmd_t *get_pmd(pud_t *pud, uintptr_t vaddr) 67 { 68 pmd_t *pmd; 69 70 assert(!pud_huge(*pud)); 71 pmd = pmd_alloc(pud, vaddr); 72 return pmd; 73 } 74 75 /* 76 * Get the pte (page) DAT table entry for the given address and pmd, 77 * allocating it if necessary. 78 * The pmd must not be large. 79 */ 80 static inline pte_t *get_pte(pmd_t *pmd, uintptr_t vaddr) 81 { 82 pte_t *pte; 83 84 assert(!pmd_large(*pmd)); 85 pte = pte_alloc(pmd, vaddr); 86 return pte; 87 } 88 89 /* 90 * Splits a large pmd (segment) DAT table entry into equivalent 4kB small 91 * pages. 92 * @pmd The pmd to split, it must be large. 93 * @va the virtual address corresponding to this pmd. 94 */ 95 static void split_pmd(pmd_t *pmd, uintptr_t va) 96 { 97 phys_addr_t pa = pmd_val(*pmd) & SEGMENT_ENTRY_SFAA; 98 unsigned long i, prot; 99 pte_t *pte; 100 101 assert(pmd_large(*pmd)); 102 pte = alloc_pages(PAGE_TABLE_ORDER); 103 prot = pmd_val(*pmd) & (SEGMENT_ENTRY_IEP | SEGMENT_ENTRY_P); 104 for (i = 0; i < PAGE_TABLE_ENTRIES; i++) 105 pte_val(pte[i]) = pa | PAGE_SIZE * i | prot; 106 idte_pmdp(va, &pmd_val(*pmd)); 107 pmd_val(*pmd) = __pa(pte) | SEGMENT_ENTRY_TT_SEGMENT; 108 109 } 110 111 /* 112 * Splits a huge pud (region 3) DAT table entry into equivalent 1MB large 113 * pages. 114 * @pud The pud to split, it must be huge. 115 * @va the virtual address corresponding to this pud. 116 */ 117 static void split_pud(pud_t *pud, uintptr_t va) 118 { 119 phys_addr_t pa = pud_val(*pud) & REGION3_ENTRY_RFAA; 120 unsigned long i, prot; 121 pmd_t *pmd; 122 123 assert(pud_huge(*pud)); 124 pmd = alloc_pages(SEGMENT_TABLE_ORDER); 125 prot = pud_val(*pud) & (REGION3_ENTRY_IEP | REGION_ENTRY_P); 126 for (i = 0; i < SEGMENT_TABLE_ENTRIES; i++) 127 pmd_val(pmd[i]) = pa | SZ_1M * i | prot | SEGMENT_ENTRY_FC | SEGMENT_ENTRY_TT_SEGMENT; 128 idte_pudp(va, &pud_val(*pud)); 129 pud_val(*pud) = __pa(pmd) | REGION_ENTRY_TT_REGION3 | REGION_TABLE_LENGTH; 130 } 131 132 void *get_dat_entry(pgd_t *pgtable, void *vaddr, enum pgt_level level) 133 { 134 uintptr_t va = (uintptr_t)vaddr; 135 pgd_t *pgd; 136 p4d_t *p4d; 137 pud_t *pud; 138 pmd_t *pmd; 139 140 assert(level && (level <= 5)); 141 pgd = pgd_offset(pgtable, va); 142 if (level == pgtable_level_pgd) 143 return pgd; 144 p4d = p4d_alloc(pgd, va); 145 if (level == pgtable_level_p4d) 146 return p4d; 147 pud = pud_alloc(p4d, va); 148 149 if (level == pgtable_level_pud) 150 return pud; 151 if (!pud_none(*pud) && pud_huge(*pud)) 152 split_pud(pud, va); 153 pmd = get_pmd(pud, va); 154 if (level == pgtable_level_pmd) 155 return pmd; 156 if (!pmd_none(*pmd) && pmd_large(*pmd)) 157 split_pmd(pmd, va); 158 return get_pte(pmd, va); 159 } 160 161 void *split_page(pgd_t *pgtable, void *vaddr, enum pgt_level level) 162 { 163 assert((level >= 3) && (level <= 5)); 164 return get_dat_entry(pgtable ? pgtable : table_root, vaddr, level); 165 } 166 167 phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *vaddr) 168 { 169 uintptr_t va = (uintptr_t)vaddr; 170 pud_t *pud; 171 pmd_t *pmd; 172 pte_t *pte; 173 174 pud = get_pud(pgtable, va); 175 if (pud_huge(*pud)) 176 return (pud_val(*pud) & REGION3_ENTRY_RFAA) | (va & ~REGION3_ENTRY_RFAA); 177 pmd = get_pmd(pud, va); 178 if (pmd_large(*pmd)) 179 return (pmd_val(*pmd) & SEGMENT_ENTRY_SFAA) | (va & ~SEGMENT_ENTRY_SFAA); 180 pte = get_pte(pmd, va); 181 return (pte_val(*pte) & PAGE_MASK) | (va & ~PAGE_MASK); 182 } 183 184 /* 185 * Get the DAT table entry of the given level for the given address, 186 * splitting if necessary. If the entry was not invalid, invalidate it, and 187 * return the pointer to the entry and, if requested, its old value. 188 * @pgtable root of the page tables 189 * @vaddr virtual address 190 * @level 3 (for 2GB pud), 4 (for 1MB pmd) or 5 (for 4kB pages) 191 * @old if not NULL, will be written with the old value of the DAT table 192 * entry before invalidation 193 */ 194 static void *dat_get_and_invalidate(pgd_t *pgtable, void *vaddr, enum pgt_level level, unsigned long *old) 195 { 196 unsigned long va = (unsigned long)vaddr; 197 void *ptr; 198 199 ptr = get_dat_entry(pgtable, vaddr, level); 200 if (old) 201 *old = *(unsigned long *)ptr; 202 if ((level == pgtable_level_pgd) && !pgd_none(*(pgd_t *)ptr)) 203 idte_pgdp(va, ptr); 204 else if ((level == pgtable_level_p4d) && !p4d_none(*(p4d_t *)ptr)) 205 idte_p4dp(va, ptr); 206 else if ((level == pgtable_level_pud) && !pud_none(*(pud_t *)ptr)) 207 idte_pudp(va, ptr); 208 else if ((level == pgtable_level_pmd) && !pmd_none(*(pmd_t *)ptr)) 209 idte_pmdp(va, ptr); 210 else if (!pte_none(*(pte_t *)ptr)) 211 ipte(va, ptr); 212 return ptr; 213 } 214 215 static void cleanup_pmd(pmd_t *pmd) 216 { 217 /* was invalid or large, nothing to do */ 218 if (pmd_none(*pmd) || pmd_large(*pmd)) 219 return; 220 /* was not large, free the corresponding page table */ 221 free_pages((void *)(pmd_val(*pmd) & PAGE_MASK)); 222 } 223 224 static void cleanup_pud(pud_t *pud) 225 { 226 unsigned long i; 227 pmd_t *pmd; 228 229 /* was invalid or large, nothing to do */ 230 if (pud_none(*pud) || pud_huge(*pud)) 231 return; 232 /* recursively clean up all pmds if needed */ 233 pmd = (pmd_t *)(pud_val(*pud) & PAGE_MASK); 234 for (i = 0; i < SEGMENT_TABLE_ENTRIES; i++) 235 cleanup_pmd(pmd + i); 236 /* free the corresponding segment table */ 237 free_pages(pmd); 238 } 239 240 /* 241 * Set the DAT entry for the given level of the given virtual address. If a 242 * mapping already existed, it is overwritten. If an existing mapping with 243 * smaller pages existed, all the lower tables are freed. 244 * Returns the pointer to the DAT table entry. 245 * @pgtable root of the page tables 246 * @val the new value for the DAT table entry 247 * @vaddr the virtual address 248 * @level 3 for pud (region 3), 4 for pmd (segment) and 5 for pte (pages) 249 */ 250 static void *set_dat_entry(pgd_t *pgtable, unsigned long val, void *vaddr, enum pgt_level level) 251 { 252 unsigned long old, *res; 253 254 res = dat_get_and_invalidate(pgtable, vaddr, level, &old); 255 if (level == pgtable_level_pmd) 256 cleanup_pmd((pmd_t *)&old); 257 if (level == pgtable_level_pud) 258 cleanup_pud((pud_t *)&old); 259 *res = val; 260 return res; 261 } 262 263 pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr) 264 { 265 assert(IS_ALIGNED(phys, PAGE_SIZE)); 266 assert(IS_ALIGNED((uintptr_t)vaddr, PAGE_SIZE)); 267 return set_dat_entry(pgtable, phys, vaddr, pgtable_level_pte); 268 } 269 270 pmdval_t *install_large_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr) 271 { 272 assert(IS_ALIGNED(phys, SZ_1M)); 273 assert(IS_ALIGNED((uintptr_t)vaddr, SZ_1M)); 274 return set_dat_entry(pgtable, phys | SEGMENT_ENTRY_FC, vaddr, pgtable_level_pmd); 275 } 276 277 pudval_t *install_huge_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr) 278 { 279 assert(IS_ALIGNED(phys, SZ_2G)); 280 assert(IS_ALIGNED((uintptr_t)vaddr, SZ_2G)); 281 return set_dat_entry(pgtable, phys | REGION3_ENTRY_FC | REGION_ENTRY_TT_REGION3, vaddr, pgtable_level_pud); 282 } 283 284 void protect_dat_entry(void *vaddr, unsigned long prot, enum pgt_level level) 285 { 286 unsigned long old, *ptr; 287 288 ptr = dat_get_and_invalidate(table_root, vaddr, level, &old); 289 *ptr = old | prot; 290 } 291 292 void unprotect_dat_entry(void *vaddr, unsigned long prot, enum pgt_level level) 293 { 294 unsigned long old, *ptr; 295 296 ptr = dat_get_and_invalidate(table_root, vaddr, level, &old); 297 *ptr = old & ~prot; 298 } 299 300 void protect_range(void *start, unsigned long len, unsigned long prot) 301 { 302 uintptr_t curr = (uintptr_t)start & PAGE_MASK; 303 304 len &= PAGE_MASK; 305 for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE) 306 protect_dat_entry((void *)curr, prot, 5); 307 } 308 309 void unprotect_range(void *start, unsigned long len, unsigned long prot) 310 { 311 uintptr_t curr = (uintptr_t)start & PAGE_MASK; 312 313 len &= PAGE_MASK; 314 for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE) 315 unprotect_dat_entry((void *)curr, prot, 5); 316 } 317 318 static void setup_identity(pgd_t *pgtable, phys_addr_t start_addr, 319 phys_addr_t end_addr) 320 { 321 phys_addr_t cur; 322 323 start_addr &= PAGE_MASK; 324 for (cur = start_addr; true; cur += PAGE_SIZE) { 325 if (start_addr < end_addr && cur >= end_addr) 326 break; 327 if (start_addr > end_addr && cur <= end_addr) 328 break; 329 install_page(pgtable, cur, __va(cur)); 330 } 331 } 332 333 void *setup_mmu(phys_addr_t phys_end, void *unused) 334 { 335 pgd_t *page_root; 336 337 /* allocate a region-1 table */ 338 page_root = pgd_alloc_one(); 339 340 /* map all physical memory 1:1 */ 341 setup_identity(page_root, 0, phys_end); 342 343 /* generate 128MB of invalid adresses at the end (for testing PGM) */ 344 init_alloc_vpage((void *) -(1UL << 27)); 345 setup_identity(page_root, -(1UL << 27), 0); 346 347 /* finally enable DAT with the new table */ 348 mmu_enable(page_root); 349 table_root = page_root; 350 return page_root; 351 } 352