18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file contains some kasan initialization code.
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 * This program is free software; you can redistribute it and/or modify
98c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as
108c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/memblock.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/kasan.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/mm.h>
198c2ecf20Sopenharmony_ci#include <linux/pfn.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/page.h>
238c2ecf20Sopenharmony_ci#include <asm/pgalloc.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "kasan.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * This page serves two purposes:
298c2ecf20Sopenharmony_ci *   - It used as early shadow memory. The entire shadow region populated
308c2ecf20Sopenharmony_ci *     with this page, before we will be able to setup normal shadow memory.
318c2ecf20Sopenharmony_ci *   - Latter it reused it as zero shadow to cover large ranges of memory
328c2ecf20Sopenharmony_ci *     that allowed to access, but not handled by kasan (vmalloc/vmemmap ...).
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ciunsigned char kasan_early_shadow_page[PAGE_SIZE] __page_aligned_bss;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 4
378c2ecf20Sopenharmony_cip4d_t kasan_early_shadow_p4d[MAX_PTRS_PER_P4D] __page_aligned_bss;
388c2ecf20Sopenharmony_cistatic inline bool kasan_p4d_table(pgd_t pgd)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return pgd_page(pgd) == virt_to_page(lm_alias(kasan_early_shadow_p4d));
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci#else
438c2ecf20Sopenharmony_cistatic inline bool kasan_p4d_table(pgd_t pgd)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	return false;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci#endif
488c2ecf20Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 3
498c2ecf20Sopenharmony_cipud_t kasan_early_shadow_pud[PTRS_PER_PUD] __page_aligned_bss;
508c2ecf20Sopenharmony_cistatic inline bool kasan_pud_table(p4d_t p4d)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return p4d_page(p4d) == virt_to_page(lm_alias(kasan_early_shadow_pud));
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci#else
558c2ecf20Sopenharmony_cistatic inline bool kasan_pud_table(p4d_t p4d)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	return false;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 2
618c2ecf20Sopenharmony_cipmd_t kasan_early_shadow_pmd[PTRS_PER_PMD] __page_aligned_bss;
628c2ecf20Sopenharmony_cistatic inline bool kasan_pmd_table(pud_t pud)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	return pud_page(pud) == virt_to_page(lm_alias(kasan_early_shadow_pmd));
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci#else
678c2ecf20Sopenharmony_cistatic inline bool kasan_pmd_table(pud_t pud)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return false;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci#endif
728c2ecf20Sopenharmony_cipte_t kasan_early_shadow_pte[PTRS_PER_PTE] __page_aligned_bss;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic inline bool kasan_pte_table(pmd_t pmd)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	return pmd_page(pmd) == virt_to_page(lm_alias(kasan_early_shadow_pte));
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic inline bool kasan_early_shadow_page_entry(pte_t pte)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	return pte_page(pte) == virt_to_page(lm_alias(kasan_early_shadow_page));
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic __init void *early_alloc(size_t size, int node)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	void *ptr = memblock_alloc_try_nid(size, size, __pa(MAX_DMA_ADDRESS),
878c2ecf20Sopenharmony_ci					   MEMBLOCK_ALLOC_ACCESSIBLE, node);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!ptr)
908c2ecf20Sopenharmony_ci		panic("%s: Failed to allocate %zu bytes align=%zx nid=%d from=%llx\n",
918c2ecf20Sopenharmony_ci		      __func__, size, size, node, (u64)__pa(MAX_DMA_ADDRESS));
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return ptr;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void __ref zero_pte_populate(pmd_t *pmd, unsigned long addr,
978c2ecf20Sopenharmony_ci				unsigned long end)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	pte_t *pte = pte_offset_kernel(pmd, addr);
1008c2ecf20Sopenharmony_ci	pte_t zero_pte;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	zero_pte = pfn_pte(PFN_DOWN(__pa_symbol(kasan_early_shadow_page)),
1038c2ecf20Sopenharmony_ci				PAGE_KERNEL);
1048c2ecf20Sopenharmony_ci	zero_pte = pte_wrprotect(zero_pte);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	while (addr + PAGE_SIZE <= end) {
1078c2ecf20Sopenharmony_ci		set_pte_at(&init_mm, addr, pte, zero_pte);
1088c2ecf20Sopenharmony_ci		addr += PAGE_SIZE;
1098c2ecf20Sopenharmony_ci		pte = pte_offset_kernel(pmd, addr);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int __ref zero_pmd_populate(pud_t *pud, unsigned long addr,
1148c2ecf20Sopenharmony_ci				unsigned long end)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	pmd_t *pmd = pmd_offset(pud, addr);
1178c2ecf20Sopenharmony_ci	unsigned long next;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	do {
1208c2ecf20Sopenharmony_ci		next = pmd_addr_end(addr, end);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		if (IS_ALIGNED(addr, PMD_SIZE) && end - addr >= PMD_SIZE) {
1238c2ecf20Sopenharmony_ci			pmd_populate_kernel(&init_mm, pmd,
1248c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pte));
1258c2ecf20Sopenharmony_ci			continue;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		if (pmd_none(*pmd)) {
1298c2ecf20Sopenharmony_ci			pte_t *p;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci			if (slab_is_available())
1328c2ecf20Sopenharmony_ci				p = pte_alloc_one_kernel(&init_mm);
1338c2ecf20Sopenharmony_ci			else
1348c2ecf20Sopenharmony_ci				p = early_alloc(PAGE_SIZE, NUMA_NO_NODE);
1358c2ecf20Sopenharmony_ci			if (!p)
1368c2ecf20Sopenharmony_ci				return -ENOMEM;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci			pmd_populate_kernel(&init_mm, pmd, p);
1398c2ecf20Sopenharmony_ci		}
1408c2ecf20Sopenharmony_ci		zero_pte_populate(pmd, addr, next);
1418c2ecf20Sopenharmony_ci	} while (pmd++, addr = next, addr != end);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int __ref zero_pud_populate(p4d_t *p4d, unsigned long addr,
1478c2ecf20Sopenharmony_ci				unsigned long end)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	pud_t *pud = pud_offset(p4d, addr);
1508c2ecf20Sopenharmony_ci	unsigned long next;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	do {
1538c2ecf20Sopenharmony_ci		next = pud_addr_end(addr, end);
1548c2ecf20Sopenharmony_ci		if (IS_ALIGNED(addr, PUD_SIZE) && end - addr >= PUD_SIZE) {
1558c2ecf20Sopenharmony_ci			pmd_t *pmd;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci			pud_populate(&init_mm, pud,
1588c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pmd));
1598c2ecf20Sopenharmony_ci			pmd = pmd_offset(pud, addr);
1608c2ecf20Sopenharmony_ci			pmd_populate_kernel(&init_mm, pmd,
1618c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pte));
1628c2ecf20Sopenharmony_ci			continue;
1638c2ecf20Sopenharmony_ci		}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		if (pud_none(*pud)) {
1668c2ecf20Sopenharmony_ci			pmd_t *p;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci			if (slab_is_available()) {
1698c2ecf20Sopenharmony_ci				p = pmd_alloc(&init_mm, pud, addr);
1708c2ecf20Sopenharmony_ci				if (!p)
1718c2ecf20Sopenharmony_ci					return -ENOMEM;
1728c2ecf20Sopenharmony_ci			} else {
1738c2ecf20Sopenharmony_ci				p = early_alloc(PAGE_SIZE, NUMA_NO_NODE);
1748c2ecf20Sopenharmony_ci#ifdef CONFIG_LOONGARCH
1758c2ecf20Sopenharmony_ci				pmd_init((unsigned long)p, (unsigned long)invalid_pte_table);
1768c2ecf20Sopenharmony_ci#endif
1778c2ecf20Sopenharmony_ci				pud_populate(&init_mm, pud, p);
1788c2ecf20Sopenharmony_ci			}
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci		zero_pmd_populate(pud, addr, next);
1818c2ecf20Sopenharmony_ci	} while (pud++, addr = next, addr != end);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int __ref zero_p4d_populate(pgd_t *pgd, unsigned long addr,
1878c2ecf20Sopenharmony_ci				unsigned long end)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	p4d_t *p4d = p4d_offset(pgd, addr);
1908c2ecf20Sopenharmony_ci	unsigned long next;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	do {
1938c2ecf20Sopenharmony_ci		next = p4d_addr_end(addr, end);
1948c2ecf20Sopenharmony_ci		if (IS_ALIGNED(addr, P4D_SIZE) && end - addr >= P4D_SIZE) {
1958c2ecf20Sopenharmony_ci			pud_t *pud;
1968c2ecf20Sopenharmony_ci			pmd_t *pmd;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci			p4d_populate(&init_mm, p4d,
1998c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pud));
2008c2ecf20Sopenharmony_ci			pud = pud_offset(p4d, addr);
2018c2ecf20Sopenharmony_ci			pud_populate(&init_mm, pud,
2028c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pmd));
2038c2ecf20Sopenharmony_ci			pmd = pmd_offset(pud, addr);
2048c2ecf20Sopenharmony_ci			pmd_populate_kernel(&init_mm, pmd,
2058c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pte));
2068c2ecf20Sopenharmony_ci			continue;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		if (p4d_none(*p4d)) {
2108c2ecf20Sopenharmony_ci			pud_t *p;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci			if (slab_is_available()) {
2138c2ecf20Sopenharmony_ci				p = pud_alloc(&init_mm, p4d, addr);
2148c2ecf20Sopenharmony_ci				if (!p)
2158c2ecf20Sopenharmony_ci					return -ENOMEM;
2168c2ecf20Sopenharmony_ci			} else {
2178c2ecf20Sopenharmony_ci				p = early_alloc(PAGE_SIZE, NUMA_NO_NODE);
2188c2ecf20Sopenharmony_ci#ifdef CONFIG_LOONGARCH
2198c2ecf20Sopenharmony_ci				pud_init((unsigned long)p, (unsigned long)invalid_pmd_table);
2208c2ecf20Sopenharmony_ci#endif
2218c2ecf20Sopenharmony_ci				p4d_populate(&init_mm, p4d, p);
2228c2ecf20Sopenharmony_ci			}
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci		zero_pud_populate(p4d, addr, next);
2258c2ecf20Sopenharmony_ci	} while (p4d++, addr = next, addr != end);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/**
2318c2ecf20Sopenharmony_ci * kasan_populate_early_shadow - populate shadow memory region with
2328c2ecf20Sopenharmony_ci *                               kasan_early_shadow_page
2338c2ecf20Sopenharmony_ci * @shadow_start - start of the memory range to populate
2348c2ecf20Sopenharmony_ci * @shadow_end   - end of the memory range to populate
2358c2ecf20Sopenharmony_ci */
2368c2ecf20Sopenharmony_ciint __ref kasan_populate_early_shadow(const void *shadow_start,
2378c2ecf20Sopenharmony_ci					const void *shadow_end)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)shadow_start;
2408c2ecf20Sopenharmony_ci	unsigned long end = (unsigned long)shadow_end;
2418c2ecf20Sopenharmony_ci	pgd_t *pgd = pgd_offset_k(addr);
2428c2ecf20Sopenharmony_ci	unsigned long next;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	do {
2458c2ecf20Sopenharmony_ci		next = pgd_addr_end(addr, end);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		if (IS_ALIGNED(addr, PGDIR_SIZE) && end - addr >= PGDIR_SIZE) {
2488c2ecf20Sopenharmony_ci			p4d_t *p4d;
2498c2ecf20Sopenharmony_ci			pud_t *pud;
2508c2ecf20Sopenharmony_ci			pmd_t *pmd;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci			/*
2538c2ecf20Sopenharmony_ci			 * kasan_early_shadow_pud should be populated with pmds
2548c2ecf20Sopenharmony_ci			 * at this moment.
2558c2ecf20Sopenharmony_ci			 * [pud,pmd]_populate*() below needed only for
2568c2ecf20Sopenharmony_ci			 * 3,2 - level page tables where we don't have
2578c2ecf20Sopenharmony_ci			 * puds,pmds, so pgd_populate(), pud_populate()
2588c2ecf20Sopenharmony_ci			 * is noops.
2598c2ecf20Sopenharmony_ci			 */
2608c2ecf20Sopenharmony_ci			pgd_populate(&init_mm, pgd,
2618c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_p4d));
2628c2ecf20Sopenharmony_ci			p4d = p4d_offset(pgd, addr);
2638c2ecf20Sopenharmony_ci			p4d_populate(&init_mm, p4d,
2648c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pud));
2658c2ecf20Sopenharmony_ci			pud = pud_offset(p4d, addr);
2668c2ecf20Sopenharmony_ci			pud_populate(&init_mm, pud,
2678c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pmd));
2688c2ecf20Sopenharmony_ci			pmd = pmd_offset(pud, addr);
2698c2ecf20Sopenharmony_ci			pmd_populate_kernel(&init_mm, pmd,
2708c2ecf20Sopenharmony_ci					lm_alias(kasan_early_shadow_pte));
2718c2ecf20Sopenharmony_ci			continue;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		if (pgd_none(*pgd)) {
2758c2ecf20Sopenharmony_ci			p4d_t *p;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci			if (slab_is_available()) {
2788c2ecf20Sopenharmony_ci				p = p4d_alloc(&init_mm, pgd, addr);
2798c2ecf20Sopenharmony_ci				if (!p)
2808c2ecf20Sopenharmony_ci					return -ENOMEM;
2818c2ecf20Sopenharmony_ci			} else {
2828c2ecf20Sopenharmony_ci				pgd_populate(&init_mm, pgd,
2838c2ecf20Sopenharmony_ci					early_alloc(PAGE_SIZE, NUMA_NO_NODE));
2848c2ecf20Sopenharmony_ci			}
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci		zero_p4d_populate(pgd, addr, next);
2878c2ecf20Sopenharmony_ci	} while (pgd++, addr = next, addr != end);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return 0;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic void kasan_free_pte(pte_t *pte_start, pmd_t *pmd)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	pte_t *pte;
2958c2ecf20Sopenharmony_ci	int i;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++) {
2988c2ecf20Sopenharmony_ci		pte = pte_start + i;
2998c2ecf20Sopenharmony_ci		if (!pte_none(*pte))
3008c2ecf20Sopenharmony_ci			return;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	pte_free_kernel(&init_mm, (pte_t *)page_to_virt(pmd_page(*pmd)));
3048c2ecf20Sopenharmony_ci	pmd_clear(pmd);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void kasan_free_pmd(pmd_t *pmd_start, pud_t *pud)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	pmd_t *pmd;
3108c2ecf20Sopenharmony_ci	int i;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PMD; i++) {
3138c2ecf20Sopenharmony_ci		pmd = pmd_start + i;
3148c2ecf20Sopenharmony_ci		if (!pmd_none(*pmd))
3158c2ecf20Sopenharmony_ci			return;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	pmd_free(&init_mm, (pmd_t *)page_to_virt(pud_page(*pud)));
3198c2ecf20Sopenharmony_ci	pud_clear(pud);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void kasan_free_pud(pud_t *pud_start, p4d_t *p4d)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	pud_t *pud;
3258c2ecf20Sopenharmony_ci	int i;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PUD; i++) {
3288c2ecf20Sopenharmony_ci		pud = pud_start + i;
3298c2ecf20Sopenharmony_ci		if (!pud_none(*pud))
3308c2ecf20Sopenharmony_ci			return;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	pud_free(&init_mm, (pud_t *)page_to_virt(p4d_page(*p4d)));
3348c2ecf20Sopenharmony_ci	p4d_clear(p4d);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void kasan_free_p4d(p4d_t *p4d_start, pgd_t *pgd)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	p4d_t *p4d;
3408c2ecf20Sopenharmony_ci	int i;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_P4D; i++) {
3438c2ecf20Sopenharmony_ci		p4d = p4d_start + i;
3448c2ecf20Sopenharmony_ci		if (!p4d_none(*p4d))
3458c2ecf20Sopenharmony_ci			return;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	p4d_free(&init_mm, (p4d_t *)page_to_virt(pgd_page(*pgd)));
3498c2ecf20Sopenharmony_ci	pgd_clear(pgd);
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void kasan_remove_pte_table(pte_t *pte, unsigned long addr,
3538c2ecf20Sopenharmony_ci				unsigned long end)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	unsigned long next;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	for (; addr < end; addr = next, pte++) {
3588c2ecf20Sopenharmony_ci		next = (addr + PAGE_SIZE) & PAGE_MASK;
3598c2ecf20Sopenharmony_ci		if (next > end)
3608c2ecf20Sopenharmony_ci			next = end;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		if (!pte_present(*pte))
3638c2ecf20Sopenharmony_ci			continue;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		if (WARN_ON(!kasan_early_shadow_page_entry(*pte)))
3668c2ecf20Sopenharmony_ci			continue;
3678c2ecf20Sopenharmony_ci		pte_clear(&init_mm, addr, pte);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void kasan_remove_pmd_table(pmd_t *pmd, unsigned long addr,
3728c2ecf20Sopenharmony_ci				unsigned long end)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	unsigned long next;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (; addr < end; addr = next, pmd++) {
3778c2ecf20Sopenharmony_ci		pte_t *pte;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		next = pmd_addr_end(addr, end);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		if (!pmd_present(*pmd))
3828c2ecf20Sopenharmony_ci			continue;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		if (kasan_pte_table(*pmd)) {
3858c2ecf20Sopenharmony_ci			if (IS_ALIGNED(addr, PMD_SIZE) &&
3868c2ecf20Sopenharmony_ci			    IS_ALIGNED(next, PMD_SIZE)) {
3878c2ecf20Sopenharmony_ci				pmd_clear(pmd);
3888c2ecf20Sopenharmony_ci				continue;
3898c2ecf20Sopenharmony_ci			}
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci		pte = pte_offset_kernel(pmd, addr);
3928c2ecf20Sopenharmony_ci		kasan_remove_pte_table(pte, addr, next);
3938c2ecf20Sopenharmony_ci		kasan_free_pte(pte_offset_kernel(pmd, 0), pmd);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic void kasan_remove_pud_table(pud_t *pud, unsigned long addr,
3988c2ecf20Sopenharmony_ci				unsigned long end)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	unsigned long next;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	for (; addr < end; addr = next, pud++) {
4038c2ecf20Sopenharmony_ci		pmd_t *pmd, *pmd_base;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		next = pud_addr_end(addr, end);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		if (!pud_present(*pud))
4088c2ecf20Sopenharmony_ci			continue;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		if (kasan_pmd_table(*pud)) {
4118c2ecf20Sopenharmony_ci			if (IS_ALIGNED(addr, PUD_SIZE) &&
4128c2ecf20Sopenharmony_ci			    IS_ALIGNED(next, PUD_SIZE)) {
4138c2ecf20Sopenharmony_ci				pud_clear(pud);
4148c2ecf20Sopenharmony_ci				continue;
4158c2ecf20Sopenharmony_ci			}
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci		pmd = pmd_offset(pud, addr);
4188c2ecf20Sopenharmony_ci		pmd_base = pmd_offset(pud, 0);
4198c2ecf20Sopenharmony_ci		kasan_remove_pmd_table(pmd, addr, next);
4208c2ecf20Sopenharmony_ci		kasan_free_pmd(pmd_base, pud);
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic void kasan_remove_p4d_table(p4d_t *p4d, unsigned long addr,
4258c2ecf20Sopenharmony_ci				unsigned long end)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	unsigned long next;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	for (; addr < end; addr = next, p4d++) {
4308c2ecf20Sopenharmony_ci		pud_t *pud;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		next = p4d_addr_end(addr, end);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		if (!p4d_present(*p4d))
4358c2ecf20Sopenharmony_ci			continue;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		if (kasan_pud_table(*p4d)) {
4388c2ecf20Sopenharmony_ci			if (IS_ALIGNED(addr, P4D_SIZE) &&
4398c2ecf20Sopenharmony_ci			    IS_ALIGNED(next, P4D_SIZE)) {
4408c2ecf20Sopenharmony_ci				p4d_clear(p4d);
4418c2ecf20Sopenharmony_ci				continue;
4428c2ecf20Sopenharmony_ci			}
4438c2ecf20Sopenharmony_ci		}
4448c2ecf20Sopenharmony_ci		pud = pud_offset(p4d, addr);
4458c2ecf20Sopenharmony_ci		kasan_remove_pud_table(pud, addr, next);
4468c2ecf20Sopenharmony_ci		kasan_free_pud(pud_offset(p4d, 0), p4d);
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_civoid kasan_remove_zero_shadow(void *start, unsigned long size)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	unsigned long addr, end, next;
4538c2ecf20Sopenharmony_ci	pgd_t *pgd;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	addr = (unsigned long)kasan_mem_to_shadow(start);
4568c2ecf20Sopenharmony_ci	end = addr + (size >> KASAN_SHADOW_SCALE_SHIFT);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (WARN_ON((unsigned long)start %
4598c2ecf20Sopenharmony_ci			(KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) ||
4608c2ecf20Sopenharmony_ci	    WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)))
4618c2ecf20Sopenharmony_ci		return;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	for (; addr < end; addr = next) {
4648c2ecf20Sopenharmony_ci		p4d_t *p4d;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		next = pgd_addr_end(addr, end);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		pgd = pgd_offset_k(addr);
4698c2ecf20Sopenharmony_ci		if (!pgd_present(*pgd))
4708c2ecf20Sopenharmony_ci			continue;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		if (kasan_p4d_table(*pgd)) {
4738c2ecf20Sopenharmony_ci			if (IS_ALIGNED(addr, PGDIR_SIZE) &&
4748c2ecf20Sopenharmony_ci			    IS_ALIGNED(next, PGDIR_SIZE)) {
4758c2ecf20Sopenharmony_ci				pgd_clear(pgd);
4768c2ecf20Sopenharmony_ci				continue;
4778c2ecf20Sopenharmony_ci			}
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		p4d = p4d_offset(pgd, addr);
4818c2ecf20Sopenharmony_ci		kasan_remove_p4d_table(p4d, addr, next);
4828c2ecf20Sopenharmony_ci		kasan_free_p4d(p4d_offset(pgd, 0), pgd);
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ciint kasan_add_zero_shadow(void *start, unsigned long size)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	int ret;
4898c2ecf20Sopenharmony_ci	void *shadow_start, *shadow_end;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	shadow_start = kasan_mem_to_shadow(start);
4928c2ecf20Sopenharmony_ci	shadow_end = shadow_start + (size >> KASAN_SHADOW_SCALE_SHIFT);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (WARN_ON((unsigned long)start %
4958c2ecf20Sopenharmony_ci			(KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) ||
4968c2ecf20Sopenharmony_ci	    WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)))
4978c2ecf20Sopenharmony_ci		return -EINVAL;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	ret = kasan_populate_early_shadow(shadow_start, shadow_end);
5008c2ecf20Sopenharmony_ci	if (ret)
5018c2ecf20Sopenharmony_ci		kasan_remove_zero_shadow(start, size);
5028c2ecf20Sopenharmony_ci	return ret;
5038c2ecf20Sopenharmony_ci}
504