18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file contains kasan initialization code for ARM64.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "kasan: " fmt
108c2ecf20Sopenharmony_ci#include <linux/kasan.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/sched/task.h>
138c2ecf20Sopenharmony_ci#include <linux/memblock.h>
148c2ecf20Sopenharmony_ci#include <linux/start_kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/mm.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
188c2ecf20Sopenharmony_ci#include <asm/kernel-pgtable.h>
198c2ecf20Sopenharmony_ci#include <asm/page.h>
208c2ecf20Sopenharmony_ci#include <asm/pgalloc.h>
218c2ecf20Sopenharmony_ci#include <asm/sections.h>
228c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic pgd_t tmp_pg_dir[PTRS_PER_PGD] __initdata __aligned(PGD_SIZE);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * The p*d_populate functions call virt_to_phys implicitly so they can't be used
288c2ecf20Sopenharmony_ci * directly on kernel symbols (bm_p*d). All the early functions are called too
298c2ecf20Sopenharmony_ci * early to use lm_alias so __p*d_populate functions must be used to populate
308c2ecf20Sopenharmony_ci * with the physical address from __pa_symbol.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic phys_addr_t __init kasan_alloc_zeroed_page(int node)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	void *p = memblock_alloc_try_nid(PAGE_SIZE, PAGE_SIZE,
368c2ecf20Sopenharmony_ci					      __pa(MAX_DMA_ADDRESS),
378c2ecf20Sopenharmony_ci					      MEMBLOCK_ALLOC_KASAN, node);
388c2ecf20Sopenharmony_ci	if (!p)
398c2ecf20Sopenharmony_ci		panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%llx\n",
408c2ecf20Sopenharmony_ci		      __func__, PAGE_SIZE, PAGE_SIZE, node,
418c2ecf20Sopenharmony_ci		      __pa(MAX_DMA_ADDRESS));
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return __pa(p);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic phys_addr_t __init kasan_alloc_raw_page(int node)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	void *p = memblock_alloc_try_nid_raw(PAGE_SIZE, PAGE_SIZE,
498c2ecf20Sopenharmony_ci						__pa(MAX_DMA_ADDRESS),
508c2ecf20Sopenharmony_ci						MEMBLOCK_ALLOC_KASAN, node);
518c2ecf20Sopenharmony_ci	if (!p)
528c2ecf20Sopenharmony_ci		panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%llx\n",
538c2ecf20Sopenharmony_ci		      __func__, PAGE_SIZE, PAGE_SIZE, node,
548c2ecf20Sopenharmony_ci		      __pa(MAX_DMA_ADDRESS));
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return __pa(p);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr, int node,
608c2ecf20Sopenharmony_ci				      bool early)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	if (pmd_none(READ_ONCE(*pmdp))) {
638c2ecf20Sopenharmony_ci		phys_addr_t pte_phys = early ?
648c2ecf20Sopenharmony_ci				__pa_symbol(kasan_early_shadow_pte)
658c2ecf20Sopenharmony_ci					: kasan_alloc_zeroed_page(node);
668c2ecf20Sopenharmony_ci		__pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE);
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return early ? pte_offset_kimg(pmdp, addr)
708c2ecf20Sopenharmony_ci		     : pte_offset_kernel(pmdp, addr);
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr, int node,
748c2ecf20Sopenharmony_ci				      bool early)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	if (pud_none(READ_ONCE(*pudp))) {
778c2ecf20Sopenharmony_ci		phys_addr_t pmd_phys = early ?
788c2ecf20Sopenharmony_ci				__pa_symbol(kasan_early_shadow_pmd)
798c2ecf20Sopenharmony_ci					: kasan_alloc_zeroed_page(node);
808c2ecf20Sopenharmony_ci		__pud_populate(pudp, pmd_phys, PMD_TYPE_TABLE);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return early ? pmd_offset_kimg(pudp, addr) : pmd_offset(pudp, addr);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr, int node,
878c2ecf20Sopenharmony_ci				      bool early)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (p4d_none(READ_ONCE(*p4dp))) {
908c2ecf20Sopenharmony_ci		phys_addr_t pud_phys = early ?
918c2ecf20Sopenharmony_ci				__pa_symbol(kasan_early_shadow_pud)
928c2ecf20Sopenharmony_ci					: kasan_alloc_zeroed_page(node);
938c2ecf20Sopenharmony_ci		__p4d_populate(p4dp, pud_phys, PMD_TYPE_TABLE);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return early ? pud_offset_kimg(p4dp, addr) : pud_offset(p4dp, addr);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void __init kasan_pte_populate(pmd_t *pmdp, unsigned long addr,
1008c2ecf20Sopenharmony_ci				      unsigned long end, int node, bool early)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	unsigned long next;
1038c2ecf20Sopenharmony_ci	pte_t *ptep = kasan_pte_offset(pmdp, addr, node, early);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	do {
1068c2ecf20Sopenharmony_ci		phys_addr_t page_phys = early ?
1078c2ecf20Sopenharmony_ci				__pa_symbol(kasan_early_shadow_page)
1088c2ecf20Sopenharmony_ci					: kasan_alloc_raw_page(node);
1098c2ecf20Sopenharmony_ci		if (!early)
1108c2ecf20Sopenharmony_ci			memset(__va(page_phys), KASAN_SHADOW_INIT, PAGE_SIZE);
1118c2ecf20Sopenharmony_ci		next = addr + PAGE_SIZE;
1128c2ecf20Sopenharmony_ci		set_pte(ptep, pfn_pte(__phys_to_pfn(page_phys), PAGE_KERNEL));
1138c2ecf20Sopenharmony_ci	} while (ptep++, addr = next, addr != end && pte_none(READ_ONCE(*ptep)));
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void __init kasan_pmd_populate(pud_t *pudp, unsigned long addr,
1178c2ecf20Sopenharmony_ci				      unsigned long end, int node, bool early)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	unsigned long next;
1208c2ecf20Sopenharmony_ci	pmd_t *pmdp = kasan_pmd_offset(pudp, addr, node, early);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	do {
1238c2ecf20Sopenharmony_ci		next = pmd_addr_end(addr, end);
1248c2ecf20Sopenharmony_ci		kasan_pte_populate(pmdp, addr, next, node, early);
1258c2ecf20Sopenharmony_ci	} while (pmdp++, addr = next, addr != end && pmd_none(READ_ONCE(*pmdp)));
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void __init kasan_pud_populate(p4d_t *p4dp, unsigned long addr,
1298c2ecf20Sopenharmony_ci				      unsigned long end, int node, bool early)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	unsigned long next;
1328c2ecf20Sopenharmony_ci	pud_t *pudp = kasan_pud_offset(p4dp, addr, node, early);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	do {
1358c2ecf20Sopenharmony_ci		next = pud_addr_end(addr, end);
1368c2ecf20Sopenharmony_ci		kasan_pmd_populate(pudp, addr, next, node, early);
1378c2ecf20Sopenharmony_ci	} while (pudp++, addr = next, addr != end && pud_none(READ_ONCE(*pudp)));
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void __init kasan_p4d_populate(pgd_t *pgdp, unsigned long addr,
1418c2ecf20Sopenharmony_ci				      unsigned long end, int node, bool early)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	unsigned long next;
1448c2ecf20Sopenharmony_ci	p4d_t *p4dp = p4d_offset(pgdp, addr);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	do {
1478c2ecf20Sopenharmony_ci		next = p4d_addr_end(addr, end);
1488c2ecf20Sopenharmony_ci		kasan_pud_populate(p4dp, addr, next, node, early);
1498c2ecf20Sopenharmony_ci	} while (p4dp++, addr = next, addr != end);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void __init kasan_pgd_populate(unsigned long addr, unsigned long end,
1538c2ecf20Sopenharmony_ci				      int node, bool early)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	unsigned long next;
1568c2ecf20Sopenharmony_ci	pgd_t *pgdp;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	pgdp = pgd_offset_k(addr);
1598c2ecf20Sopenharmony_ci	do {
1608c2ecf20Sopenharmony_ci		next = pgd_addr_end(addr, end);
1618c2ecf20Sopenharmony_ci		kasan_p4d_populate(pgdp, addr, next, node, early);
1628c2ecf20Sopenharmony_ci	} while (pgdp++, addr = next, addr != end);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* The early shadow maps everything to a single page of zeroes */
1668c2ecf20Sopenharmony_ciasmlinkage void __init kasan_early_init(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	BUILD_BUG_ON(KASAN_SHADOW_OFFSET !=
1698c2ecf20Sopenharmony_ci		KASAN_SHADOW_END - (1UL << (64 - KASAN_SHADOW_SCALE_SHIFT)));
1708c2ecf20Sopenharmony_ci	BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS), PGDIR_SIZE));
1718c2ecf20Sopenharmony_ci	BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS_MIN), PGDIR_SIZE));
1728c2ecf20Sopenharmony_ci	BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, PGDIR_SIZE));
1738c2ecf20Sopenharmony_ci	kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE,
1748c2ecf20Sopenharmony_ci			   true);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/* Set up full kasan mappings, ensuring that the mapped pages are zeroed */
1788c2ecf20Sopenharmony_cistatic void __init kasan_map_populate(unsigned long start, unsigned long end,
1798c2ecf20Sopenharmony_ci				      int node)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	kasan_pgd_populate(start & PAGE_MASK, PAGE_ALIGN(end), node, false);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/*
1858c2ecf20Sopenharmony_ci * Copy the current shadow region into a new pgdir.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_civoid __init kasan_copy_shadow(pgd_t *pgdir)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	pgd_t *pgdp, *pgdp_new, *pgdp_end;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	pgdp = pgd_offset_k(KASAN_SHADOW_START);
1928c2ecf20Sopenharmony_ci	pgdp_end = pgd_offset_k(KASAN_SHADOW_END);
1938c2ecf20Sopenharmony_ci	pgdp_new = pgd_offset_pgd(pgdir, KASAN_SHADOW_START);
1948c2ecf20Sopenharmony_ci	do {
1958c2ecf20Sopenharmony_ci		set_pgd(pgdp_new, READ_ONCE(*pgdp));
1968c2ecf20Sopenharmony_ci	} while (pgdp++, pgdp_new++, pgdp != pgdp_end);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void __init clear_pgds(unsigned long start,
2008c2ecf20Sopenharmony_ci			unsigned long end)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	/*
2038c2ecf20Sopenharmony_ci	 * Remove references to kasan page tables from
2048c2ecf20Sopenharmony_ci	 * swapper_pg_dir. pgd_clear() can't be used
2058c2ecf20Sopenharmony_ci	 * here because it's nop on 2,3-level pagetable setups
2068c2ecf20Sopenharmony_ci	 */
2078c2ecf20Sopenharmony_ci	for (; start < end; start += PGDIR_SIZE)
2088c2ecf20Sopenharmony_ci		set_pgd(pgd_offset_k(start), __pgd(0));
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_civoid __init kasan_init(void)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	u64 kimg_shadow_start, kimg_shadow_end;
2148c2ecf20Sopenharmony_ci	u64 mod_shadow_start, mod_shadow_end;
2158c2ecf20Sopenharmony_ci	phys_addr_t pa_start, pa_end;
2168c2ecf20Sopenharmony_ci	u64 i;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	kimg_shadow_start = (u64)kasan_mem_to_shadow(_text) & PAGE_MASK;
2198c2ecf20Sopenharmony_ci	kimg_shadow_end = PAGE_ALIGN((u64)kasan_mem_to_shadow(_end));
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	mod_shadow_start = (u64)kasan_mem_to_shadow((void *)MODULES_VADDR);
2228c2ecf20Sopenharmony_ci	mod_shadow_end = (u64)kasan_mem_to_shadow((void *)MODULES_END);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/*
2258c2ecf20Sopenharmony_ci	 * We are going to perform proper setup of shadow memory.
2268c2ecf20Sopenharmony_ci	 * At first we should unmap early shadow (clear_pgds() call below).
2278c2ecf20Sopenharmony_ci	 * However, instrumented code couldn't execute without shadow memory.
2288c2ecf20Sopenharmony_ci	 * tmp_pg_dir used to keep early shadow mapped until full shadow
2298c2ecf20Sopenharmony_ci	 * setup will be finished.
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	memcpy(tmp_pg_dir, swapper_pg_dir, sizeof(tmp_pg_dir));
2328c2ecf20Sopenharmony_ci	dsb(ishst);
2338c2ecf20Sopenharmony_ci	cpu_replace_ttbr1(lm_alias(tmp_pg_dir));
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	kasan_map_populate(kimg_shadow_start, kimg_shadow_end,
2388c2ecf20Sopenharmony_ci			   early_pfn_to_nid(virt_to_pfn(lm_alias(_text))));
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	kasan_populate_early_shadow(kasan_mem_to_shadow((void *)PAGE_END),
2418c2ecf20Sopenharmony_ci				   (void *)mod_shadow_start);
2428c2ecf20Sopenharmony_ci	kasan_populate_early_shadow((void *)kimg_shadow_end,
2438c2ecf20Sopenharmony_ci				   (void *)KASAN_SHADOW_END);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (kimg_shadow_start > mod_shadow_end)
2468c2ecf20Sopenharmony_ci		kasan_populate_early_shadow((void *)mod_shadow_end,
2478c2ecf20Sopenharmony_ci					    (void *)kimg_shadow_start);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	for_each_mem_range(i, &pa_start, &pa_end) {
2508c2ecf20Sopenharmony_ci		void *start = (void *)__phys_to_virt(pa_start);
2518c2ecf20Sopenharmony_ci		void *end = (void *)__phys_to_virt(pa_end);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		if (start >= end)
2548c2ecf20Sopenharmony_ci			break;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		kasan_map_populate((unsigned long)kasan_mem_to_shadow(start),
2578c2ecf20Sopenharmony_ci				   (unsigned long)kasan_mem_to_shadow(end),
2588c2ecf20Sopenharmony_ci				   early_pfn_to_nid(virt_to_pfn(start)));
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/*
2628c2ecf20Sopenharmony_ci	 * KAsan may reuse the contents of kasan_early_shadow_pte directly,
2638c2ecf20Sopenharmony_ci	 * so we should make sure that it maps the zero page read-only.
2648c2ecf20Sopenharmony_ci	 */
2658c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++)
2668c2ecf20Sopenharmony_ci		set_pte(&kasan_early_shadow_pte[i],
2678c2ecf20Sopenharmony_ci			pfn_pte(sym_to_pfn(kasan_early_shadow_page),
2688c2ecf20Sopenharmony_ci				PAGE_KERNEL_RO));
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
2718c2ecf20Sopenharmony_ci	cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* At this point kasan is fully initialized. Enable error messages */
2748c2ecf20Sopenharmony_ci	init_task.kasan_depth = 0;
2758c2ecf20Sopenharmony_ci	pr_info("KernelAddressSanitizer initialized\n");
2768c2ecf20Sopenharmony_ci}
277