powerpc/fsl_booke/32: implement KASLR infrastructure
authorJason Yan <yanaijie@huawei.com>
Fri, 20 Sep 2019 09:45:40 +0000 (17:45 +0800)
committerMichael Ellerman <mpe@ellerman.id.au>
Wed, 13 Nov 2019 08:27:40 +0000 (19:27 +1100)
This patch add support to boot kernel from places other than KERNELBASE.
Since CONFIG_RELOCATABLE has already supported, what we need to do is
map or copy kernel to a proper place and relocate. Freescale Book-E
parts expect lowmem to be mapped by fixed TLB entries(TLB1). The TLB1
entries are not suitable to map the kernel directly in a randomized
region, so we chose to copy the kernel to a proper place and restart to
relocate.

The offset of the kernel was not randomized yet(a fixed 64M is set). We
will randomize it in the next patch.

Signed-off-by: Jason Yan <yanaijie@huawei.com>
Tested-by: Diana Craciun <diana.craciun@nxp.com>
Reviewed-by: Christophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: Scott Wood <oss@buserror.net>
[mpe: Use PTRRELOC() in early_init()]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/Kconfig
arch/powerpc/include/asm/nohash/mmu-book3e.h
arch/powerpc/kernel/early_32.c
arch/powerpc/kernel/fsl_booke_entry_mapping.S
arch/powerpc/kernel/head_fsl_booke.S
arch/powerpc/mm/mmu_decl.h
arch/powerpc/mm/nohash/Makefile
arch/powerpc/mm/nohash/fsl_booke.c
arch/powerpc/mm/nohash/kaslr_booke.c [new file with mode: 0644]

index 3e56c9c2f16eed8487190350bd615a702c9de498..4c4a0fcd16742cc4e74a9803f3090c1be62734bb 100644 (file)
@@ -551,6 +551,17 @@ config RELOCATABLE
          setting can still be useful to bootwrappers that need to know the
          load address of the kernel (eg. u-boot/mkimage).
 
+config RANDOMIZE_BASE
+       bool "Randomize the address of the kernel image"
+       depends on (FSL_BOOKE && FLATMEM && PPC32)
+       depends on RELOCATABLE
+       help
+         Randomizes the virtual address at which the kernel image is
+         loaded, as a security feature that deters exploit attempts
+         relying on knowledge of the location of kernel internals.
+
+         If unsure, say Y.
+
 config RELOCATABLE_TEST
        bool "Test relocatable kernel"
        depends on (PPC64 && RELOCATABLE)
index fa3efc2d310fa960767824101badd3f75bbb5270..b410046643126e12ff615a9553f5efe36348dad7 100644 (file)
@@ -75,7 +75,6 @@
 #define MAS2_E                 0x00000001
 #define MAS2_WIMGE_MASK                0x0000001f
 #define MAS2_EPN_MASK(size)            (~0 << (size + 10))
-#define MAS2_VAL(addr, size, flags)    ((addr) & MAS2_EPN_MASK(size) | (flags))
 
 #define MAS3_RPN               0xFFFFF000
 #define MAS3_U0                        0x00000200
index 3482118ffe763a0dc09b82ff64b5db92a787846b..ef2ad49459040dcc7107b38703097a6ff7a068c3 100644 (file)
  */
 notrace unsigned long __init early_init(unsigned long dt_ptr)
 {
-       unsigned long offset = reloc_offset();
+       unsigned long kva, offset = reloc_offset();
+
+       kva = *PTRRELOC(&kernstart_virt_addr);
 
        /* First zero the BSS */
-       memset(PTRRELOC(&__bss_start), 0, __bss_stop - __bss_start);
+       if (kva == KERNELBASE)
+               memset(PTRRELOC(&__bss_start), 0, __bss_stop - __bss_start);
 
        /*
         * Identify the CPU type and fix up code sections
@@ -32,5 +35,5 @@ notrace unsigned long __init early_init(unsigned long dt_ptr)
 
        apply_feature_fixups();
 
-       return KERNELBASE + offset;
+       return kva + offset;
 }
index f4d3eaae54a935db888d759272112c5e2e849706..8bccce6544b5eadc5845020b2c22958899f92cfe 100644 (file)
@@ -155,23 +155,22 @@ skpinv:   addi    r6,r6,1                         /* Increment */
 
 #if defined(ENTRY_MAPPING_BOOT_SETUP)
 
-/* 6. Setup KERNELBASE mapping in TLB1[0] */
+/* 6. Setup kernstart_virt_addr mapping in TLB1[0] */
        lis     r6,0x1000               /* Set MAS0(TLBSEL) = TLB1(1), ESEL = 0 */
        mtspr   SPRN_MAS0,r6
        lis     r6,(MAS1_VALID|MAS1_IPROT)@h
        ori     r6,r6,(MAS1_TSIZE(BOOK3E_PAGESZ_64M))@l
        mtspr   SPRN_MAS1,r6
-       lis     r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, MAS2_M_IF_NEEDED)@h
-       ori     r6,r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, MAS2_M_IF_NEEDED)@l
+       lis     r6,MAS2_EPN_MASK(BOOK3E_PAGESZ_64M)@h
+       ori     r6,r6,MAS2_EPN_MASK(BOOK3E_PAGESZ_64M)@l
+       and     r6,r6,r20
+       ori     r6,r6,MAS2_M_IF_NEEDED@l
        mtspr   SPRN_MAS2,r6
        mtspr   SPRN_MAS3,r8
        tlbwe
 
-/* 7. Jump to KERNELBASE mapping */
-       lis     r6,(KERNELBASE & ~0xfff)@h
-       ori     r6,r6,(KERNELBASE & ~0xfff)@l
-       rlwinm  r7,r25,0,0x03ffffff
-       add     r6,r7,r6
+/* 7. Jump to kernstart_virt_addr mapping */
+       mr      r6,r20
 
 #elif defined(ENTRY_MAPPING_KEXEC_SETUP)
 /*
index d9f599b01ff13ed35ea88f515d76218be9275ec2..838d9d4650c751a8985d957a84dea1e65f16d207 100644 (file)
@@ -155,6 +155,8 @@ _ENTRY(_start);
  */
 
 _ENTRY(__early_start)
+       LOAD_REG_ADDR_PIC(r20, kernstart_virt_addr)
+       lwz     r20,0(r20)
 
 #define ENTRY_MAPPING_BOOT_SETUP
 #include "fsl_booke_entry_mapping.S"
@@ -277,8 +279,8 @@ set_ivor:
        ori     r6, r6, swapper_pg_dir@l
        lis     r5, abatron_pteptrs@h
        ori     r5, r5, abatron_pteptrs@l
-       lis     r4, KERNELBASE@h
-       ori     r4, r4, KERNELBASE@l
+       lis     r3, kernstart_virt_addr@ha
+       lwz     r4, kernstart_virt_addr@l(r3)
        stw     r5, 0(r4)       /* Save abatron_pteptrs at a fixed location */
        stw     r6, 0(r5)
 
@@ -1067,7 +1069,12 @@ __secondary_start:
        mr      r5,r25          /* phys kernel start */
        rlwinm  r5,r5,0,~0x3ffffff      /* aligned 64M */
        subf    r4,r5,r4        /* memstart_addr - phys kernel start */
-       li      r5,0            /* no device tree */
+       lis     r7,KERNELBASE@h
+       ori     r7,r7,KERNELBASE@l
+       cmpw    r20,r7          /* if kernstart_virt_addr != KERNELBASE, randomized */
+       beq     2f
+       li      r4,0
+2:     li      r5,0            /* no device tree */
        li      r6,0            /* not boot cpu */
        bl      restore_to_as0
 
index bbfd185fb99afe3ec82513bbdc95518a75b70807..ae06c5675abbb10cbe82cd2ec1306d607bc216bf 100644 (file)
@@ -141,10 +141,17 @@ extern int switch_to_as1(void);
 extern void restore_to_as0(int esel, int offset, void *dt_ptr, int bootcpu);
 void create_kaslr_tlb_entry(int entry, unsigned long virt, phys_addr_t phys);
 void reloc_kernel_entry(void *fdt, int addr);
+extern int is_second_reloc;
 #endif
 extern void loadcam_entry(unsigned int index);
 extern void loadcam_multi(int first_idx, int num, int tmp_idx);
 
+#ifdef CONFIG_RANDOMIZE_BASE
+void kaslr_early_init(void *dt_ptr, phys_addr_t size);
+#else
+static inline void kaslr_early_init(void *dt_ptr, phys_addr_t size) {}
+#endif
+
 struct tlbcam {
        u32     MAS0;
        u32     MAS1;
index 33b6f6f29d3f259a8fe8724480f98ff5c7658630..0424f6ce5bd80cf5e2c63340a8e7218a4a789bec 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_40x)               += 40x.o
 obj-$(CONFIG_44x)              += 44x.o
 obj-$(CONFIG_PPC_8xx)          += 8xx.o
 obj-$(CONFIG_PPC_FSL_BOOK3E)   += fsl_booke.o
+obj-$(CONFIG_RANDOMIZE_BASE)   += kaslr_booke.o
 ifdef CONFIG_HUGETLB_PAGE
 obj-$(CONFIG_PPC_FSL_BOOK3E)   += book3e_hugetlbpage.o
 endif
index 556e3cd52a35f4d64533512e4502b7bb6781b2de..2dc27cf88add00f24ed952c77e5db73753d43f5c 100644 (file)
@@ -263,7 +263,8 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,
 int __initdata is_second_reloc;
 notrace void __init relocate_init(u64 dt_ptr, phys_addr_t start)
 {
-       unsigned long base = KERNELBASE;
+       unsigned long base = kernstart_virt_addr;
+       phys_addr_t size;
 
        kernstart_addr = start;
        if (is_second_reloc) {
@@ -291,7 +292,7 @@ notrace void __init relocate_init(u64 dt_ptr, phys_addr_t start)
        start &= ~0x3ffffff;
        base &= ~0x3ffffff;
        virt_phys_offset = base - start;
-       early_get_first_memblock_info(__va(dt_ptr), NULL);
+       early_get_first_memblock_info(__va(dt_ptr), &size);
        /*
         * We now get the memstart_addr, then we should check if this
         * address is the same as what the PAGE_OFFSET map to now. If
@@ -316,6 +317,8 @@ notrace void __init relocate_init(u64 dt_ptr, phys_addr_t start)
                /* We should never reach here */
                panic("Relocation error");
        }
+
+       kaslr_early_init(__va(dt_ptr), size);
 }
 #endif
 #endif
diff --git a/arch/powerpc/mm/nohash/kaslr_booke.c b/arch/powerpc/mm/nohash/kaslr_booke.c
new file mode 100644 (file)
index 0000000..29c1567
--- /dev/null
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (C) 2019 Jason Yan <yanaijie@huawei.com>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/memblock.h>
+#include <asm/pgalloc.h>
+#include <asm/prom.h>
+#include <mm/mmu_decl.h>
+
+static unsigned long __init kaslr_choose_location(void *dt_ptr, phys_addr_t size,
+                                                 unsigned long kernel_sz)
+{
+       /* return a fixed offset of 64M for now */
+       return SZ_64M;
+}
+
+/*
+ * To see if we need to relocate the kernel to a random offset
+ * void *dt_ptr - address of the device tree
+ * phys_addr_t size - size of the first memory block
+ */
+notrace void __init kaslr_early_init(void *dt_ptr, phys_addr_t size)
+{
+       unsigned long tlb_virt;
+       phys_addr_t tlb_phys;
+       unsigned long offset;
+       unsigned long kernel_sz;
+
+       kernel_sz = (unsigned long)_end - (unsigned long)_stext;
+
+       offset = kaslr_choose_location(dt_ptr, size, kernel_sz);
+       if (offset == 0)
+               return;
+
+       kernstart_virt_addr += offset;
+       kernstart_addr += offset;
+
+       is_second_reloc = 1;
+
+       if (offset >= SZ_64M) {
+               tlb_virt = round_down(kernstart_virt_addr, SZ_64M);
+               tlb_phys = round_down(kernstart_addr, SZ_64M);
+
+               /* Create kernel map to relocate in */
+               create_kaslr_tlb_entry(1, tlb_virt, tlb_phys);
+       }
+
+       /* Copy the kernel to it's new location and run */
+       memcpy((void *)kernstart_virt_addr, (void *)_stext, kernel_sz);
+       flush_icache_range(kernstart_virt_addr, kernstart_virt_addr + kernel_sz);
+
+       reloc_kernel_entry(dt_ptr, kernstart_virt_addr);
+}