Skip to content

Commit

Permalink
powerpc/32: Add KASAN support
Browse files Browse the repository at this point in the history
This patch adds KASAN support for PPC32. The following patch
will add an early activation of hash table for book3s. Until
then, a warning will be raised if trying to use KASAN on an
hash 6xx.

To support KASAN, this patch initialises that MMU mapings for
accessing to the KASAN shadow area defined in a previous patch.

An early mapping is set as soon as the kernel code has been
relocated at its definitive place.

Then the definitive mapping is set once paging is initialised.

For modules, the shadow area is allocated at module_alloc().

Signed-off-by: Christophe Leroy <[email protected]>
Signed-off-by: Michael Ellerman <[email protected]>
  • Loading branch information
chleroy authored and mpe committed May 2, 2019
1 parent f072015 commit 2edb16e
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 0 deletions.
1 change: 1 addition & 0 deletions arch/powerpc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ config PPC
select GENERIC_TIME_VSYSCALL
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_KASAN if PPC32
select HAVE_ARCH_KGDB
select HAVE_ARCH_MMAP_RND_BITS
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
Expand Down
9 changes: 9 additions & 0 deletions arch/powerpc/include/asm/kasan.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,14 @@

#define KASAN_SHADOW_SIZE (KASAN_SHADOW_END - KASAN_SHADOW_START)

#ifdef CONFIG_KASAN
void kasan_early_init(void);
void kasan_mmu_init(void);
void kasan_init(void);
#else
static inline void kasan_init(void) { }
static inline void kasan_mmu_init(void) { }
#endif

#endif /* __ASSEMBLY */
#endif
3 changes: 3 additions & 0 deletions arch/powerpc/kernel/head_32.S
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,9 @@ start_here:
* Do early platform-specific initialization,
* and set up the MMU.
*/
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
li r3,0
mr r4,r31
bl machine_init
Expand Down
3 changes: 3 additions & 0 deletions arch/powerpc/kernel/head_40x.S
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,9 @@ start_here:
/*
* Decide what sort of machine this is and initialize the MMU.
*/
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
li r3,0
mr r4,r31
bl machine_init
Expand Down
3 changes: 3 additions & 0 deletions arch/powerpc/kernel/head_44x.S
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ _ENTRY(_start);
/*
* Decide what sort of machine this is and initialize the MMU.
*/
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
li r3,0
mr r4,r31
bl machine_init
Expand Down
3 changes: 3 additions & 0 deletions arch/powerpc/kernel/head_8xx.S
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,9 @@ start_here:
/*
* Decide what sort of machine this is and initialize the MMU.
*/
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
li r3,0
mr r4,r31
bl machine_init
Expand Down
3 changes: 3 additions & 0 deletions arch/powerpc/kernel/head_fsl_booke.S
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ set_ivor:
/*
* Decide what sort of machine this is and initialize the MMU.
*/
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
mr r3,r30
mr r4,r31
bl machine_init
Expand Down
3 changes: 3 additions & 0 deletions arch/powerpc/kernel/setup-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include <asm/livepatch.h>
#include <asm/mmu_context.h>
#include <asm/cpu_has_feature.h>
#include <asm/kasan.h>

#include "setup.h"

Expand Down Expand Up @@ -871,6 +872,8 @@ static void smp_setup_pacas(void)
*/
void __init setup_arch(char **cmdline_p)
{
kasan_init();

*cmdline_p = boot_command_line;

/* Set a half-reasonable default so udelay does something sensible */
Expand Down
1 change: 1 addition & 0 deletions arch/powerpc/mm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
obj-$(CONFIG_HIGHMEM) += highmem.o
obj-$(CONFIG_PPC_COPRO_BASE) += copro_fault.o
obj-$(CONFIG_PPC_PTDUMP) += ptdump/
obj-$(CONFIG_KASAN) += kasan/
3 changes: 3 additions & 0 deletions arch/powerpc/mm/init_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include <asm/sections.h>
#include <asm/hugetlb.h>
#include <asm/kup.h>
#include <asm/kasan.h>

#include <mm/mmu_decl.h>

Expand Down Expand Up @@ -179,6 +180,8 @@ void __init MMU_init(void)
btext_unmap();
#endif

kasan_mmu_init();

setup_kup();

/* Shortly after that, the entire linear mapping will be available */
Expand Down
156 changes: 156 additions & 0 deletions arch/powerpc/mm/kasan/kasan_init_32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0

#define DISABLE_BRANCH_PROFILING

#include <linux/kasan.h>
#include <linux/printk.h>
#include <linux/memblock.h>
#include <linux/sched/task.h>
#include <linux/vmalloc.h>
#include <asm/pgalloc.h>
#include <asm/code-patching.h>
#include <mm/mmu_decl.h>

static void kasan_populate_pte(pte_t *ptep, pgprot_t prot)
{
unsigned long va = (unsigned long)kasan_early_shadow_page;
phys_addr_t pa = __pa(kasan_early_shadow_page);
int i;

for (i = 0; i < PTRS_PER_PTE; i++, ptep++)
__set_pte_at(&init_mm, va, ptep, pfn_pte(PHYS_PFN(pa), prot), 0);
}

static int kasan_init_shadow_page_tables(unsigned long k_start, unsigned long k_end)
{
pmd_t *pmd;
unsigned long k_cur, k_next;

pmd = pmd_offset(pud_offset(pgd_offset_k(k_start), k_start), k_start);

for (k_cur = k_start; k_cur != k_end; k_cur = k_next, pmd++) {
pte_t *new;

k_next = pgd_addr_end(k_cur, k_end);
if ((void *)pmd_page_vaddr(*pmd) != kasan_early_shadow_pte)
continue;

new = pte_alloc_one_kernel(&init_mm);

if (!new)
return -ENOMEM;
kasan_populate_pte(new, PAGE_KERNEL_RO);
pmd_populate_kernel(&init_mm, pmd, new);
}
return 0;
}

static void __ref *kasan_get_one_page(void)
{
if (slab_is_available())
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);

return memblock_alloc(PAGE_SIZE, PAGE_SIZE);
}

static int __ref kasan_init_region(void *start, size_t size)
{
unsigned long k_start = (unsigned long)kasan_mem_to_shadow(start);
unsigned long k_end = (unsigned long)kasan_mem_to_shadow(start + size);
unsigned long k_cur;
int ret;
void *block = NULL;

ret = kasan_init_shadow_page_tables(k_start, k_end);
if (ret)
return ret;

if (!slab_is_available())
block = memblock_alloc(k_end - k_start, PAGE_SIZE);

for (k_cur = k_start; k_cur < k_end; k_cur += PAGE_SIZE) {
pmd_t *pmd = pmd_offset(pud_offset(pgd_offset_k(k_cur), k_cur), k_cur);
void *va = block ? block + k_cur - k_start : kasan_get_one_page();
pte_t pte = pfn_pte(PHYS_PFN(__pa(va)), PAGE_KERNEL);

if (!va)
return -ENOMEM;

__set_pte_at(&init_mm, k_cur, pte_offset_kernel(pmd, k_cur), pte, 0);
}
flush_tlb_kernel_range(k_start, k_end);
return 0;
}

static void __init kasan_remap_early_shadow_ro(void)
{
kasan_populate_pte(kasan_early_shadow_pte, PAGE_KERNEL_RO);

flush_tlb_kernel_range(KASAN_SHADOW_START, KASAN_SHADOW_END);
}

void __init kasan_mmu_init(void)
{
int ret;
struct memblock_region *reg;

for_each_memblock(memory, reg) {
phys_addr_t base = reg->base;
phys_addr_t top = min(base + reg->size, total_lowmem);

if (base >= top)
continue;

ret = kasan_init_region(__va(base), top - base);
if (ret)
panic("kasan: kasan_init_region() failed");
}
}

void __init kasan_init(void)
{
kasan_remap_early_shadow_ro();

clear_page(kasan_early_shadow_page);

/* At this point kasan is fully initialized. Enable error messages */
init_task.kasan_depth = 0;
pr_info("KASAN init done\n");
}

#ifdef CONFIG_MODULES
void *module_alloc(unsigned long size)
{
void *base = vmalloc_exec(size);

if (!base)
return NULL;

if (!kasan_init_region(base, size))
return base;

vfree(base);

return NULL;
}
#endif

void __init kasan_early_init(void)
{
unsigned long addr = KASAN_SHADOW_START;
unsigned long end = KASAN_SHADOW_END;
unsigned long next;
pmd_t *pmd = pmd_offset(pud_offset(pgd_offset_k(addr), addr), addr);

BUILD_BUG_ON(KASAN_SHADOW_START & ~PGDIR_MASK);

kasan_populate_pte(kasan_early_shadow_pte, PAGE_KERNEL);

do {
next = pgd_addr_end(addr, end);
pmd_populate_kernel(&init_mm, pmd, kasan_early_shadow_pte);
} while (pmd++, addr = next, addr != end);

if (early_mmu_has_feature(MMU_FTR_HPTE_TABLE))
WARN(true, "KASAN not supported on hash 6xx");
}

0 comments on commit 2edb16e

Please sign in to comment.