aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/Kconfig11
-rw-r--r--arch/arm/boot/Makefile13
-rwxr-xr-xarch/arm/boot/deflate_xip_data.sh64
-rw-r--r--arch/arm/kernel/Makefile5
-rw-r--r--arch/arm/kernel/head-common.S11
-rw-r--r--arch/arm/kernel/head-inflate-data.c62
-rw-r--r--arch/arm/kernel/vmlinux-xip.lds.S8
7 files changed, 172 insertions, 2 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 61a0cb15067e..bf79c461bd2c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2005,6 +2005,17 @@ config XIP_PHYS_ADDR
2005 be linked for and stored to. This address is dependent on your 2005 be linked for and stored to. This address is dependent on your
2006 own flash usage. 2006 own flash usage.
2007 2007
2008config XIP_DEFLATED_DATA
2009 bool "Store kernel .data section compressed in ROM"
2010 depends on XIP_KERNEL
2011 select ZLIB_INFLATE
2012 help
2013 Before the kernel is actually executed, its .data section has to be
2014 copied to RAM from ROM. This option allows for storing that data
2015 in compressed form and decompressed to RAM rather than merely being
2016 copied, saving some precious ROM space. A possible drawback is a
2017 slightly longer boot delay.
2018
2008config KEXEC 2019config KEXEC
2009 bool "Kexec system call (EXPERIMENTAL)" 2020 bool "Kexec system call (EXPERIMENTAL)"
2010 depends on (!SMP || PM_SLEEP_SMP) 2021 depends on (!SMP || PM_SLEEP_SMP)
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
31 31
32ifeq ($(CONFIG_XIP_KERNEL),y) 32ifeq ($(CONFIG_XIP_KERNEL),y)
33 33
34cmd_deflate_xip_data = $(CONFIG_SHELL) -c \
35 '$(srctree)/$(src)/deflate_xip_data.sh $< $@ || { rm -f $@; false; }'
36
37ifeq ($(CONFIG_XIP_DEFLATED_DATA),y)
38quiet_cmd_mkxip = XIPZ $@
39cmd_mkxip = $(cmd_objcopy) && $(cmd_deflate_xip_data)
40else
41quiet_cmd_mkxip = $(quiet_cmd_objcopy)
42cmd_mkxip = $(cmd_objcopy)
43endif
44
34$(obj)/xipImage: vmlinux FORCE 45$(obj)/xipImage: vmlinux FORCE
35 $(call if_changed,objcopy) 46 $(call if_changed,mkxip)
36 @$(kecho) ' Physical Address of xipImage: $(CONFIG_XIP_PHYS_ADDR)' 47 @$(kecho) ' Physical Address of xipImage: $(CONFIG_XIP_PHYS_ADDR)'
37 48
38$(obj)/Image $(obj)/zImage: FORCE 49$(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 @@
1#!/bin/sh
2
3# XIP kernel .data segment compressor
4#
5# Created by: Nicolas Pitre, August 2017
6# Copyright: (C) 2017 Linaro Limited
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11
12# This script locates the start of the .data section in xipImage and
13# substitutes it with a compressed version. The needed offsets are obtained
14# from symbol addresses in vmlinux. It is expected that .data extends to
15# the end of xipImage.
16
17set -e
18
19VMLINUX="$1"
20XIPIMAGE="$2"
21
22DD="dd status=none"
23
24# Use "make V=1" to debug this script.
25case "$KBUILD_VERBOSE" in
26*1*)
27 set -x
28 ;;
29esac
30
31sym_val() {
32 # extract hex value for symbol in $1
33 local val=$($NM "$VMLINUX" | sed -n "/ $1$/{s/ .*$//p;q}")
34 [ "$val" ] || { echo "can't find $1 in $VMLINUX" 1>&2; exit 1; }
35 # convert from hex to decimal
36 echo $((0x$val))
37}
38
39__data_loc=$(sym_val __data_loc)
40_edata_loc=$(sym_val _edata_loc)
41base_offset=$(sym_val _xiprom)
42
43# convert to file based offsets
44data_start=$(($__data_loc - $base_offset))
45data_end=$(($_edata_loc - $base_offset))
46
47# Make sure data occupies the last part of the file.
48file_end=$(stat -c "%s" "$XIPIMAGE")
49if [ "$file_end" != "$data_end" ]; then
50 printf "end of xipImage doesn't match with _edata_loc (%#x vs %#x)\n" \
51 $(($file_end + $base_offset)) $_edata_loc 2>&1
52 exit 1;
53fi
54
55# be ready to clean up
56trap 'rm -f "$XIPIMAGE.tmp"' 0 1 2 3
57
58# substitute the data section by a compressed version
59$DD if="$XIPIMAGE" count=$data_start iflag=count_bytes of="$XIPIMAGE.tmp"
60$DD if="$XIPIMAGE" skip=$data_start iflag=skip_bytes |
61gzip -9 >> "$XIPIMAGE.tmp"
62
63# replace kernel binary
64mv -f "$XIPIMAGE.tmp" "$XIPIMAGE"
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
87obj-$(CONFIG_DEBUG_LL) += debug.o 87obj-$(CONFIG_DEBUG_LL) += debug.o
88obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 88obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
89 89
90# This is executed very early using a temporary stack when no memory allocator
91# nor global data is available. Everything has to be allocated on the stack.
92CFLAGS_head-inflate-data.o := $(call cc-option,-Wframe-larger-than=10240)
93obj-$(CONFIG_XIP_DEFLATED_DATA) += head-inflate-data.o
94
90obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o 95obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o
91AFLAGS_hyp-stub.o :=-Wa,-march=armv7-a 96AFLAGS_hyp-stub.o :=-Wa,-march=armv7-a
92ifeq ($(CONFIG_ARM_PSCI),y) 97ifeq ($(CONFIG_ARM_PSCI),y)
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index bf9c4e38eced..a25027b87a60 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -87,7 +87,14 @@ __mmap_switched:
87 adr r4, __mmap_switched_data 87 adr r4, __mmap_switched_data
88 mov fp, #0 88 mov fp, #0
89 89
90#ifdef CONFIG_XIP_KERNEL 90#if defined(CONFIG_XIP_DEFLATED_DATA)
91 ARM( ldr sp, [r4], #4 )
92 THUMB( ldr sp, [r4] )
93 THUMB( add r4, #4 )
94 bl __inflate_kernel_data @ decompress .data to RAM
95 teq r0, #0
96 bne __error
97#elif defined(CONFIG_XIP_KERNEL)
91 ARM( ldmia r4!, {r0, r1, r2, sp} ) 98 ARM( ldmia r4!, {r0, r1, r2, sp} )
92 THUMB( ldmia r4!, {r0, r1, r2, r3} ) 99 THUMB( ldmia r4!, {r0, r1, r2, r3} )
93 THUMB( mov sp, r3 ) 100 THUMB( mov sp, r3 )
@@ -114,9 +121,11 @@ ENDPROC(__mmap_switched)
114 .type __mmap_switched_data, %object 121 .type __mmap_switched_data, %object
115__mmap_switched_data: 122__mmap_switched_data:
116#ifdef CONFIG_XIP_KERNEL 123#ifdef CONFIG_XIP_KERNEL
124#ifndef CONFIG_XIP_DEFLATED_DATA
117 .long _sdata @ r0 125 .long _sdata @ r0
118 .long __data_loc @ r1 126 .long __data_loc @ r1
119 .long _edata_loc @ r2 127 .long _edata_loc @ r2
128#endif
120 .long __bss_stop @ sp (temporary stack in .bss) 129 .long __bss_stop @ sp (temporary stack in .bss)
121#endif 130#endif
122 131
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 @@
1/*
2 * XIP kernel .data segment decompressor
3 *
4 * Created by: Nicolas Pitre, August 2017
5 * Copyright: (C) 2017 Linaro Limited
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/init.h>
13#include <linux/zutil.h>
14
15/* for struct inflate_state */
16#include "../../../lib/zlib_inflate/inftrees.h"
17#include "../../../lib/zlib_inflate/inflate.h"
18#include "../../../lib/zlib_inflate/infutil.h"
19
20extern char __data_loc[];
21extern char _edata_loc[];
22extern char _sdata[];
23
24/*
25 * This code is called very early during the boot process to decompress
26 * the .data segment stored compressed in ROM. Therefore none of the global
27 * variables are valid yet, hence no kernel services such as memory
28 * allocation is available. Everything must be allocated on the stack and
29 * we must avoid any global data access. We use a temporary stack located
30 * in the .bss area. The linker script makes sure the .bss is big enough
31 * to hold our stack frame plus some room for called functions.
32 *
33 * We mimic the code in lib/decompress_inflate.c to use the smallest work
34 * area possible. And because everything is statically allocated on the
35 * stack then there is no need to clean up before returning.
36 */
37
38int __init __inflate_kernel_data(void)
39{
40 struct z_stream_s stream, *strm = &stream;
41 struct inflate_state state;
42 char *in = __data_loc;
43 int rc;
44
45 /* Check and skip gzip header (assume no filename) */
46 if (in[0] != 0x1f || in[1] != 0x8b || in[2] != 0x08 || in[3] & ~3)
47 return -1;
48 in += 10;
49
50 strm->workspace = &state;
51 strm->next_in = in;
52 strm->avail_in = _edata_loc - __data_loc; /* upper bound */
53 strm->next_out = _sdata;
54 strm->avail_out = _edata_loc - __data_loc;
55 zlib_inflateInit2(strm, -MAX_WBITS);
56 WS(strm)->inflate_state.wsize = 0;
57 WS(strm)->inflate_state.window = NULL;
58 rc = zlib_inflate(strm, Z_FINISH);
59 if (rc == Z_OK || rc == Z_STREAM_END)
60 rc = strm->avail_out; /* should be 0 */
61 return rc;
62}
diff --git a/arch/arm/kernel/vmlinux-xip.lds.S b/arch/arm/kernel/vmlinux-xip.lds.S
index 39b1fb470a0a..7a844310085e 100644
--- a/arch/arm/kernel/vmlinux-xip.lds.S
+++ b/arch/arm/kernel/vmlinux-xip.lds.S
@@ -306,3 +306,11 @@ ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")
306 */ 306 */
307ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & PAGE_MASK) <= PAGE_SIZE, 307ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & PAGE_MASK) <= PAGE_SIZE,
308 "HYP init code too big or misaligned") 308 "HYP init code too big or misaligned")
309
310#ifdef CONFIG_XIP_DEFLATED_DATA
311/*
312 * The .bss is used as a stack area for __inflate_kernel_data() whose stack
313 * frame is 9568 bytes. Make sure it has extra room left.
314 */
315ASSERT((_end - __bss_start) >= 12288, ".bss too small for CONFIG_XIP_DEFLATED_DATA")
316#endif