diff options
author | Russell King <rmk+kernel@armlinux.org.uk> | 2017-11-08 19:42:47 +0000 |
---|---|---|
committer | Russell King <rmk+kernel@armlinux.org.uk> | 2017-11-08 19:42:47 +0000 |
commit | 02196144a0a0ad71fb75bd4a5a4785ab36c3b78e (patch) | |
tree | b87fe75626250a456604a331faf3594b4f3925a8 /arch/arm | |
parent | 7f3d1f984336377074ebf804ff53869ef1906fbe (diff) | |
parent | fe9c0589eeef4b3edbaad9f7500679a2eeafe951 (diff) | |
download | linux-02196144a0a0ad71fb75bd4a5a4785ab36c3b78e.tar.gz linux-02196144a0a0ad71fb75bd4a5a4785ab36c3b78e.tar.bz2 linux-02196144a0a0ad71fb75bd4a5a4785ab36c3b78e.zip |
Merge branch 'devel-stable' into for-next
Diffstat (limited to 'arch/arm')
30 files changed, 1111 insertions, 449 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 979aac3e2fbf..631e61bc7ac4 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -240,15 +240,6 @@ config NEED_RET_TO_USER config ARCH_MTD_XIP bool -config VECTORS_BASE - hex - default 0xffff0000 if MMU || CPU_HIGH_VECTOR - default DRAM_BASE if REMAP_VECTORS_TO_RAM - default 0x00000000 - help - The base address of exception vectors. This must be two pages - in size. - config ARM_PATCH_PHYS_VIRT bool "Patch physical to virtual translations at runtime" if EMBEDDED default y @@ -2006,6 +1997,17 @@ config XIP_PHYS_ADDR be linked for and stored to. This address is dependent on your own flash usage. +config XIP_DEFLATED_DATA + bool "Store kernel .data section compressed in ROM" + depends on XIP_KERNEL + select ZLIB_INFLATE + help + Before the kernel is actually executed, its .data section has to be + copied to RAM from ROM. This option allows for storing that data + in compressed form and decompressed to RAM rather than merely being + copied, saving some precious ROM space. A possible drawback is a + slightly longer boot delay. + config KEXEC bool "Kexec system call (EXPERIMENTAL)" depends on (!SMP || PM_SLEEP_SMP) diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu index b7576349528c..0fad7d943630 100644 --- a/arch/arm/Kconfig-nommu +++ b/arch/arm/Kconfig-nommu @@ -52,8 +52,8 @@ config REMAP_VECTORS_TO_RAM config ARM_MPU bool 'Use the ARM v7 PMSA Compliant MPU' - depends on CPU_V7 - default y + depends on CPU_V7 || CPU_V7M + default y if CPU_V7 help Some ARM systems without an MMU have instead a Memory Protection Unit (MPU) that defines the type and permissions for regions of diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 50f8d1be7fcb..a3af4dc08c3e 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -31,8 +31,19 @@ targets := Image zImage xipImage bootpImage uImage ifeq ($(CONFIG_XIP_KERNEL),y) +cmd_deflate_xip_data = $(CONFIG_SHELL) -c \ + '$(srctree)/$(src)/deflate_xip_data.sh $< $@ || { rm -f $@; false; }' + +ifeq ($(CONFIG_XIP_DEFLATED_DATA),y) +quiet_cmd_mkxip = XIPZ $@ +cmd_mkxip = $(cmd_objcopy) && $(cmd_deflate_xip_data) +else +quiet_cmd_mkxip = $(quiet_cmd_objcopy) +cmd_mkxip = $(cmd_objcopy) +endif + $(obj)/xipImage: vmlinux FORCE - $(call if_changed,objcopy) + $(call if_changed,mkxip) @$(kecho) ' Physical Address of xipImage: $(CONFIG_XIP_PHYS_ADDR)' $(obj)/Image $(obj)/zImage: FORCE diff --git a/arch/arm/boot/deflate_xip_data.sh b/arch/arm/boot/deflate_xip_data.sh new file mode 100755 index 000000000000..1189598a25eb --- /dev/null +++ b/arch/arm/boot/deflate_xip_data.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +# XIP kernel .data segment compressor +# +# Created by: Nicolas Pitre, August 2017 +# Copyright: (C) 2017 Linaro Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. + +# This script locates the start of the .data section in xipImage and +# substitutes it with a compressed version. The needed offsets are obtained +# from symbol addresses in vmlinux. It is expected that .data extends to +# the end of xipImage. + +set -e + +VMLINUX="$1" +XIPIMAGE="$2" + +DD="dd status=none" + +# Use "make V=1" to debug this script. +case "$KBUILD_VERBOSE" in +*1*) + set -x + ;; +esac + +sym_val() { + # extract hex value for symbol in $1 + local val=$($NM "$VMLINUX" | sed -n "/ $1$/{s/ .*$//p;q}") + [ "$val" ] || { echo "can't find $1 in $VMLINUX" 1>&2; exit 1; } + # convert from hex to decimal + echo $((0x$val)) +} + +__data_loc=$(sym_val __data_loc) +_edata_loc=$(sym_val _edata_loc) +base_offset=$(sym_val _xiprom) + +# convert to file based offsets +data_start=$(($__data_loc - $base_offset)) +data_end=$(($_edata_loc - $base_offset)) + +# Make sure data occupies the last part of the file. +file_end=$(stat -c "%s" "$XIPIMAGE") +if [ "$file_end" != "$data_end" ]; then + printf "end of xipImage doesn't match with _edata_loc (%#x vs %#x)\n" \ + $(($file_end + $base_offset)) $_edata_loc 2>&1 + exit 1; +fi + +# be ready to clean up +trap 'rm -f "$XIPIMAGE.tmp"' 0 1 2 3 + +# substitute the data section by a compressed version +$DD if="$XIPIMAGE" count=$data_start iflag=count_bytes of="$XIPIMAGE.tmp" +$DD if="$XIPIMAGE" skip=$data_start iflag=skip_bytes | +gzip -9 >> "$XIPIMAGE.tmp" + +# replace kernel binary +mv -f "$XIPIMAGE.tmp" "$XIPIMAGE" diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index b62eaeb147aa..abaac5e07b80 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -173,6 +173,11 @@ static inline unsigned int __attribute_const__ read_cpuid_cachetype(void) return read_cpuid(CPUID_CACHETYPE); } +static inline unsigned int __attribute_const__ read_cpuid_mputype(void) +{ + return read_cpuid(CPUID_MPUIR); +} + #elif defined(CONFIG_CPU_V7M) static inline unsigned int __attribute_const__ read_cpuid_id(void) @@ -185,6 +190,11 @@ static inline unsigned int __attribute_const__ read_cpuid_cachetype(void) return readl(BASEADDR_V7M_SCB + V7M_SCB_CTR); } +static inline unsigned int __attribute_const__ read_cpuid_mputype(void) +{ + return readl(BASEADDR_V7M_SCB + MPU_TYPE); +} + #else /* ifdef CONFIG_CPU_CP15 / elif defined(CONFIG_CPU_V7M) */ static inline unsigned int __attribute_const__ read_cpuid_id(void) diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index f13ae153fb24..ad0ca4f2ba13 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -100,10 +100,15 @@ struct elf32_hdr; extern int elf_check_arch(const struct elf32_hdr *); #define elf_check_arch elf_check_arch +#define ELFOSABI_ARM_FDPIC 65 /* ARM FDPIC platform */ +#define elf_check_fdpic(x) ((x)->e_ident[EI_OSABI] == ELFOSABI_ARM_FDPIC) +#define elf_check_const_displacement(x) ((x)->e_flags & EF_ARM_PIC) +#define ELF_FDPIC_CORE_EFLAGS 0 + #define vmcore_elf64_check_arch(x) (0) -extern int arm_elf_read_implies_exec(const struct elf32_hdr *, int); -#define elf_read_implies_exec(ex,stk) arm_elf_read_implies_exec(&(ex), stk) +extern int arm_elf_read_implies_exec(int); +#define elf_read_implies_exec(ex,stk) arm_elf_read_implies_exec(stk) struct task_struct; int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); @@ -120,6 +125,13 @@ int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); have no such handler. */ #define ELF_PLAT_INIT(_r, load_addr) (_r)->ARM_r0 = 0 +#define ELF_FDPIC_PLAT_INIT(_r, _exec_map_addr, _interp_map_addr, dynamic_addr) \ + do { \ + (_r)->ARM_r7 = _exec_map_addr; \ + (_r)->ARM_r8 = _interp_map_addr; \ + (_r)->ARM_r9 = dynamic_addr; \ + } while(0) + extern void elf_set_personality(const struct elf32_hdr *); #define SET_PERSONALITY(ex) elf_set_personality(&(ex)) diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h index a5b47421059d..bdec37c6ac35 100644 --- a/arch/arm/include/asm/mmu.h +++ b/arch/arm/include/asm/mmu.h @@ -14,6 +14,10 @@ typedef struct { #ifdef CONFIG_VDSO unsigned long vdso; #endif +#ifdef CONFIG_BINFMT_ELF_FDPIC + unsigned long exec_fdpic_loadmap; + unsigned long interp_fdpic_loadmap; +#endif } mm_context_t; #ifdef CONFIG_CPU_HAS_ASID @@ -33,6 +37,10 @@ typedef struct { */ typedef struct { unsigned long end_brk; +#ifdef CONFIG_BINFMT_ELF_FDPIC + unsigned long exec_fdpic_loadmap; + unsigned long interp_fdpic_loadmap; +#endif } mm_context_t; #endif diff --git a/arch/arm/include/asm/mpu.h b/arch/arm/include/asm/mpu.h index c3247cc2fe08..56ec02617f58 100644 --- a/arch/arm/include/asm/mpu.h +++ b/arch/arm/include/asm/mpu.h @@ -1,8 +1,6 @@ #ifndef __ARM_MPU_H #define __ARM_MPU_H -#ifdef CONFIG_ARM_MPU - /* MPUIR layout */ #define MPUIR_nU 1 #define MPUIR_DREGION 8 @@ -17,6 +15,11 @@ /* MPU D/I Size Register fields */ #define MPU_RSR_SZ 1 #define MPU_RSR_EN 0 +#define MPU_RSR_SD 8 + +/* Number of subregions (SD) */ +#define MPU_NR_SUBREGS 8 +#define MPU_MIN_SUBREG_SIZE 256 /* The D/I RSR value for an enabled region spanning the whole of memory */ #define MPU_RSR_ALL_MEM 63 @@ -38,6 +41,7 @@ #endif /* Access permission bits of ACR (only define those that we use)*/ +#define MPU_AP_PL1RO_PL0NA (0x5 << 8) #define MPU_AP_PL1RW_PL0RW (0x3 << 8) #define MPU_AP_PL1RW_PL0R0 (0x2 << 8) #define MPU_AP_PL1RW_PL0NA (0x1 << 8) @@ -46,7 +50,7 @@ #define MPU_PROBE_REGION 0 #define MPU_BG_REGION 1 #define MPU_RAM_REGION 2 -#define MPU_VECTORS_REGION 3 +#define MPU_ROM_REGION 3 /* Maximum number of regions Linux is interested in */ #define MPU_MAX_REGIONS 16 @@ -64,13 +68,23 @@ struct mpu_rgn { }; struct mpu_rgn_info { - u32 mpuir; + unsigned int used; struct mpu_rgn rgns[MPU_MAX_REGIONS]; }; extern struct mpu_rgn_info mpu_rgn_info; -#endif /* __ASSEMBLY__ */ +#ifdef CONFIG_ARM_MPU + +extern void __init adjust_lowmem_bounds_mpu(void); +extern void __init mpu_setup(void); -#endif /* CONFIG_ARM_MPU */ +#else + +static inline void adjust_lowmem_bounds_mpu(void) {} +static inline void mpu_setup(void) {} + +#endif /* !CONFIG_ARM_MPU */ + +#endif /* __ASSEMBLY__ */ #endif diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index c3d5fc124a05..338cbe0a18ef 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -47,15 +47,24 @@ struct thread_struct { #define INIT_THREAD { } -#ifdef CONFIG_MMU -#define nommu_start_thread(regs) do { } while (0) -#else -#define nommu_start_thread(regs) regs->ARM_r10 = current->mm->start_data -#endif - #define start_thread(regs,pc,sp) \ ({ \ + unsigned long r7, r8, r9; \ + \ + if (IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC)) { \ + r7 = regs->ARM_r7; \ + r8 = regs->ARM_r8; \ + r9 = regs->ARM_r9; \ + } \ memset(regs->uregs, 0, sizeof(regs->uregs)); \ + if (IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) && \ + current->personality & FDPIC_FUNCPTRS) { \ + regs->ARM_r7 = r7; \ + regs->ARM_r8 = r8; \ + regs->ARM_r9 = r9; \ + regs->ARM_r10 = current->mm->start_data; \ + } else if (!IS_ENABLED(CONFIG_MMU)) \ + regs->ARM_r10 = current->mm->start_data; \ if (current->personality & ADDR_LIMIT_32BIT) \ regs->ARM_cpsr = USR_MODE; \ else \ @@ -65,7 +74,6 @@ struct thread_struct { regs->ARM_cpsr |= PSR_ENDSTATE; \ regs->ARM_pc = pc & ~1; /* pc */ \ regs->ARM_sp = sp; /* sp */ \ - nommu_start_thread(regs); \ }) /* Forward declaration, a strange C thing */ diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 3d6dc8b460e4..709a55989cb0 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -60,7 +60,7 @@ asmlinkage void secondary_start_kernel(void); */ struct secondary_data { union { - unsigned long mpu_rgn_szr; + struct mpu_rgn_info *mpu_rgn_info; u64 pgdir; }; unsigned long swapper_pg_dir; diff --git a/arch/arm/include/asm/ucontext.h b/arch/arm/include/asm/ucontext.h index 921d8274855c..b42c75ae0d19 100644 --- a/arch/arm/include/asm/ucontext.h +++ b/arch/arm/include/asm/ucontext.h @@ -2,6 +2,7 @@ #define _ASMARM_UCONTEXT_H #include <asm/fpstate.h> +#include <asm/user.h> /* * struct sigcontext only has room for the basic registers, but struct diff --git a/arch/arm/include/asm/v7m.h b/arch/arm/include/asm/v7m.h index 1fd775c1bc5d..5de776c81382 100644 --- a/arch/arm/include/asm/v7m.h +++ b/arch/arm/include/asm/v7m.h @@ -57,6 +57,16 @@ #define V7M_SCB_CCSIDR 0x80 /* Cache size ID register */ #define V7M_SCB_CSSELR 0x84 /* Cache size selection register */ +/* Memory-mapped MPU registers for M-class */ +#define MPU_TYPE 0x90 +#define MPU_CTRL 0x94 +#define MPU_CTRL_ENABLE 1 +#define MPU_CTRL_PRIVDEFENA (1 << 2) + +#define MPU_RNR 0x98 +#define MPU_RBAR 0x9c +#define MPU_RASR 0xa0 + /* Cache opeartions */ #define V7M_SCB_ICIALLU 0x250 /* I-cache invalidate all to PoU */ #define V7M_SCB_ICIMVAU 0x258 /* I-cache invalidate by MVA to PoU */ diff --git a/arch/arm/include/uapi/asm/ptrace.h b/arch/arm/include/uapi/asm/ptrace.h index 5af0ed1b825a..3173eb9751fd 100644 --- a/arch/arm/include/uapi/asm/ptrace.h +++ b/arch/arm/include/uapi/asm/ptrace.h @@ -31,6 +31,10 @@ #define PTRACE_SETVFPREGS 28 #define PTRACE_GETHBPREGS 29 #define PTRACE_SETHBPREGS 30 +#define PTRACE_GETFDPIC 31 + +#define PTRACE_GETFDPIC_EXEC 0 +#define PTRACE_GETFDPIC_INTERP 1 /* * PSR bits diff --git a/arch/arm/include/uapi/asm/unistd.h b/arch/arm/include/uapi/asm/unistd.h index 28bd456494a3..575b25fc29c6 100644 --- a/arch/arm/include/uapi/asm/unistd.h +++ b/arch/arm/include/uapi/asm/unistd.h @@ -35,5 +35,6 @@ #define __ARM_NR_usr26 (__ARM_NR_BASE+3) #define __ARM_NR_usr32 (__ARM_NR_BASE+4) #define __ARM_NR_set_tls (__ARM_NR_BASE+5) +#define __ARM_NR_get_tls (__ARM_NR_BASE+6) #endif /* _UAPI__ASM_ARM_UNISTD_H */ diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index ad325a8c7e1e..52f437997cc6 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -87,6 +87,11 @@ head-y := head$(MMUEXT).o obj-$(CONFIG_DEBUG_LL) += debug.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +# This is executed very early using a temporary stack when no memory allocator +# nor global data is available. Everything has to be allocated on the stack. +CFLAGS_head-inflate-data.o := $(call cc-option,-Wframe-larger-than=10240) +obj-$(CONFIG_XIP_DEFLATED_DATA) += head-inflate-data.o + obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o AFLAGS_hyp-stub.o :=-Wa,-march=armv7-a ifeq ($(CONFIG_ARM_PSCI),y) diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 608008229c7d..f369ece99958 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -23,11 +23,13 @@ #include <asm/mach/arch.h> #include <asm/thread_info.h> #include <asm/memory.h> +#include <asm/mpu.h> #include <asm/procinfo.h> #include <asm/suspend.h> #include <asm/vdso_datapage.h> #include <asm/hardware/cache-l2x0.h> #include <linux/kbuild.h> +#include "signal.h" /* * Make sure that the compiler and target are compatible. @@ -112,6 +114,9 @@ int main(void) DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit)); DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs)); BLANK(); + DEFINE(SIGFRAME_RC3_OFFSET, offsetof(struct sigframe, retcode[3])); + DEFINE(RT_SIGFRAME_RC3_OFFSET, offsetof(struct rt_sigframe, sig.retcode[3])); + BLANK(); #ifdef CONFIG_CACHE_L2X0 DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base)); DEFINE(L2X0_R_AUX_CTRL, offsetof(struct l2x0_regs, aux_ctrl)); @@ -183,5 +188,15 @@ int main(void) #ifdef CONFIG_VDSO DEFINE(VDSO_DATA_SIZE, sizeof(union vdso_data_store)); #endif + BLANK(); +#ifdef CONFIG_ARM_MPU + DEFINE(MPU_RNG_INFO_RNGS, offsetof(struct mpu_rgn_info, rgns)); + DEFINE(MPU_RNG_INFO_USED, offsetof(struct mpu_rgn_info, used)); + + DEFINE(MPU_RNG_SIZE, sizeof(struct mpu_rgn)); + DEFINE(MPU_RGN_DRBAR, offsetof(struct mpu_rgn, drbar)); + DEFINE(MPU_RGN_DRSR, offsetof(struct mpu_rgn, drsr)); + DEFINE(MPU_RGN_DRACR, offsetof(struct mpu_rgn, dracr)); +#endif return 0; } diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c index d0d1e83150c9..569e69ece5ca 100644 --- a/arch/arm/kernel/elf.c +++ b/arch/arm/kernel/elf.c @@ -3,6 +3,7 @@ #include <linux/personality.h> #include <linux/binfmts.h> #include <linux/elf.h> +#include <linux/elf-fdpic.h> #include <asm/system_info.h> int elf_check_arch(const struct elf32_hdr *x) @@ -80,7 +81,7 @@ EXPORT_SYMBOL(elf_set_personality); * - the binary requires an executable stack * - we're running on a CPU which doesn't support NX. */ -int arm_elf_read_implies_exec(const struct elf32_hdr *x, int executable_stack) +int arm_elf_read_implies_exec(int executable_stack) { if (executable_stack != EXSTACK_DISABLE_X) return 1; @@ -89,3 +90,24 @@ int arm_elf_read_implies_exec(const struct elf32_hdr *x, int executable_stack) return 0; } EXPORT_SYMBOL(arm_elf_read_implies_exec); + +#if defined(CONFIG_MMU) && defined(CONFIG_BINFMT_ELF_FDPIC) + +void elf_fdpic_arch_lay_out_mm(struct elf_fdpic_params *exec_params, + struct elf_fdpic_params *interp_params, + unsigned long *start_stack, + unsigned long *start_brk) +{ + elf_set_personality(&exec_params->hdr); + + exec_params->load_addr = 0x8000; + interp_params->load_addr = ELF_ET_DYN_BASE; + *start_stack = TASK_SIZE - SZ_16M; + + if ((exec_params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) == ELF_FDPIC_FLAG_INDEPENDENT) { + exec_params->flags &= ~ELF_FDPIC_FLAG_ARRANGEMENT; + exec_params->flags |= ELF_FDPIC_FLAG_CONSTDISP; + } +} + +#endif diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S index 8733012d231f..21dde771a7dd 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -79,47 +79,69 @@ ENDPROC(__vet_atags) */ __INIT __mmap_switched: - adr r3, __mmap_switched_data - - ldmia r3!, {r4, r5, r6, r7} - cmp r4, r5 @ Copy data segment if needed -1: cmpne r5, r6 - ldrne fp, [r4], #4 - strne fp, [r5], #4 - bne 1b - - mov fp, #0 @ Clear BSS (and zero fp) -1: cmp r6, r7 - strcc fp, [r6],#4 - bcc 1b - - ARM( ldmia r3, {r4, r5, r6, r7, sp}) - THUMB( ldmia r3, {r4, r5, r6, r7} ) - THUMB( ldr sp, [r3, #16] ) - str r9, [r4] @ Save processor ID - str r1, [r5] @ Save machine type - str r2, [r6] @ Save atags pointer - cmp r7, #0 - strne r0, [r7] @ Save control register values + + mov r7, r1 + mov r8, r2 + mov r10, r0 + + adr r4, __mmap_switched_data + mov fp, #0 + +#if defined(CONFIG_XIP_DEFLATED_DATA) + ARM( ldr sp, [r4], #4 ) + THUMB( ldr sp, [r4] ) + THUMB( add r4, #4 ) + bl __inflate_kernel_data @ decompress .data to RAM + teq r0, #0 + bne __error +#elif defined(CONFIG_XIP_KERNEL) + ARM( ldmia r4!, {r0, r1, r2, sp} ) + THUMB( ldmia r4!, {r0, r1, r2, r3} ) + THUMB( mov sp, r3 ) + sub r2, r2, r1 + bl memcpy @ copy .data to RAM +#endif + + ARM( ldmia r4!, {r0, r1, sp} ) + THUMB( ldmia r4!, {r0, r1, r3} ) + THUMB( mov sp, r3 ) + sub r1, r1, r0 + bl __memzero @ clear .bss + + ldmia r4, {r0, r1, r2, r3} + str r9, [r0] @ Save processor ID + str r7, [r1] @ Save machine type + str r8, [r2] @ Save atags pointer + cmp r3, #0 + strne r10, [r3] @ Save control register values + mov lr, #0 b start_kernel ENDPROC(__mmap_switched) .align 2 .type __mmap_switched_data, %object __mmap_switched_data: - .long __data_loc @ r4 - .long _sdata @ r5 - .long __bss_start @ r6 - .long _end @ r7 - .long processor_id @ r4 - .long __machine_arch_type @ r5 - .long __atags_pointer @ r6 +#ifdef CONFIG_XIP_KERNEL +#ifndef CONFIG_XIP_DEFLATED_DATA + .long _sdata @ r0 + .long __data_loc @ r1 + .long _edata_loc @ r2 +#endif + .long __bss_stop @ sp (temporary stack in .bss) +#endif + + .long __bss_start @ r0 + .long __bss_stop @ r1 + .long init_thread_union + THREAD_START_SP @ sp + + .long processor_id @ r0 + .long __machine_arch_type @ r1 + .long __atags_pointer @ r2 #ifdef CONFIG_CPU_CP15 - .long cr_alignment @ r7 + .long cr_alignment @ r3 #else - .long 0 @ r7 + .long 0 @ r3 #endif - .long init_thread_union + THREAD_START_SP @ sp .size __mmap_switched_data, . - __mmap_switched_data /* diff --git a/arch/arm/kernel/head-inflate-data.c b/arch/arm/kernel/head-inflate-data.c new file mode 100644 index 000000000000..6dd0ce5e6058 --- /dev/null +++ b/arch/arm/kernel/head-inflate-data.c @@ -0,0 +1,62 @@ +/* + * XIP kernel .data segment decompressor + * + * Created by: Nicolas Pitre, August 2017 + * Copyright: (C) 2017 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/zutil.h> + +/* for struct inflate_state */ +#include "../../../lib/zlib_inflate/inftrees.h" +#include "../../../lib/zlib_inflate/inflate.h" +#include "../../../lib/zlib_inflate/infutil.h" + +extern char __data_loc[]; +extern char _edata_loc[]; +extern char _sdata[]; + +/* + * This code is called very early during the boot process to decompress + * the .data segment stored compressed in ROM. Therefore none of the global + * variables are valid yet, hence no kernel services such as memory + * allocation is available. Everything must be allocated on the stack and + * we must avoid any global data access. We use a temporary stack located + * in the .bss area. The linker script makes sure the .bss is big enough + * to hold our stack frame plus some room for called functions. + * + * We mimic the code in lib/decompress_inflate.c to use the smallest work + * area possible. And because everything is statically allocated on the + * stack then there is no need to clean up before returning. + */ + +int __init __inflate_kernel_data(void) +{ + struct z_stream_s stream, *strm = &stream; + struct inflate_state state; + char *in = __data_loc; + int rc; + + /* Check and skip gzip header (assume no filename) */ + if (in[0] != 0x1f || in[1] != 0x8b || in[2] != 0x08 || in[3] & ~3) + return -1; + in += 10; + + strm->workspace = &state; + strm->next_in = in; + strm->avail_in = _edata_loc - __data_loc; /* upper bound */ + strm->next_out = _sdata; + strm->avail_out = _edata_loc - __data_loc; + zlib_inflateInit2(strm, -MAX_WBITS); + WS(strm)->inflate_state.wsize = 0; + WS(strm)->inflate_state.window = NULL; + rc = zlib_inflate(strm, Z_FINISH); + if (rc == Z_OK || rc == Z_STREAM_END) + rc = strm->avail_out; /* should be 0 */ + return rc; +} diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 2e21e08de747..2e38f85b757a 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -13,6 +13,7 @@ */ #include <linux/linkage.h> #include <linux/init.h> +#include <linux/errno.h> #include <asm/assembler.h> #include <asm/ptrace.h> @@ -110,8 +111,8 @@ ENTRY(secondary_startup) #ifdef CONFIG_ARM_MPU /* Use MPU region info supplied by __cpu_up */ - ldr r6, [r7] @ get secondary_data.mpu_szr - bl __setup_mpu @ Initialize the MPU + ldr r6, [r7] @ get secondary_data.mpu_rgn_info + bl __secondary_setup_mpu @ Initialize the MPU #endif badr lr, 1f @ return (PIC) address @@ -175,19 +176,33 @@ ENDPROC(__after_proc_init) #ifdef CONFIG_ARM_MPU +#ifndef CONFIG_CPU_V7M /* Set which MPU region should be programmed */ -.macro set_region_nr tmp, rgnr +.macro set_region_nr tmp, rgnr, unused mov \tmp, \rgnr @ Use static region numbers mcr p15, 0, \tmp, c6, c2, 0 @ Write RGNR .endm /* Setup a single MPU region, either D or I side (D-side for unified) */ -.macro setup_region bar, acr, sr, side = MPU_DATA_SIDE +.macro setup_region bar, acr, sr, side = MPU_DATA_SIDE, unused mcr p15, 0, \bar, c6, c1, (0 + \side) @ I/DRBAR mcr p15, 0, \acr, c6, c1, (4 + \side) @ I/DRACR mcr p15, 0, \sr, c6, c1, (2 + \side) @ I/DRSR .endm +#else +.macro set_region_nr tmp, rgnr, base + mov \tmp, \rgnr + str \tmp, [\base, #MPU_RNR] +.endm + +.macro setup_region bar, acr, sr, unused, base + lsl \acr, \acr, #16 + orr \acr, \acr, \sr + str \bar, [\base, #MPU_RBAR] + str \acr, [\base, #MPU_RASR] +.endm +#endif /* * Setup the MPU and initial MPU Regions. We create the following regions: * Region 0: Use this for probing the MPU details, so leave disabled. @@ -201,64 +216,137 @@ ENDPROC(__after_proc_init) ENTRY(__setup_mpu) /* Probe for v7 PMSA compliance */ - mrc p15, 0, r0, c0, c1, 4 @ Read ID_MMFR0 +M_CLASS(movw r12, #:lower16:BASEADDR_V7M_SCB) +M_CLASS(movt r12, #:upper16:BASEADDR_V7M_SCB) + +AR_CLASS(mrc p15, 0, r0, c0, c1, 4) @ Read ID_MMFR0 +M_CLASS(ldr r0, [r12, 0x50]) and r0, r0, #(MMFR0_PMSA) @ PMSA field teq r0, #(MMFR0_PMSAv7) @ PMSA v7 - bne __error_p @ Fail: ARM_MPU on NOT v7 PMSA + bxne lr /* Determine whether the D/I-side memory map is unified. We set the * flags here and continue to use them for the rest of this function */ - mrc p15, 0, r0, c0, c0, 4 @ MPUIR +AR_CLASS(mrc p15, 0, r0, c0, c0, 4) @ MPUIR +M_CLASS(ldr r0, [r12, #MPU_TYPE]) ands r5, r0, #MPUIR_DREGION_SZMASK @ 0 size d region => No MPU - beq __error_p @ Fail: ARM_MPU and no MPU + bxeq lr tst r0, #MPUIR_nU @ MPUIR_nU = 0 for unified /* Setup second region first to free up r6 */ - set_region_nr r0, #MPU_RAM_REGION + set_region_nr r0, #MPU_RAM_REGION, r12 isb /* Full access from PL0, PL1, shared for CONFIG_SMP, cacheable */ ldr r0, =PLAT_PHYS_OFFSET @ RAM starts at PHYS_OFFSET ldr r5,=(MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL) |