aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/boot
diff options
context:
space:
mode:
authorOliver O'Halloran <oohall@gmail.com>2016-09-22 02:54:31 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2016-09-28 00:31:43 -0400
commit1b7898ee276b39e54d870dc4ef3374f663d0b426 (patch)
tree1c41ed5b37a1214816eb732c7d75cd4326e82deb /arch/powerpc/boot
parent22750d98b00bd704694224d6df69a1a6f29ea22c (diff)
powerpc/boot: Use the pre-boot decompression API
Currently the powerpc boot wrapper has its own wrapper around zlib to handle decompressing gzipped kernels. The kernel decompressor library functions now provide a generic interface that can be used in the pre-boot environment. This allows boot wrappers to easily support different compression algorithms. This patch converts the wrapper to use this new API, but does not add support for using new algorithms. Signed-off-by: Oliver O'Halloran <oohall@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch/powerpc/boot')
-rw-r--r--arch/powerpc/boot/Makefile34
-rw-r--r--arch/powerpc/boot/decompress.c143
-rw-r--r--arch/powerpc/boot/main.c35
-rw-r--r--arch/powerpc/boot/ops.h3
4 files changed, 190 insertions, 25 deletions
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index bede555d78cf..861348c72519 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -63,13 +63,28 @@ $(obj)/treeboot-currituck.o: BOOTCFLAGS += -mcpu=405
63$(obj)/treeboot-akebono.o: BOOTCFLAGS += -mcpu=405 63$(obj)/treeboot-akebono.o: BOOTCFLAGS += -mcpu=405
64$(obj)/virtex405-head.o: BOOTAFLAGS += -mcpu=405 64$(obj)/virtex405-head.o: BOOTAFLAGS += -mcpu=405
65 65
66# the kernel's version of zlib pulls in a lot of other kernel headers 66# The pre-boot decompressors pull in a lot of kernel headers and other source
67# which we don't provide inside the wrapper. 67# files. This creates a bit of a dependency headache since we need to copy
68# these files into the build dir, fix up any includes and ensure that dependent
69# files are copied in the right order.
70
71# these need to be seperate variables because they are copied out of different
72# directories in the kernel tree. Sure you COULd merge them, but it's a
73# cure-is-worse-than-disease situation.
74zlib-decomp-$(CONFIG_KERNEL_GZIP) := decompress_inflate.c
68zlib-$(CONFIG_KERNEL_GZIP) := inffast.c inflate.c inftrees.c 75zlib-$(CONFIG_KERNEL_GZIP) := inffast.c inflate.c inftrees.c
69zlibheader-$(CONFIG_KERNEL_GZIP) := inffast.h inffixed.h inflate.h inftrees.h infutil.h 76zlibheader-$(CONFIG_KERNEL_GZIP) := inffast.h inffixed.h inflate.h inftrees.h infutil.h
70zliblinuxheader-$(CONFIG_KERNEL_GZIP) := zlib.h zconf.h zutil.h 77zliblinuxheader-$(CONFIG_KERNEL_GZIP) := zlib.h zconf.h zutil.h
71 78
72$(addprefix $(obj)/,$(zlib-y) cuboot-c2k.o gunzip_util.o main.o): \ 79$(addprefix $(obj)/, decompress.o): \
80 $(addprefix $(obj)/,$(zlib-decomp-y))
81
82$(addprefix $(obj)/, $(zlib-decomp-y)): \
83 $(addprefix $(obj)/,$(zliblinuxheader-y)) \
84 $(addprefix $(obj)/,$(zlibheader-y)) \
85 $(addprefix $(obj)/,$(zlib-y))
86
87$(addprefix $(obj)/,$(zlib-y)): \
73 $(addprefix $(obj)/,$(zliblinuxheader-y)) \ 88 $(addprefix $(obj)/,$(zliblinuxheader-y)) \
74 $(addprefix $(obj)/,$(zlibheader-y)) 89 $(addprefix $(obj)/,$(zlibheader-y))
75 90
@@ -79,10 +94,10 @@ libfdtheader := fdt.h libfdt.h libfdt_internal.h
79$(addprefix $(obj)/,$(libfdt) libfdt-wrapper.o simpleboot.o epapr.o opal.o): \ 94$(addprefix $(obj)/,$(libfdt) libfdt-wrapper.o simpleboot.o epapr.o opal.o): \
80 $(addprefix $(obj)/,$(libfdtheader)) 95 $(addprefix $(obj)/,$(libfdtheader))
81 96
82src-wlib-y := string.S crt0.S crtsavres.S stdio.c main.c \ 97src-wlib-y := string.S crt0.S crtsavres.S stdio.c decompress.c main.c \
83 $(libfdt) libfdt-wrapper.c \ 98 $(libfdt) libfdt-wrapper.c \
84 ns16550.c serial.c simple_alloc.c div64.S util.S \ 99 ns16550.c serial.c simple_alloc.c div64.S util.S \
85 gunzip_util.c elf_util.c $(zlib-y) devtree.c stdlib.c \ 100 elf_util.c $(zlib-y) devtree.c stdlib.c \
86 oflib.c ofconsole.c cuboot.c mpsc.c cpm-serial.c \ 101 oflib.c ofconsole.c cuboot.c mpsc.c cpm-serial.c \
87 uartlite.c mpc52xx-psc.c opal.c opal-calls.S 102 uartlite.c mpc52xx-psc.c opal.c opal-calls.S
88src-wlib-$(CONFIG_40x) += 4xx.c planetcore.c 103src-wlib-$(CONFIG_40x) += 4xx.c planetcore.c
@@ -143,6 +158,9 @@ $(addprefix $(obj)/,$(zlibheader-y)): $(obj)/%: $(srctree)/lib/zlib_inflate/%
143$(addprefix $(obj)/,$(zliblinuxheader-y)): $(obj)/%: $(srctree)/include/linux/% 158$(addprefix $(obj)/,$(zliblinuxheader-y)): $(obj)/%: $(srctree)/include/linux/%
144 $(call cmd,copy_kern_src) 159 $(call cmd,copy_kern_src)
145 160
161$(addprefix $(obj)/,$(zlib-decomp-y)): $(obj)/%: $(srctree)/lib/%
162 $(call cmd,copy_kern_src)
163
146quiet_cmd_copy_libfdt = COPY $@ 164quiet_cmd_copy_libfdt = COPY $@
147 cmd_copy_libfdt = cp $< $@ 165 cmd_copy_libfdt = cp $< $@
148 166
@@ -160,7 +178,7 @@ $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds : $(obj)/%: $(srctree)/$(src)/%.S
160 $(Q)cp $< $@ 178 $(Q)cp $< $@
161 179
162clean-files := $(zlib-) $(zlibheader-) $(zliblinuxheader-) \ 180clean-files := $(zlib-) $(zlibheader-) $(zliblinuxheader-) \
163 $(libfdt) $(libfdtheader) \ 181 $(zlib-decomp-) $(libfdt) $(libfdtheader) \
164 empty.c zImage.coff.lds zImage.ps3.lds zImage.lds 182 empty.c zImage.coff.lds zImage.ps3.lds zImage.lds
165 183
166quiet_cmd_bootcc = BOOTCC $@ 184quiet_cmd_bootcc = BOOTCC $@
@@ -410,8 +428,8 @@ clean-files += $(image-) $(initrd-) cuImage.* dtbImage.* treeImage.* \
410 zImage.maple simpleImage.* otheros.bld *.dtb 428 zImage.maple simpleImage.* otheros.bld *.dtb
411 429
412# clean up files cached by wrapper 430# clean up files cached by wrapper
413clean-kernel := vmlinux.strip vmlinux.bin 431clean-kernel-base := vmlinux.strip vmlinux.bin
414clean-kernel += $(addsuffix .gz,$(clean-kernel)) 432clean-kernel := $(addsuffix .gz,$(clean-kernel-base))
415# If not absolute clean-files are relative to $(obj). 433# If not absolute clean-files are relative to $(obj).
416clean-files += $(addprefix $(objtree)/, $(clean-kernel)) 434clean-files += $(addprefix $(objtree)/, $(clean-kernel))
417 435
diff --git a/arch/powerpc/boot/decompress.c b/arch/powerpc/boot/decompress.c
new file mode 100644
index 000000000000..3b414d425f16
--- /dev/null
+++ b/arch/powerpc/boot/decompress.c
@@ -0,0 +1,143 @@
1/*
2 * Wrapper around the kernel's pre-boot decompression library.
3 *
4 * Copyright (C) IBM Corporation 2016.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include "elf.h"
13#include "page.h"
14#include "string.h"
15#include "stdio.h"
16#include "ops.h"
17#include "reg.h"
18#include "types.h"
19
20/*
21 * The decompressor_*.c files play #ifdef games so they can be used in both
22 * pre-boot and regular kernel code. We need these definitions to make the
23 * includes work.
24 */
25
26#define STATIC static
27#define INIT
28#define __always_inline inline
29
30/*
31 * The build process will copy the required zlib source files and headers
32 * out of lib/ and "fix" the includes so they do not pull in other kernel
33 * headers.
34 */
35
36#ifdef CONFIG_KERNEL_GZIP
37# include "decompress_inflate.c"
38#endif
39
40/* globals for tracking the state of the decompression */
41static unsigned long decompressed_bytes;
42static unsigned long limit;
43static unsigned long skip;
44static char *output_buffer;
45
46/*
47 * flush() is called by __decompress() when the decompressor's scratch buffer is
48 * full.
49 */
50static long flush(void *v, unsigned long buffer_size)
51{
52 unsigned long end = decompressed_bytes + buffer_size;
53 unsigned long size = buffer_size;
54 unsigned long offset = 0;
55 char *in = v;
56 char *out;
57
58 /*
59 * if we hit our decompression limit, we need to fake an error to abort
60 * the in-progress decompression.
61 */
62 if (decompressed_bytes >= limit)
63 return -1;
64
65 /* skip this entire block */
66 if (end <= skip) {
67 decompressed_bytes += buffer_size;
68 return buffer_size;
69 }
70
71 /* skip some data at the start, but keep the rest of the block */
72 if (decompressed_bytes < skip && end > skip) {
73 offset = skip - decompressed_bytes;
74
75 in += offset;
76 size -= offset;
77 decompressed_bytes += offset;
78 }
79
80 out = &output_buffer[decompressed_bytes - skip];
81 size = min(decompressed_bytes + size, limit) - decompressed_bytes;
82
83 memcpy(out, in, size);
84 decompressed_bytes += size;
85
86 return buffer_size;
87}
88
89static void print_err(char *s)
90{
91 /* suppress the "error" when we terminate the decompressor */
92 if (decompressed_bytes >= limit)
93 return;
94
95 printf("Decompression error: '%s'\n\r", s);
96}
97
98/**
99 * partial_decompress - decompresses part or all of a compressed buffer
100 * @inbuf: input buffer
101 * @input_size: length of the input buffer
102 * @outbuf: input buffer
103 * @output_size: length of the input buffer
104 * @skip number of output bytes to ignore
105 *
106 * This function takes compressed data from inbuf, decompresses and write it to
107 * outbuf. Once output_size bytes are written to the output buffer, or the
108 * stream is exhausted the function will return the number of bytes that were
109 * decompressed. Otherwise it will return whatever error code the decompressor
110 * reported (NB: This is specific to each decompressor type).
111 *
112 * The skip functionality is mainly there so the program and discover
113 * the size of the compressed image so that it can ask firmware (if present)
114 * for an appropriately sized buffer.
115 */
116long partial_decompress(void *inbuf, unsigned long input_size,
117 void *outbuf, unsigned long output_size, unsigned long _skip)
118{
119 int ret;
120
121 /*
122 * The skipped bytes needs to be included in the size of data we want
123 * to decompress.
124 */
125 output_size += _skip;
126
127 decompressed_bytes = 0;
128 output_buffer = outbuf;
129 limit = output_size;
130 skip = _skip;
131
132 ret = __decompress(inbuf, input_size, NULL, flush, outbuf,
133 output_size, NULL, print_err);
134
135 /*
136 * If decompression was aborted due to an actual error rather than
137 * a fake error that we used to abort, then we should report it.
138 */
139 if (decompressed_bytes < limit)
140 return ret;
141
142 return decompressed_bytes - skip;
143}
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index d80161b633f4..f7a184b6c35b 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -15,11 +15,8 @@
15#include "string.h" 15#include "string.h"
16#include "stdio.h" 16#include "stdio.h"
17#include "ops.h" 17#include "ops.h"
18#include "gunzip_util.h"
19#include "reg.h" 18#include "reg.h"
20 19
21static struct gunzip_state gzstate;
22
23struct addr_range { 20struct addr_range {
24 void *addr; 21 void *addr;
25 unsigned long size; 22 unsigned long size;
@@ -30,15 +27,14 @@ struct addr_range {
30static struct addr_range prep_kernel(void) 27static struct addr_range prep_kernel(void)
31{ 28{
32 char elfheader[256]; 29 char elfheader[256];
33 void *vmlinuz_addr = _vmlinux_start; 30 unsigned char *vmlinuz_addr = (unsigned char *)_vmlinux_start;
34 unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start; 31 unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start;
35 void *addr = 0; 32 void *addr = 0;
36 struct elf_info ei; 33 struct elf_info ei;
37 int len; 34 long len;
38 35
39 /* gunzip the ELF header of the kernel */ 36 partial_decompress(vmlinuz_addr, vmlinuz_size,
40 gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size); 37 elfheader, sizeof(elfheader), 0);
41 gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
42 38
43 if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) 39 if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei))
44 fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); 40 fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
@@ -51,7 +47,7 @@ static struct addr_range prep_kernel(void)
51 * the kernel bss must be claimed (it will be zero'd by the 47 * the kernel bss must be claimed (it will be zero'd by the
52 * kernel itself) 48 * kernel itself)
53 */ 49 */
54 printf("Allocating 0x%lx bytes for kernel ...\n\r", ei.memsize); 50 printf("Allocating 0x%lx bytes for kernel...\n\r", ei.memsize);
55 51
56 if (platform_ops.vmlinux_alloc) { 52 if (platform_ops.vmlinux_alloc) {
57 addr = platform_ops.vmlinux_alloc(ei.memsize); 53 addr = platform_ops.vmlinux_alloc(ei.memsize);
@@ -71,16 +67,21 @@ static struct addr_range prep_kernel(void)
71 "device tree\n\r"); 67 "device tree\n\r");
72 } 68 }
73 69
74 /* Finally, gunzip the kernel */ 70 /* Finally, decompress the kernel */
75 printf("gunzipping (0x%p <- 0x%p:0x%p)...", addr, 71 printf("Decompressing (0x%p <- 0x%p:0x%p)...\n\r", addr,
76 vmlinuz_addr, vmlinuz_addr+vmlinuz_size); 72 vmlinuz_addr, vmlinuz_addr+vmlinuz_size);
77 /* discard up to the actual load data */ 73
78 gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader)); 74 len = partial_decompress(vmlinuz_addr, vmlinuz_size,
79 len = gunzip_finish(&gzstate, addr, ei.loadsize); 75 addr, ei.loadsize, ei.elfoffset);
76
77 if (len < 0)
78 fatal("Decompression failed with error code %ld\n\r", len);
79
80 if (len != ei.loadsize) 80 if (len != ei.loadsize)
81 fatal("ran out of data! only got 0x%x of 0x%lx bytes.\n\r", 81 fatal("Decompression error: got 0x%lx bytes, expected 0x%lx.\n\r",
82 len, ei.loadsize); 82 len, ei.loadsize);
83 printf("done 0x%x bytes\n\r", len); 83
84 printf("Done! Decompressed 0x%lx bytes\n\r", len);
84 85
85 flush_cache(addr, ei.loadsize); 86 flush_cache(addr, ei.loadsize);
86 87
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h
index e19b64ef977a..309d1b127e96 100644
--- a/arch/powerpc/boot/ops.h
+++ b/arch/powerpc/boot/ops.h
@@ -260,4 +260,7 @@ int __ilog2_u32(u32 n)
260 return 31 - bit; 260 return 31 - bit;
261} 261}
262 262
263long partial_decompress(void *inbuf, unsigned long input_size, void *outbuf,
264 unsigned long output_size, unsigned long skip);
265
263#endif /* _PPC_BOOT_OPS_H_ */ 266#endif /* _PPC_BOOT_OPS_H_ */