18ad8b727SNick Hu // SPDX-License-Identifier: GPL-2.0 28ad8b727SNick Hu // Copyright (C) 2019 Andes Technology Corporation 38ad8b727SNick Hu 48ad8b727SNick Hu #include <linux/pfn.h> 58ad8b727SNick Hu #include <linux/init_task.h> 68ad8b727SNick Hu #include <linux/kasan.h> 78ad8b727SNick Hu #include <linux/kernel.h> 88ad8b727SNick Hu #include <linux/memblock.h> 9ca5999fdSMike Rapoport #include <linux/pgtable.h> 1065fddcfcSMike Rapoport #include <asm/tlbflush.h> 118ad8b727SNick Hu #include <asm/fixmap.h> 12e178d670SNylon Chen #include <asm/pgalloc.h> 13e178d670SNylon Chen 14e178d670SNylon Chen static __init void *early_alloc(size_t size, int node) 15e178d670SNylon Chen { 16e178d670SNylon Chen void *ptr = memblock_alloc_try_nid(size, size, 17e178d670SNylon Chen __pa(MAX_DMA_ADDRESS), MEMBLOCK_ALLOC_ACCESSIBLE, node); 18e178d670SNylon Chen 19e178d670SNylon Chen if (!ptr) 20e178d670SNylon Chen panic("%pS: Failed to allocate %zu bytes align=%zx nid=%d from=%llx\n", 21e178d670SNylon Chen __func__, size, size, node, (u64)__pa(MAX_DMA_ADDRESS)); 22e178d670SNylon Chen 23e178d670SNylon Chen return ptr; 24e178d670SNylon Chen } 258ad8b727SNick Hu 268ad8b727SNick Hu extern pgd_t early_pg_dir[PTRS_PER_PGD]; 278ad8b727SNick Hu asmlinkage void __init kasan_early_init(void) 288ad8b727SNick Hu { 298ad8b727SNick Hu uintptr_t i; 308ad8b727SNick Hu pgd_t *pgd = early_pg_dir + pgd_index(KASAN_SHADOW_START); 318ad8b727SNick Hu 328ad8b727SNick Hu for (i = 0; i < PTRS_PER_PTE; ++i) 338ad8b727SNick Hu set_pte(kasan_early_shadow_pte + i, 348ad8b727SNick Hu mk_pte(virt_to_page(kasan_early_shadow_page), 358ad8b727SNick Hu PAGE_KERNEL)); 368ad8b727SNick Hu 378ad8b727SNick Hu for (i = 0; i < PTRS_PER_PMD; ++i) 388ad8b727SNick Hu set_pmd(kasan_early_shadow_pmd + i, 398458ca14SZong Li pfn_pmd(PFN_DOWN 408458ca14SZong Li (__pa((uintptr_t) kasan_early_shadow_pte)), 418ad8b727SNick Hu __pgprot(_PAGE_TABLE))); 428ad8b727SNick Hu 438ad8b727SNick Hu for (i = KASAN_SHADOW_START; i < KASAN_SHADOW_END; 448ad8b727SNick Hu i += PGDIR_SIZE, ++pgd) 458ad8b727SNick Hu set_pgd(pgd, 468458ca14SZong Li pfn_pgd(PFN_DOWN 478458ca14SZong Li (__pa(((uintptr_t) kasan_early_shadow_pmd))), 488ad8b727SNick Hu __pgprot(_PAGE_TABLE))); 498ad8b727SNick Hu 508ad8b727SNick Hu /* init for swapper_pg_dir */ 518ad8b727SNick Hu pgd = pgd_offset_k(KASAN_SHADOW_START); 528ad8b727SNick Hu 538ad8b727SNick Hu for (i = KASAN_SHADOW_START; i < KASAN_SHADOW_END; 548ad8b727SNick Hu i += PGDIR_SIZE, ++pgd) 558ad8b727SNick Hu set_pgd(pgd, 568458ca14SZong Li pfn_pgd(PFN_DOWN 578458ca14SZong Li (__pa(((uintptr_t) kasan_early_shadow_pmd))), 588ad8b727SNick Hu __pgprot(_PAGE_TABLE))); 598ad8b727SNick Hu 604cb699d0SVincent Chen local_flush_tlb_all(); 618ad8b727SNick Hu } 628ad8b727SNick Hu 63d127c19cSAlexandre Ghiti static void kasan_populate_pte(pmd_t *pmd, unsigned long vaddr, unsigned long end) 648ad8b727SNick Hu { 65d127c19cSAlexandre Ghiti phys_addr_t phys_addr; 66d127c19cSAlexandre Ghiti pte_t *ptep, *base_pte; 67a0a31fd8SZong Li 68d127c19cSAlexandre Ghiti if (pmd_none(*pmd)) 69d127c19cSAlexandre Ghiti base_pte = memblock_alloc(PTRS_PER_PTE * sizeof(pte_t), PAGE_SIZE); 70d127c19cSAlexandre Ghiti else 71d127c19cSAlexandre Ghiti base_pte = (pte_t *)pmd_page_vaddr(*pmd); 728ad8b727SNick Hu 73d127c19cSAlexandre Ghiti ptep = base_pte + pte_index(vaddr); 74d127c19cSAlexandre Ghiti 75d127c19cSAlexandre Ghiti do { 76d127c19cSAlexandre Ghiti if (pte_none(*ptep)) { 77d127c19cSAlexandre Ghiti phys_addr = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE); 78d127c19cSAlexandre Ghiti set_pte(ptep, pfn_pte(PFN_DOWN(phys_addr), PAGE_KERNEL)); 79d127c19cSAlexandre Ghiti } 80d127c19cSAlexandre Ghiti } while (ptep++, vaddr += PAGE_SIZE, vaddr != end); 81d127c19cSAlexandre Ghiti 82d127c19cSAlexandre Ghiti set_pmd(pmd, pfn_pmd(PFN_DOWN(__pa(base_pte)), PAGE_TABLE)); 838ad8b727SNick Hu } 848ad8b727SNick Hu 85d127c19cSAlexandre Ghiti static void kasan_populate_pmd(pgd_t *pgd, unsigned long vaddr, unsigned long end) 86d127c19cSAlexandre Ghiti { 87d127c19cSAlexandre Ghiti phys_addr_t phys_addr; 88d127c19cSAlexandre Ghiti pmd_t *pmdp, *base_pmd; 89d127c19cSAlexandre Ghiti unsigned long next; 908ad8b727SNick Hu 91d127c19cSAlexandre Ghiti base_pmd = (pmd_t *)pgd_page_vaddr(*pgd); 92d127c19cSAlexandre Ghiti if (base_pmd == lm_alias(kasan_early_shadow_pmd)) 93d127c19cSAlexandre Ghiti base_pmd = memblock_alloc(PTRS_PER_PMD * sizeof(pmd_t), PAGE_SIZE); 94d127c19cSAlexandre Ghiti 95d127c19cSAlexandre Ghiti pmdp = base_pmd + pmd_index(vaddr); 96d127c19cSAlexandre Ghiti 97d127c19cSAlexandre Ghiti do { 98d127c19cSAlexandre Ghiti next = pmd_addr_end(vaddr, end); 99d7fbcf40SAlexandre Ghiti 100d7fbcf40SAlexandre Ghiti if (pmd_none(*pmdp) && IS_ALIGNED(vaddr, PMD_SIZE) && (next - vaddr) >= PMD_SIZE) { 101d7fbcf40SAlexandre Ghiti phys_addr = memblock_phys_alloc(PMD_SIZE, PMD_SIZE); 102d7fbcf40SAlexandre Ghiti if (phys_addr) { 103d7fbcf40SAlexandre Ghiti set_pmd(pmdp, pfn_pmd(PFN_DOWN(phys_addr), PAGE_KERNEL)); 104d7fbcf40SAlexandre Ghiti continue; 105d7fbcf40SAlexandre Ghiti } 106d7fbcf40SAlexandre Ghiti } 107d7fbcf40SAlexandre Ghiti 108d127c19cSAlexandre Ghiti kasan_populate_pte(pmdp, vaddr, next); 109d127c19cSAlexandre Ghiti } while (pmdp++, vaddr = next, vaddr != end); 110d127c19cSAlexandre Ghiti 111d127c19cSAlexandre Ghiti /* 112d127c19cSAlexandre Ghiti * Wait for the whole PGD to be populated before setting the PGD in 113d127c19cSAlexandre Ghiti * the page table, otherwise, if we did set the PGD before populating 114d127c19cSAlexandre Ghiti * it entirely, memblock could allocate a page at a physical address 115d127c19cSAlexandre Ghiti * where KASAN is not populated yet and then we'd get a page fault. 116d127c19cSAlexandre Ghiti */ 117d127c19cSAlexandre Ghiti set_pgd(pgd, pfn_pgd(PFN_DOWN(__pa(base_pmd)), PAGE_TABLE)); 118d127c19cSAlexandre Ghiti } 119d127c19cSAlexandre Ghiti 120d127c19cSAlexandre Ghiti static void kasan_populate_pgd(unsigned long vaddr, unsigned long end) 121d127c19cSAlexandre Ghiti { 122d127c19cSAlexandre Ghiti phys_addr_t phys_addr; 123d127c19cSAlexandre Ghiti pgd_t *pgdp = pgd_offset_k(vaddr); 124d127c19cSAlexandre Ghiti unsigned long next; 125d127c19cSAlexandre Ghiti 126d127c19cSAlexandre Ghiti do { 127d127c19cSAlexandre Ghiti next = pgd_addr_end(vaddr, end); 128d7fbcf40SAlexandre Ghiti 129d7fbcf40SAlexandre Ghiti /* 130d7fbcf40SAlexandre Ghiti * pgdp can't be none since kasan_early_init initialized all KASAN 131d7fbcf40SAlexandre Ghiti * shadow region with kasan_early_shadow_pmd: if this is stillthe case, 132d7fbcf40SAlexandre Ghiti * that means we can try to allocate a hugepage as a replacement. 133d7fbcf40SAlexandre Ghiti */ 134d7fbcf40SAlexandre Ghiti if (pgd_page_vaddr(*pgdp) == (unsigned long)lm_alias(kasan_early_shadow_pmd) && 135d7fbcf40SAlexandre Ghiti IS_ALIGNED(vaddr, PGDIR_SIZE) && (next - vaddr) >= PGDIR_SIZE) { 136d7fbcf40SAlexandre Ghiti phys_addr = memblock_phys_alloc(PGDIR_SIZE, PGDIR_SIZE); 137d7fbcf40SAlexandre Ghiti if (phys_addr) { 138d7fbcf40SAlexandre Ghiti set_pgd(pgdp, pfn_pgd(PFN_DOWN(phys_addr), PAGE_KERNEL)); 139d7fbcf40SAlexandre Ghiti continue; 140d7fbcf40SAlexandre Ghiti } 141d7fbcf40SAlexandre Ghiti } 142d7fbcf40SAlexandre Ghiti 143d127c19cSAlexandre Ghiti kasan_populate_pmd(pgdp, vaddr, next); 144d127c19cSAlexandre Ghiti } while (pgdp++, vaddr = next, vaddr != end); 145d127c19cSAlexandre Ghiti } 146d127c19cSAlexandre Ghiti 147d127c19cSAlexandre Ghiti static void __init kasan_populate(void *start, void *end) 148d127c19cSAlexandre Ghiti { 149d127c19cSAlexandre Ghiti unsigned long vaddr = (unsigned long)start & PAGE_MASK; 150d127c19cSAlexandre Ghiti unsigned long vend = PAGE_ALIGN((unsigned long)end); 151d127c19cSAlexandre Ghiti 152d127c19cSAlexandre Ghiti kasan_populate_pgd(vaddr, vend); 1538ad8b727SNick Hu 1544cb699d0SVincent Chen local_flush_tlb_all(); 1559484e2aeSAlexandre Ghiti memset(start, KASAN_SHADOW_INIT, end - start); 1568ad8b727SNick Hu } 1578ad8b727SNick Hu 15878947bdfSPalmer Dabbelt static void __init kasan_shallow_populate(void *start, void *end) 159e178d670SNylon Chen { 160e178d670SNylon Chen unsigned long vaddr = (unsigned long)start & PAGE_MASK; 161e178d670SNylon Chen unsigned long vend = PAGE_ALIGN((unsigned long)end); 162e178d670SNylon Chen unsigned long pfn; 163e178d670SNylon Chen int index; 164e178d670SNylon Chen void *p; 165e178d670SNylon Chen pud_t *pud_dir, *pud_k; 166e178d670SNylon Chen pgd_t *pgd_dir, *pgd_k; 167e178d670SNylon Chen p4d_t *p4d_dir, *p4d_k; 168e178d670SNylon Chen 169e178d670SNylon Chen while (vaddr < vend) { 170e178d670SNylon Chen index = pgd_index(vaddr); 171e178d670SNylon Chen pfn = csr_read(CSR_SATP) & SATP_PPN; 172e178d670SNylon Chen pgd_dir = (pgd_t *)pfn_to_virt(pfn) + index; 173e178d670SNylon Chen pgd_k = init_mm.pgd + index; 174e178d670SNylon Chen pgd_dir = pgd_offset_k(vaddr); 175e178d670SNylon Chen set_pgd(pgd_dir, *pgd_k); 176e178d670SNylon Chen 177e178d670SNylon Chen p4d_dir = p4d_offset(pgd_dir, vaddr); 178e178d670SNylon Chen p4d_k = p4d_offset(pgd_k, vaddr); 179e178d670SNylon Chen 180e178d670SNylon Chen vaddr = (vaddr + PUD_SIZE) & PUD_MASK; 181e178d670SNylon Chen pud_dir = pud_offset(p4d_dir, vaddr); 182e178d670SNylon Chen pud_k = pud_offset(p4d_k, vaddr); 183e178d670SNylon Chen 184e178d670SNylon Chen if (pud_present(*pud_dir)) { 185e178d670SNylon Chen p = early_alloc(PAGE_SIZE, NUMA_NO_NODE); 186e178d670SNylon Chen pud_populate(&init_mm, pud_dir, p); 187e178d670SNylon Chen } 188e178d670SNylon Chen vaddr += PAGE_SIZE; 189e178d670SNylon Chen } 190f3773dd0SAlexandre Ghiti 191f3773dd0SAlexandre Ghiti local_flush_tlb_all(); 1928ad8b727SNick Hu } 1938ad8b727SNick Hu 1948ad8b727SNick Hu void __init kasan_init(void) 1958ad8b727SNick Hu { 196b10d6bcaSMike Rapoport phys_addr_t _start, _end; 197b10d6bcaSMike Rapoport u64 i; 1988ad8b727SNick Hu 1998ad8b727SNick Hu kasan_populate_early_shadow((void *)KASAN_SHADOW_START, 2008458ca14SZong Li (void *)kasan_mem_to_shadow((void *) 201e178d670SNylon Chen VMEMMAP_END)); 202e178d670SNylon Chen if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) 203e178d670SNylon Chen kasan_shallow_populate( 204e178d670SNylon Chen (void *)kasan_mem_to_shadow((void *)VMALLOC_START), 205e178d670SNylon Chen (void *)kasan_mem_to_shadow((void *)VMALLOC_END)); 206e178d670SNylon Chen else 207e178d670SNylon Chen kasan_populate_early_shadow( 208e178d670SNylon Chen (void *)kasan_mem_to_shadow((void *)VMALLOC_START), 209e178d670SNylon Chen (void *)kasan_mem_to_shadow((void *)VMALLOC_END)); 2108ad8b727SNick Hu 211b10d6bcaSMike Rapoport for_each_mem_range(i, &_start, &_end) { 212c25a053eSNick Hu void *start = (void *)__va(_start); 213c25a053eSNick Hu void *end = (void *)__va(_end); 2148ad8b727SNick Hu 2158ad8b727SNick Hu if (start >= end) 2168ad8b727SNick Hu break; 2178ad8b727SNick Hu 218d127c19cSAlexandre Ghiti kasan_populate(kasan_mem_to_shadow(start), kasan_mem_to_shadow(end)); 219*9d8c7d92SYang Li } 2208ad8b727SNick Hu 2218ad8b727SNick Hu for (i = 0; i < PTRS_PER_PTE; i++) 2228ad8b727SNick Hu set_pte(&kasan_early_shadow_pte[i], 2238ad8b727SNick Hu mk_pte(virt_to_page(kasan_early_shadow_page), 2248458ca14SZong Li __pgprot(_PAGE_PRESENT | _PAGE_READ | 2258458ca14SZong Li _PAGE_ACCESSED))); 2268ad8b727SNick Hu 2279484e2aeSAlexandre Ghiti memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE); 2288ad8b727SNick Hu init_task.kasan_depth = 0; 2298ad8b727SNick Hu } 230