kasan_init.c 3.0 KB
Newer Older
N
Nick Hu 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2019 Andes Technology Corporation

#include <linux/pfn.h>
#include <linux/init_task.h>
#include <linux/kasan.h>
#include <linux/kernel.h>
#include <linux/memblock.h>
#include <asm/tlbflush.h>
#include <asm/pgtable.h>
#include <asm/fixmap.h>

extern pgd_t early_pg_dir[PTRS_PER_PGD];
asmlinkage void __init kasan_early_init(void)
{
	uintptr_t i;
	pgd_t *pgd = early_pg_dir + pgd_index(KASAN_SHADOW_START);

	for (i = 0; i < PTRS_PER_PTE; ++i)
		set_pte(kasan_early_shadow_pte + i,
			mk_pte(virt_to_page(kasan_early_shadow_page),
			PAGE_KERNEL));

	for (i = 0; i < PTRS_PER_PMD; ++i)
		set_pmd(kasan_early_shadow_pmd + i,
		 pfn_pmd(PFN_DOWN(__pa((uintptr_t)kasan_early_shadow_pte)),
			__pgprot(_PAGE_TABLE)));

	for (i = KASAN_SHADOW_START; i < KASAN_SHADOW_END;
	     i += PGDIR_SIZE, ++pgd)
		set_pgd(pgd,
		 pfn_pgd(PFN_DOWN(__pa(((uintptr_t)kasan_early_shadow_pmd))),
			__pgprot(_PAGE_TABLE)));

	/* init for swapper_pg_dir */
	pgd = pgd_offset_k(KASAN_SHADOW_START);

	for (i = KASAN_SHADOW_START; i < KASAN_SHADOW_END;
	     i += PGDIR_SIZE, ++pgd)
		set_pgd(pgd,
		 pfn_pgd(PFN_DOWN(__pa(((uintptr_t)kasan_early_shadow_pmd))),
			__pgprot(_PAGE_TABLE)));

	flush_tlb_all();
}

static void __init populate(void *start, void *end)
{
49
	unsigned long i, offset;
N
Nick Hu 已提交
50 51 52
	unsigned long vaddr = (unsigned long)start & PAGE_MASK;
	unsigned long vend = PAGE_ALIGN((unsigned long)end);
	unsigned long n_pages = (vend - vaddr) / PAGE_SIZE;
53 54
	unsigned long n_ptes =
	    ((n_pages + PTRS_PER_PTE) & -PTRS_PER_PTE) / PTRS_PER_PTE;
N
Nick Hu 已提交
55
	unsigned long n_pmds =
56 57 58 59 60 61
	    ((n_ptes + PTRS_PER_PMD) & -PTRS_PER_PMD) / PTRS_PER_PMD;

	pte_t *pte =
	    memblock_alloc(n_ptes * PTRS_PER_PTE * sizeof(pte_t), PAGE_SIZE);
	pmd_t *pmd =
	    memblock_alloc(n_pmds * PTRS_PER_PMD * sizeof(pmd_t), PAGE_SIZE);
N
Nick Hu 已提交
62 63 64 65
	pgd_t *pgd = pgd_offset_k(vaddr);

	for (i = 0; i < n_pages; i++) {
		phys_addr_t phys = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
66
		set_pte(&pte[i], pfn_pte(PHYS_PFN(phys), PAGE_KERNEL));
N
Nick Hu 已提交
67 68
	}

69 70 71
	for (i = 0, offset = 0; i < n_ptes; i++, offset += PTRS_PER_PTE)
		set_pmd(&pmd[i],
			pfn_pmd(PFN_DOWN(__pa(&pte[offset])),
N
Nick Hu 已提交
72 73
				__pgprot(_PAGE_TABLE)));

74 75 76
	for (i = 0, offset = 0; i < n_pmds; i++, offset += PTRS_PER_PMD)
		set_pgd(&pgd[i],
			pfn_pgd(PFN_DOWN(__pa(&pmd[offset])),
N
Nick Hu 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
				__pgprot(_PAGE_TABLE)));

	flush_tlb_all();
	memset(start, 0, end - start);
}

void __init kasan_init(void)
{
	struct memblock_region *reg;
	unsigned long i;

	kasan_populate_early_shadow((void *)KASAN_SHADOW_START,
			(void *)kasan_mem_to_shadow((void *)VMALLOC_END));

	for_each_memblock(memory, reg) {
		void *start = (void *)__va(reg->base);
		void *end = (void *)__va(reg->base + reg->size);

		if (start >= end)
			break;

		populate(kasan_mem_to_shadow(start),
			 kasan_mem_to_shadow(end));
	};

	for (i = 0; i < PTRS_PER_PTE; i++)
		set_pte(&kasan_early_shadow_pte[i],
			mk_pte(virt_to_page(kasan_early_shadow_page),
			__pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_ACCESSED)));

	memset(kasan_early_shadow_page, 0, PAGE_SIZE);
	init_task.kasan_depth = 0;
}