Commit 2b0e86cc authored by Jason Yan's avatar Jason Yan Committed by Michael Ellerman
Browse files

powerpc/fsl_booke/32: implement KASLR infrastructure

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

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: default avatarJason Yan <>
Tested-by: default avatarDiana Craciun <>
Reviewed-by: default avatarChristophe Leroy <>
Signed-off-by: default avatarScott Wood <>
[mpe: Use PTRRELOC() in early_init()]
Signed-off-by: default avatarMichael Ellerman <>
parent c061b38a
......@@ -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).
bool "Randomize the address of the kernel image"
depends on (FSL_BOOKE && FLATMEM && PPC32)
depends on RELOCATABLE
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.
bool "Test relocatable kernel"
depends on (PPC64 && RELOCATABLE)
......@@ -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
......@@ -19,10 +19,13 @@
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)
return KERNELBASE + offset;
return kva + offset;
......@@ -155,23 +155,22 @@ skpinv: addi r6,r6,1 /* Increment */
/* 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
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
/* 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
......@@ -155,6 +155,8 @@ _ENTRY(_start);
LOAD_REG_ADDR_PIC(r20, kernstart_virt_addr)
lwz r20,0(r20)
#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 */
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
......@@ -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;
extern void loadcam_entry(unsigned int index);
extern void loadcam_multi(int first_idx, int num, int tmp_idx);
void kaslr_early_init(void *dt_ptr, phys_addr_t size);
static inline void kaslr_early_init(void *dt_ptr, phys_addr_t size) {}
struct tlbcam {
u32 MAS0;
u32 MAS1;
......@@ -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
obj-$(CONFIG_PPC_FSL_BOOK3E) += book3e_hugetlbpage.o
......@@ -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);
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2019 Jason Yan <>
#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)
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);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment