162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains kasan initialization code for ARM64. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) "kasan: " fmt 1062306a36Sopenharmony_ci#include <linux/kasan.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/sched/task.h> 1362306a36Sopenharmony_ci#include <linux/memblock.h> 1462306a36Sopenharmony_ci#include <linux/start_kernel.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/mmu_context.h> 1862306a36Sopenharmony_ci#include <asm/kernel-pgtable.h> 1962306a36Sopenharmony_ci#include <asm/page.h> 2062306a36Sopenharmony_ci#include <asm/pgalloc.h> 2162306a36Sopenharmony_ci#include <asm/sections.h> 2262306a36Sopenharmony_ci#include <asm/tlbflush.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic pgd_t tmp_pg_dir[PTRS_PER_PGD] __initdata __aligned(PGD_SIZE); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * The p*d_populate functions call virt_to_phys implicitly so they can't be used 3062306a36Sopenharmony_ci * directly on kernel symbols (bm_p*d). All the early functions are called too 3162306a36Sopenharmony_ci * early to use lm_alias so __p*d_populate functions must be used to populate 3262306a36Sopenharmony_ci * with the physical address from __pa_symbol. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic phys_addr_t __init kasan_alloc_zeroed_page(int node) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci void *p = memblock_alloc_try_nid(PAGE_SIZE, PAGE_SIZE, 3862306a36Sopenharmony_ci __pa(MAX_DMA_ADDRESS), 3962306a36Sopenharmony_ci MEMBLOCK_ALLOC_NOLEAKTRACE, node); 4062306a36Sopenharmony_ci if (!p) 4162306a36Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%llx\n", 4262306a36Sopenharmony_ci __func__, PAGE_SIZE, PAGE_SIZE, node, 4362306a36Sopenharmony_ci __pa(MAX_DMA_ADDRESS)); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return __pa(p); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic phys_addr_t __init kasan_alloc_raw_page(int node) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci void *p = memblock_alloc_try_nid_raw(PAGE_SIZE, PAGE_SIZE, 5162306a36Sopenharmony_ci __pa(MAX_DMA_ADDRESS), 5262306a36Sopenharmony_ci MEMBLOCK_ALLOC_NOLEAKTRACE, 5362306a36Sopenharmony_ci node); 5462306a36Sopenharmony_ci if (!p) 5562306a36Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%llx\n", 5662306a36Sopenharmony_ci __func__, PAGE_SIZE, PAGE_SIZE, node, 5762306a36Sopenharmony_ci __pa(MAX_DMA_ADDRESS)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return __pa(p); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr, int node, 6362306a36Sopenharmony_ci bool early) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci if (pmd_none(READ_ONCE(*pmdp))) { 6662306a36Sopenharmony_ci phys_addr_t pte_phys = early ? 6762306a36Sopenharmony_ci __pa_symbol(kasan_early_shadow_pte) 6862306a36Sopenharmony_ci : kasan_alloc_zeroed_page(node); 6962306a36Sopenharmony_ci __pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return early ? pte_offset_kimg(pmdp, addr) 7362306a36Sopenharmony_ci : pte_offset_kernel(pmdp, addr); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr, int node, 7762306a36Sopenharmony_ci bool early) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci if (pud_none(READ_ONCE(*pudp))) { 8062306a36Sopenharmony_ci phys_addr_t pmd_phys = early ? 8162306a36Sopenharmony_ci __pa_symbol(kasan_early_shadow_pmd) 8262306a36Sopenharmony_ci : kasan_alloc_zeroed_page(node); 8362306a36Sopenharmony_ci __pud_populate(pudp, pmd_phys, PUD_TYPE_TABLE); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return early ? pmd_offset_kimg(pudp, addr) : pmd_offset(pudp, addr); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr, int node, 9062306a36Sopenharmony_ci bool early) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci if (p4d_none(READ_ONCE(*p4dp))) { 9362306a36Sopenharmony_ci phys_addr_t pud_phys = early ? 9462306a36Sopenharmony_ci __pa_symbol(kasan_early_shadow_pud) 9562306a36Sopenharmony_ci : kasan_alloc_zeroed_page(node); 9662306a36Sopenharmony_ci __p4d_populate(p4dp, pud_phys, P4D_TYPE_TABLE); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return early ? pud_offset_kimg(p4dp, addr) : pud_offset(p4dp, addr); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void __init kasan_pte_populate(pmd_t *pmdp, unsigned long addr, 10362306a36Sopenharmony_ci unsigned long end, int node, bool early) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci unsigned long next; 10662306a36Sopenharmony_ci pte_t *ptep = kasan_pte_offset(pmdp, addr, node, early); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci do { 10962306a36Sopenharmony_ci phys_addr_t page_phys = early ? 11062306a36Sopenharmony_ci __pa_symbol(kasan_early_shadow_page) 11162306a36Sopenharmony_ci : kasan_alloc_raw_page(node); 11262306a36Sopenharmony_ci if (!early) 11362306a36Sopenharmony_ci memset(__va(page_phys), KASAN_SHADOW_INIT, PAGE_SIZE); 11462306a36Sopenharmony_ci next = addr + PAGE_SIZE; 11562306a36Sopenharmony_ci set_pte(ptep, pfn_pte(__phys_to_pfn(page_phys), PAGE_KERNEL)); 11662306a36Sopenharmony_ci } while (ptep++, addr = next, addr != end && pte_none(READ_ONCE(*ptep))); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void __init kasan_pmd_populate(pud_t *pudp, unsigned long addr, 12062306a36Sopenharmony_ci unsigned long end, int node, bool early) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci unsigned long next; 12362306a36Sopenharmony_ci pmd_t *pmdp = kasan_pmd_offset(pudp, addr, node, early); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci do { 12662306a36Sopenharmony_ci next = pmd_addr_end(addr, end); 12762306a36Sopenharmony_ci kasan_pte_populate(pmdp, addr, next, node, early); 12862306a36Sopenharmony_ci } while (pmdp++, addr = next, addr != end && pmd_none(READ_ONCE(*pmdp))); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void __init kasan_pud_populate(p4d_t *p4dp, unsigned long addr, 13262306a36Sopenharmony_ci unsigned long end, int node, bool early) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned long next; 13562306a36Sopenharmony_ci pud_t *pudp = kasan_pud_offset(p4dp, addr, node, early); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci do { 13862306a36Sopenharmony_ci next = pud_addr_end(addr, end); 13962306a36Sopenharmony_ci kasan_pmd_populate(pudp, addr, next, node, early); 14062306a36Sopenharmony_ci } while (pudp++, addr = next, addr != end && pud_none(READ_ONCE(*pudp))); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void __init kasan_p4d_populate(pgd_t *pgdp, unsigned long addr, 14462306a36Sopenharmony_ci unsigned long end, int node, bool early) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned long next; 14762306a36Sopenharmony_ci p4d_t *p4dp = p4d_offset(pgdp, addr); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci do { 15062306a36Sopenharmony_ci next = p4d_addr_end(addr, end); 15162306a36Sopenharmony_ci kasan_pud_populate(p4dp, addr, next, node, early); 15262306a36Sopenharmony_ci } while (p4dp++, addr = next, addr != end); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void __init kasan_pgd_populate(unsigned long addr, unsigned long end, 15662306a36Sopenharmony_ci int node, bool early) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci unsigned long next; 15962306a36Sopenharmony_ci pgd_t *pgdp; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci pgdp = pgd_offset_k(addr); 16262306a36Sopenharmony_ci do { 16362306a36Sopenharmony_ci next = pgd_addr_end(addr, end); 16462306a36Sopenharmony_ci kasan_p4d_populate(pgdp, addr, next, node, early); 16562306a36Sopenharmony_ci } while (pgdp++, addr = next, addr != end); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* The early shadow maps everything to a single page of zeroes */ 16962306a36Sopenharmony_ciasmlinkage void __init kasan_early_init(void) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci BUILD_BUG_ON(KASAN_SHADOW_OFFSET != 17262306a36Sopenharmony_ci KASAN_SHADOW_END - (1UL << (64 - KASAN_SHADOW_SCALE_SHIFT))); 17362306a36Sopenharmony_ci BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS), PGDIR_SIZE)); 17462306a36Sopenharmony_ci BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS_MIN), PGDIR_SIZE)); 17562306a36Sopenharmony_ci BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, PGDIR_SIZE)); 17662306a36Sopenharmony_ci kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE, 17762306a36Sopenharmony_ci true); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* Set up full kasan mappings, ensuring that the mapped pages are zeroed */ 18162306a36Sopenharmony_cistatic void __init kasan_map_populate(unsigned long start, unsigned long end, 18262306a36Sopenharmony_ci int node) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci kasan_pgd_populate(start & PAGE_MASK, PAGE_ALIGN(end), node, false); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * Copy the current shadow region into a new pgdir. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_civoid __init kasan_copy_shadow(pgd_t *pgdir) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci pgd_t *pgdp, *pgdp_new, *pgdp_end; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci pgdp = pgd_offset_k(KASAN_SHADOW_START); 19562306a36Sopenharmony_ci pgdp_end = pgd_offset_k(KASAN_SHADOW_END); 19662306a36Sopenharmony_ci pgdp_new = pgd_offset_pgd(pgdir, KASAN_SHADOW_START); 19762306a36Sopenharmony_ci do { 19862306a36Sopenharmony_ci set_pgd(pgdp_new, READ_ONCE(*pgdp)); 19962306a36Sopenharmony_ci } while (pgdp++, pgdp_new++, pgdp != pgdp_end); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void __init clear_pgds(unsigned long start, 20362306a36Sopenharmony_ci unsigned long end) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * Remove references to kasan page tables from 20762306a36Sopenharmony_ci * swapper_pg_dir. pgd_clear() can't be used 20862306a36Sopenharmony_ci * here because it's nop on 2,3-level pagetable setups 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci for (; start < end; start += PGDIR_SIZE) 21162306a36Sopenharmony_ci set_pgd(pgd_offset_k(start), __pgd(0)); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic void __init kasan_init_shadow(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci u64 kimg_shadow_start, kimg_shadow_end; 21762306a36Sopenharmony_ci u64 mod_shadow_start; 21862306a36Sopenharmony_ci u64 vmalloc_shadow_end; 21962306a36Sopenharmony_ci phys_addr_t pa_start, pa_end; 22062306a36Sopenharmony_ci u64 i; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci kimg_shadow_start = (u64)kasan_mem_to_shadow(KERNEL_START) & PAGE_MASK; 22362306a36Sopenharmony_ci kimg_shadow_end = PAGE_ALIGN((u64)kasan_mem_to_shadow(KERNEL_END)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci mod_shadow_start = (u64)kasan_mem_to_shadow((void *)MODULES_VADDR); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci vmalloc_shadow_end = (u64)kasan_mem_to_shadow((void *)VMALLOC_END); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* 23062306a36Sopenharmony_ci * We are going to perform proper setup of shadow memory. 23162306a36Sopenharmony_ci * At first we should unmap early shadow (clear_pgds() call below). 23262306a36Sopenharmony_ci * However, instrumented code couldn't execute without shadow memory. 23362306a36Sopenharmony_ci * tmp_pg_dir used to keep early shadow mapped until full shadow 23462306a36Sopenharmony_ci * setup will be finished. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci memcpy(tmp_pg_dir, swapper_pg_dir, sizeof(tmp_pg_dir)); 23762306a36Sopenharmony_ci dsb(ishst); 23862306a36Sopenharmony_ci cpu_replace_ttbr1(lm_alias(tmp_pg_dir), idmap_pg_dir); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci kasan_map_populate(kimg_shadow_start, kimg_shadow_end, 24362306a36Sopenharmony_ci early_pfn_to_nid(virt_to_pfn(lm_alias(KERNEL_START)))); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci kasan_populate_early_shadow(kasan_mem_to_shadow((void *)PAGE_END), 24662306a36Sopenharmony_ci (void *)mod_shadow_start); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci BUILD_BUG_ON(VMALLOC_START != MODULES_END); 24962306a36Sopenharmony_ci kasan_populate_early_shadow((void *)vmalloc_shadow_end, 25062306a36Sopenharmony_ci (void *)KASAN_SHADOW_END); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for_each_mem_range(i, &pa_start, &pa_end) { 25362306a36Sopenharmony_ci void *start = (void *)__phys_to_virt(pa_start); 25462306a36Sopenharmony_ci void *end = (void *)__phys_to_virt(pa_end); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (start >= end) 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci kasan_map_populate((unsigned long)kasan_mem_to_shadow(start), 26062306a36Sopenharmony_ci (unsigned long)kasan_mem_to_shadow(end), 26162306a36Sopenharmony_ci early_pfn_to_nid(virt_to_pfn(start))); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* 26562306a36Sopenharmony_ci * KAsan may reuse the contents of kasan_early_shadow_pte directly, 26662306a36Sopenharmony_ci * so we should make sure that it maps the zero page read-only. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; i++) 26962306a36Sopenharmony_ci set_pte(&kasan_early_shadow_pte[i], 27062306a36Sopenharmony_ci pfn_pte(sym_to_pfn(kasan_early_shadow_page), 27162306a36Sopenharmony_ci PAGE_KERNEL_RO)); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE); 27462306a36Sopenharmony_ci cpu_replace_ttbr1(lm_alias(swapper_pg_dir), idmap_pg_dir); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void __init kasan_init_depth(void) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci init_task.kasan_depth = 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci#ifdef CONFIG_KASAN_VMALLOC 28362306a36Sopenharmony_civoid __init kasan_populate_early_vm_area_shadow(void *start, unsigned long size) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci unsigned long shadow_start, shadow_end; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (!is_vmalloc_or_module_addr(start)) 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci shadow_start = (unsigned long)kasan_mem_to_shadow(start); 29162306a36Sopenharmony_ci shadow_start = ALIGN_DOWN(shadow_start, PAGE_SIZE); 29262306a36Sopenharmony_ci shadow_end = (unsigned long)kasan_mem_to_shadow(start + size); 29362306a36Sopenharmony_ci shadow_end = ALIGN(shadow_end, PAGE_SIZE); 29462306a36Sopenharmony_ci kasan_map_populate(shadow_start, shadow_end, NUMA_NO_NODE); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci#endif 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_civoid __init kasan_init(void) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci kasan_init_shadow(); 30162306a36Sopenharmony_ci kasan_init_depth(); 30262306a36Sopenharmony_ci#if defined(CONFIG_KASAN_GENERIC) 30362306a36Sopenharmony_ci /* CONFIG_KASAN_SW_TAGS also requires kasan_init_sw_tags(). */ 30462306a36Sopenharmony_ci pr_info("KernelAddressSanitizer initialized (generic)\n"); 30562306a36Sopenharmony_ci#endif 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */ 309