162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#define DISABLE_BRANCH_PROFILING
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/kasan.h>
662306a36Sopenharmony_ci#include <linux/printk.h>
762306a36Sopenharmony_ci#include <linux/memblock.h>
862306a36Sopenharmony_ci#include <linux/sched/task.h>
962306a36Sopenharmony_ci#include <asm/pgalloc.h>
1062306a36Sopenharmony_ci#include <asm/code-patching.h>
1162306a36Sopenharmony_ci#include <mm/mmu_decl.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic pgprot_t __init kasan_prot_ro(void)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	if (early_mmu_has_feature(MMU_FTR_HPTE_TABLE))
1662306a36Sopenharmony_ci		return PAGE_READONLY;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	return PAGE_KERNEL_RO;
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void __init kasan_populate_pte(pte_t *ptep, pgprot_t prot)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	unsigned long va = (unsigned long)kasan_early_shadow_page;
2462306a36Sopenharmony_ci	phys_addr_t pa = __pa(kasan_early_shadow_page);
2562306a36Sopenharmony_ci	int i;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++, ptep++)
2862306a36Sopenharmony_ci		__set_pte_at(&init_mm, va, ptep, pfn_pte(PHYS_PFN(pa), prot), 1);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint __init kasan_init_shadow_page_tables(unsigned long k_start, unsigned long k_end)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	pmd_t *pmd;
3462306a36Sopenharmony_ci	unsigned long k_cur, k_next;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	pmd = pmd_off_k(k_start);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (k_cur = k_start; k_cur != k_end; k_cur = k_next, pmd++) {
3962306a36Sopenharmony_ci		pte_t *new;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		k_next = pgd_addr_end(k_cur, k_end);
4262306a36Sopenharmony_ci		if ((void *)pmd_page_vaddr(*pmd) != kasan_early_shadow_pte)
4362306a36Sopenharmony_ci			continue;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci		new = memblock_alloc(PTE_FRAG_SIZE, PTE_FRAG_SIZE);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		if (!new)
4862306a36Sopenharmony_ci			return -ENOMEM;
4962306a36Sopenharmony_ci		kasan_populate_pte(new, PAGE_KERNEL);
5062306a36Sopenharmony_ci		pmd_populate_kernel(&init_mm, pmd, new);
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci	return 0;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciint __init __weak kasan_init_region(void *start, size_t size)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	unsigned long k_start = (unsigned long)kasan_mem_to_shadow(start);
5862306a36Sopenharmony_ci	unsigned long k_end = (unsigned long)kasan_mem_to_shadow(start + size);
5962306a36Sopenharmony_ci	unsigned long k_cur;
6062306a36Sopenharmony_ci	int ret;
6162306a36Sopenharmony_ci	void *block;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ret = kasan_init_shadow_page_tables(k_start, k_end);
6462306a36Sopenharmony_ci	if (ret)
6562306a36Sopenharmony_ci		return ret;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	k_start = k_start & PAGE_MASK;
6862306a36Sopenharmony_ci	block = memblock_alloc(k_end - k_start, PAGE_SIZE);
6962306a36Sopenharmony_ci	if (!block)
7062306a36Sopenharmony_ci		return -ENOMEM;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	for (k_cur = k_start & PAGE_MASK; k_cur < k_end; k_cur += PAGE_SIZE) {
7362306a36Sopenharmony_ci		pmd_t *pmd = pmd_off_k(k_cur);
7462306a36Sopenharmony_ci		void *va = block + k_cur - k_start;
7562306a36Sopenharmony_ci		pte_t pte = pfn_pte(PHYS_PFN(__pa(va)), PAGE_KERNEL);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		__set_pte_at(&init_mm, k_cur, pte_offset_kernel(pmd, k_cur), pte, 0);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci	flush_tlb_kernel_range(k_start, k_end);
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid __init
8462306a36Sopenharmony_cikasan_update_early_region(unsigned long k_start, unsigned long k_end, pte_t pte)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	unsigned long k_cur;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	for (k_cur = k_start; k_cur != k_end; k_cur += PAGE_SIZE) {
8962306a36Sopenharmony_ci		pmd_t *pmd = pmd_off_k(k_cur);
9062306a36Sopenharmony_ci		pte_t *ptep = pte_offset_kernel(pmd, k_cur);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		if (pte_page(*ptep) != virt_to_page(lm_alias(kasan_early_shadow_page)))
9362306a36Sopenharmony_ci			continue;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		__set_pte_at(&init_mm, k_cur, ptep, pte, 0);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	flush_tlb_kernel_range(k_start, k_end);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void __init kasan_remap_early_shadow_ro(void)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	pgprot_t prot = kasan_prot_ro();
10462306a36Sopenharmony_ci	phys_addr_t pa = __pa(kasan_early_shadow_page);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	kasan_populate_pte(kasan_early_shadow_pte, prot);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	kasan_update_early_region(KASAN_SHADOW_START, KASAN_SHADOW_END,
10962306a36Sopenharmony_ci				  pfn_pte(PHYS_PFN(pa), prot));
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void __init kasan_unmap_early_shadow_vmalloc(void)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	unsigned long k_start = (unsigned long)kasan_mem_to_shadow((void *)VMALLOC_START);
11562306a36Sopenharmony_ci	unsigned long k_end = (unsigned long)kasan_mem_to_shadow((void *)VMALLOC_END);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	kasan_update_early_region(k_start, k_end, __pte(0));
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#ifdef MODULES_VADDR
12062306a36Sopenharmony_ci	k_start = (unsigned long)kasan_mem_to_shadow((void *)MODULES_VADDR);
12162306a36Sopenharmony_ci	k_end = (unsigned long)kasan_mem_to_shadow((void *)MODULES_END);
12262306a36Sopenharmony_ci	kasan_update_early_region(k_start, k_end, __pte(0));
12362306a36Sopenharmony_ci#endif
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_civoid __init kasan_mmu_init(void)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	int ret;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (early_mmu_has_feature(MMU_FTR_HPTE_TABLE)) {
13162306a36Sopenharmony_ci		ret = kasan_init_shadow_page_tables(KASAN_SHADOW_START, KASAN_SHADOW_END);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		if (ret)
13462306a36Sopenharmony_ci			panic("kasan: kasan_init_shadow_page_tables() failed");
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_civoid __init kasan_init(void)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	phys_addr_t base, end;
14162306a36Sopenharmony_ci	u64 i;
14262306a36Sopenharmony_ci	int ret;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for_each_mem_range(i, &base, &end) {
14562306a36Sopenharmony_ci		phys_addr_t top = min(end, total_lowmem);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		if (base >= top)
14862306a36Sopenharmony_ci			continue;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		ret = kasan_init_region(__va(base), top - base);
15162306a36Sopenharmony_ci		if (ret)
15262306a36Sopenharmony_ci			panic("kasan: kasan_init_region() failed");
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) {
15662306a36Sopenharmony_ci		ret = kasan_init_shadow_page_tables(KASAN_SHADOW_START, KASAN_SHADOW_END);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (ret)
15962306a36Sopenharmony_ci			panic("kasan: kasan_init_shadow_page_tables() failed");
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	kasan_remap_early_shadow_ro();
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	clear_page(kasan_early_shadow_page);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* At this point kasan is fully initialized. Enable error messages */
16762306a36Sopenharmony_ci	init_task.kasan_depth = 0;
16862306a36Sopenharmony_ci	pr_info("KASAN init done\n");
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_civoid __init kasan_late_init(void)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_KASAN_VMALLOC))
17462306a36Sopenharmony_ci		kasan_unmap_early_shadow_vmalloc();
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_civoid __init kasan_early_init(void)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	unsigned long addr = KASAN_SHADOW_START;
18062306a36Sopenharmony_ci	unsigned long end = KASAN_SHADOW_END;
18162306a36Sopenharmony_ci	unsigned long next;
18262306a36Sopenharmony_ci	pmd_t *pmd = pmd_off_k(addr);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	BUILD_BUG_ON(KASAN_SHADOW_START & ~PGDIR_MASK);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	kasan_populate_pte(kasan_early_shadow_pte, PAGE_KERNEL);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	do {
18962306a36Sopenharmony_ci		next = pgd_addr_end(addr, end);
19062306a36Sopenharmony_ci		pmd_populate_kernel(&init_mm, pmd, kasan_early_shadow_pte);
19162306a36Sopenharmony_ci	} while (pmd++, addr = next, addr != end);
19262306a36Sopenharmony_ci}
193