18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Helper routines for building identity mapping page tables. This is 48c2ecf20Sopenharmony_ci * included by both the compressed kernel and the regular kernel. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_cistatic void ident_pmd_init(struct x86_mapping_info *info, pmd_t *pmd_page, 88c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end) 98c2ecf20Sopenharmony_ci{ 108c2ecf20Sopenharmony_ci addr &= PMD_MASK; 118c2ecf20Sopenharmony_ci for (; addr < end; addr += PMD_SIZE) { 128c2ecf20Sopenharmony_ci pmd_t *pmd = pmd_page + pmd_index(addr); 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci if (pmd_present(*pmd)) 158c2ecf20Sopenharmony_ci continue; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci set_pmd(pmd, __pmd((addr - info->offset) | info->page_flag)); 188c2ecf20Sopenharmony_ci } 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int ident_pud_init(struct x86_mapping_info *info, pud_t *pud_page, 228c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci unsigned long next; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci for (; addr < end; addr = next) { 278c2ecf20Sopenharmony_ci pud_t *pud = pud_page + pud_index(addr); 288c2ecf20Sopenharmony_ci pmd_t *pmd; 298c2ecf20Sopenharmony_ci bool use_gbpage; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci next = (addr & PUD_MASK) + PUD_SIZE; 328c2ecf20Sopenharmony_ci if (next > end) 338c2ecf20Sopenharmony_ci next = end; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* if this is already a gbpage, this portion is already mapped */ 368c2ecf20Sopenharmony_ci if (pud_large(*pud)) 378c2ecf20Sopenharmony_ci continue; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* Is using a gbpage allowed? */ 408c2ecf20Sopenharmony_ci use_gbpage = info->direct_gbpages; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Don't use gbpage if it maps more than the requested region. */ 438c2ecf20Sopenharmony_ci /* at the begining: */ 448c2ecf20Sopenharmony_ci use_gbpage &= ((addr & ~PUD_MASK) == 0); 458c2ecf20Sopenharmony_ci /* ... or at the end: */ 468c2ecf20Sopenharmony_ci use_gbpage &= ((next & ~PUD_MASK) == 0); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Never overwrite existing mappings */ 498c2ecf20Sopenharmony_ci use_gbpage &= !pud_present(*pud); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (use_gbpage) { 528c2ecf20Sopenharmony_ci pud_t pudval; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci pudval = __pud((addr - info->offset) | info->page_flag); 558c2ecf20Sopenharmony_ci set_pud(pud, pudval); 568c2ecf20Sopenharmony_ci continue; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (pud_present(*pud)) { 608c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, 0); 618c2ecf20Sopenharmony_ci ident_pmd_init(info, pmd, addr, next); 628c2ecf20Sopenharmony_ci continue; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci pmd = (pmd_t *)info->alloc_pgt_page(info->context); 658c2ecf20Sopenharmony_ci if (!pmd) 668c2ecf20Sopenharmony_ci return -ENOMEM; 678c2ecf20Sopenharmony_ci ident_pmd_init(info, pmd, addr, next); 688c2ecf20Sopenharmony_ci set_pud(pud, __pud(__pa(pmd) | info->kernpg_flag)); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int ident_p4d_init(struct x86_mapping_info *info, p4d_t *p4d_page, 758c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci unsigned long next; 788c2ecf20Sopenharmony_ci int result; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci for (; addr < end; addr = next) { 818c2ecf20Sopenharmony_ci p4d_t *p4d = p4d_page + p4d_index(addr); 828c2ecf20Sopenharmony_ci pud_t *pud; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci next = (addr & P4D_MASK) + P4D_SIZE; 858c2ecf20Sopenharmony_ci if (next > end) 868c2ecf20Sopenharmony_ci next = end; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (p4d_present(*p4d)) { 898c2ecf20Sopenharmony_ci pud = pud_offset(p4d, 0); 908c2ecf20Sopenharmony_ci result = ident_pud_init(info, pud, addr, next); 918c2ecf20Sopenharmony_ci if (result) 928c2ecf20Sopenharmony_ci return result; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci continue; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci pud = (pud_t *)info->alloc_pgt_page(info->context); 978c2ecf20Sopenharmony_ci if (!pud) 988c2ecf20Sopenharmony_ci return -ENOMEM; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci result = ident_pud_init(info, pud, addr, next); 1018c2ecf20Sopenharmony_ci if (result) 1028c2ecf20Sopenharmony_ci return result; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci set_p4d(p4d, __p4d(__pa(pud) | info->kernpg_flag)); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciint kernel_ident_mapping_init(struct x86_mapping_info *info, pgd_t *pgd_page, 1118c2ecf20Sopenharmony_ci unsigned long pstart, unsigned long pend) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci unsigned long addr = pstart + info->offset; 1148c2ecf20Sopenharmony_ci unsigned long end = pend + info->offset; 1158c2ecf20Sopenharmony_ci unsigned long next; 1168c2ecf20Sopenharmony_ci int result; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Set the default pagetable flags if not supplied */ 1198c2ecf20Sopenharmony_ci if (!info->kernpg_flag) 1208c2ecf20Sopenharmony_ci info->kernpg_flag = _KERNPG_TABLE; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Filter out unsupported __PAGE_KERNEL_* bits: */ 1238c2ecf20Sopenharmony_ci info->kernpg_flag &= __default_kernel_pte_mask; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for (; addr < end; addr = next) { 1268c2ecf20Sopenharmony_ci pgd_t *pgd = pgd_page + pgd_index(addr); 1278c2ecf20Sopenharmony_ci p4d_t *p4d; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci next = (addr & PGDIR_MASK) + PGDIR_SIZE; 1308c2ecf20Sopenharmony_ci if (next > end) 1318c2ecf20Sopenharmony_ci next = end; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (pgd_present(*pgd)) { 1348c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, 0); 1358c2ecf20Sopenharmony_ci result = ident_p4d_init(info, p4d, addr, next); 1368c2ecf20Sopenharmony_ci if (result) 1378c2ecf20Sopenharmony_ci return result; 1388c2ecf20Sopenharmony_ci continue; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci p4d = (p4d_t *)info->alloc_pgt_page(info->context); 1428c2ecf20Sopenharmony_ci if (!p4d) 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci result = ident_p4d_init(info, p4d, addr, next); 1458c2ecf20Sopenharmony_ci if (result) 1468c2ecf20Sopenharmony_ci return result; 1478c2ecf20Sopenharmony_ci if (pgtable_l5_enabled()) { 1488c2ecf20Sopenharmony_ci set_pgd(pgd, __pgd(__pa(p4d) | info->kernpg_flag)); 1498c2ecf20Sopenharmony_ci } else { 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * With p4d folded, pgd is equal to p4d. 1528c2ecf20Sopenharmony_ci * The pgd entry has to point to the pud page table in this case. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci pud_t *pud = pud_offset(p4d, 0); 1558c2ecf20Sopenharmony_ci set_pgd(pgd, __pgd(__pa(pud) | info->kernpg_flag)); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 161