162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * KASAN for 64-bit Book3S powerpc
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2019-2022, Daniel Axtens, IBM Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * ppc64 turns on virtual memory late in boot, after calling into generic code
1062306a36Sopenharmony_ci * like the device-tree parser, so it uses this in conjunction with a hook in
1162306a36Sopenharmony_ci * outline mode to avoid invalid access early in boot.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define DISABLE_BRANCH_PROFILING
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kasan.h>
1762306a36Sopenharmony_ci#include <linux/printk.h>
1862306a36Sopenharmony_ci#include <linux/sched/task.h>
1962306a36Sopenharmony_ci#include <linux/memblock.h>
2062306a36Sopenharmony_ci#include <asm/pgalloc.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(powerpc_kasan_enabled_key);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic void __init kasan_init_phys_region(void *start, void *end)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	unsigned long k_start, k_end, k_cur;
2762306a36Sopenharmony_ci	void *va;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (start >= end)
3062306a36Sopenharmony_ci		return;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	k_start = ALIGN_DOWN((unsigned long)kasan_mem_to_shadow(start), PAGE_SIZE);
3362306a36Sopenharmony_ci	k_end = ALIGN((unsigned long)kasan_mem_to_shadow(end), PAGE_SIZE);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	va = memblock_alloc(k_end - k_start, PAGE_SIZE);
3662306a36Sopenharmony_ci	for (k_cur = k_start; k_cur < k_end; k_cur += PAGE_SIZE, va += PAGE_SIZE)
3762306a36Sopenharmony_ci		map_kernel_page(k_cur, __pa(va), PAGE_KERNEL);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid __init kasan_init(void)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	/*
4362306a36Sopenharmony_ci	 * We want to do the following things:
4462306a36Sopenharmony_ci	 *  1) Map real memory into the shadow for all physical memblocks
4562306a36Sopenharmony_ci	 *     This takes us from c000... to c008...
4662306a36Sopenharmony_ci	 *  2) Leave a hole over the shadow of vmalloc space. KASAN_VMALLOC
4762306a36Sopenharmony_ci	 *     will manage this for us.
4862306a36Sopenharmony_ci	 *     This takes us from c008... to c00a...
4962306a36Sopenharmony_ci	 *  3) Map the 'early shadow'/zero page over iomap and vmemmap space.
5062306a36Sopenharmony_ci	 *     This takes us up to where we start at c00e...
5162306a36Sopenharmony_ci	 */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	void *k_start = kasan_mem_to_shadow((void *)RADIX_VMALLOC_END);
5462306a36Sopenharmony_ci	void *k_end = kasan_mem_to_shadow((void *)RADIX_VMEMMAP_END);
5562306a36Sopenharmony_ci	phys_addr_t start, end;
5662306a36Sopenharmony_ci	u64 i;
5762306a36Sopenharmony_ci	pte_t zero_pte = pfn_pte(virt_to_pfn(kasan_early_shadow_page), PAGE_KERNEL);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (!early_radix_enabled()) {
6062306a36Sopenharmony_ci		pr_warn("KASAN not enabled as it requires radix!");
6162306a36Sopenharmony_ci		return;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	for_each_mem_range(i, &start, &end)
6562306a36Sopenharmony_ci		kasan_init_phys_region((void *)start, (void *)end);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++)
6862306a36Sopenharmony_ci		__set_pte_at(&init_mm, (unsigned long)kasan_early_shadow_page,
6962306a36Sopenharmony_ci			     &kasan_early_shadow_pte[i], zero_pte, 0);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	for (i = 0; i < PTRS_PER_PMD; i++)
7262306a36Sopenharmony_ci		pmd_populate_kernel(&init_mm, &kasan_early_shadow_pmd[i],
7362306a36Sopenharmony_ci				    kasan_early_shadow_pte);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	for (i = 0; i < PTRS_PER_PUD; i++)
7662306a36Sopenharmony_ci		pud_populate(&init_mm, &kasan_early_shadow_pud[i],
7762306a36Sopenharmony_ci			     kasan_early_shadow_pmd);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* map the early shadow over the iomap and vmemmap space */
8062306a36Sopenharmony_ci	kasan_populate_early_shadow(k_start, k_end);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* mark early shadow region as RO and wipe it */
8362306a36Sopenharmony_ci	zero_pte = pfn_pte(virt_to_pfn(kasan_early_shadow_page), PAGE_KERNEL_RO);
8462306a36Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++)
8562306a36Sopenharmony_ci		__set_pte_at(&init_mm, (unsigned long)kasan_early_shadow_page,
8662306a36Sopenharmony_ci			     &kasan_early_shadow_pte[i], zero_pte, 0);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * clear_page relies on some cache info that hasn't been set up yet.
9062306a36Sopenharmony_ci	 * It ends up looping ~forever and blows up other data.
9162306a36Sopenharmony_ci	 * Use memset instead.
9262306a36Sopenharmony_ci	 */
9362306a36Sopenharmony_ci	memset(kasan_early_shadow_page, 0, PAGE_SIZE);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	static_branch_inc(&powerpc_kasan_enabled_key);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* Enable error messages */
9862306a36Sopenharmony_ci	init_task.kasan_depth = 0;
9962306a36Sopenharmony_ci	pr_info("KASAN init done\n");
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid __init kasan_early_init(void) { }
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_civoid __init kasan_late_init(void) { }
105