summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-02-21 17:52:58 +0000
committerAidan MacDonald <amachronic@protonmail.com>2022-03-04 09:02:56 -0500
commitee87bfb933cdb595718bd8ddadf6552c3fa8895d (patch)
tree71fae89c44f6e6544c559555247103d46083aba5
parentd541a3a1919f90b8b6dc8e649d586df7e407cb54 (diff)
downloadrockbox-ee87bfb933cdb595718bd8ddadf6552c3fa8895d.tar.gz
rockbox-ee87bfb933cdb595718bd8ddadf6552c3fa8895d.zip
Add support code for dealing with U-Boot uImages
Adds a loader for the legacy uImage format that is commonly used on embedded Linux systems. It verifies checksums and supports uncompressed and gzipped images. Supports arbitrary reader functions to allow the images to be streamed off any storage device, for optimal RAM use. Change-Id: I93c35f9a6f323999a22a07300e05627fabfcbd2c
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/export/linuxboot.h189
-rw-r--r--firmware/linuxboot.c218
3 files changed, 408 insertions, 1 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 4ea922af1b..2e2f13bbe9 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -33,6 +33,7 @@ logf.c
33#endif /* ROCKBOX_HAS_LOGF */ 33#endif /* ROCKBOX_HAS_LOGF */
34#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 34#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
35load_code.c 35load_code.c
36linuxboot.c
36#ifdef RB_PROFILE 37#ifdef RB_PROFILE
37profile.c 38profile.c
38#endif /* RB_PROFILE */ 39#endif /* RB_PROFILE */
@@ -46,7 +47,6 @@ timer.c
46debug.c 47debug.c
47#endif /* PLATFORM_NATIVE */ 48#endif /* PLATFORM_NATIVE */
48panic.c 49panic.c
49
50#if (CONFIG_PLATFORM & PLATFORM_HOSTED) && defined(BOOTFILE) 50#if (CONFIG_PLATFORM & PLATFORM_HOSTED) && defined(BOOTFILE)
51target/hosted/rolo.c 51target/hosted/rolo.c
52#endif 52#endif
diff --git a/firmware/export/linuxboot.h b/firmware/export/linuxboot.h
new file mode 100644
index 0000000000..7dbc213012
--- /dev/null
+++ b/firmware/export/linuxboot.h
@@ -0,0 +1,189 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2022 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __LINUXBOOT_H__
23#define __LINUXBOOT_H__
24
25#include "system.h"
26#include <stddef.h>
27#include <sys/types.h>
28
29/*
30 * From u-boot's include/image.h
31 * SPDX-License-Identifier: GPL-2.0+
32 * (C) Copyright 2008 Semihalf
33 * (C) Copyright 2000-2005
34 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
35 */
36
37#define IH_MAGIC 0x27051956
38#define IH_NMLEN 32
39
40enum
41{
42 IH_ARCH_INVALID,
43 IH_ARCH_ALPHA,
44 IH_ARCH_ARM,
45 IH_ARCH_I386,
46 IH_ARCH_IA64,
47 IH_ARCH_MIPS,
48 IH_ARCH_MIPS64,
49 IH_ARCH_PPC,
50 IH_ARCH_S390,
51 IH_ARCH_SH,
52 IH_ARCH_SPARC,
53 IH_ARCH_SPARC64,
54 IH_ARCH_M68K,
55 /* NOTE: Other archs not relevant and omitted here, can add if needed */
56 IH_ARCH_COUNT,
57};
58
59enum
60{
61 IH_TYPE_INVALID = 0,
62 IN_TYPE_STANDALONE,
63 IH_TYPE_KERNEL,
64 IH_TYPE_RAMDISK,
65 IH_TYPE_MULTI,
66 IH_TYPE_FIRMWARE,
67 IH_TYPE_SCRIPT,
68 IH_TYPE_FILESYSTEM,
69 IH_TYPE_FLATDT,
70 /* NOTE: Other types not relevant and omitted here, can add if needed */
71 IH_TYPE_COUNT,
72};
73
74enum
75{
76 IH_COMP_NONE = 0,
77 IH_COMP_GZIP,
78 IH_COMP_BZIP2,
79 IH_COMP_LZMA,
80 IH_COMP_LZO,
81 IH_COMP_LZ4,
82 IH_COMP_ZSTD,
83 IH_COMP_COUNT,
84};
85
86/* Legacy U-Boot image header as produced by mkimage(1).
87 *
88 * WARNING: all fields are big-endian so you usually do not want to
89 * access them directly, use the accessor functions instead.
90 */
91struct uimage_header
92{
93 uint32_t ih_magic;
94 uint32_t ih_hcrc;
95 uint32_t ih_time;
96 uint32_t ih_size;
97 uint32_t ih_load;
98 uint32_t ih_ep;
99 uint32_t ih_dcrc;
100 uint8_t ih_os;
101 uint8_t ih_arch;
102 uint8_t ih_type;
103 uint8_t ih_comp;
104 uint8_t ih_name[IH_NMLEN];
105};
106
107#define _uimage_get32_f(name) \
108 static inline uint32_t uimage_get_##name(const struct uimage_header* uh) \
109 { return betoh32(uh->ih_##name); }
110_uimage_get32_f(magic)
111_uimage_get32_f(hcrc)
112_uimage_get32_f(time)
113_uimage_get32_f(size)
114_uimage_get32_f(load)
115_uimage_get32_f(ep)
116_uimage_get32_f(dcrc)
117#undef _uimage_get32_f
118
119#define _uimage_set32_f(name) \
120 static inline void uimage_set_##name(struct uimage_header* uh, uint32_t val) \
121 { uh->ih_##name = htobe32(val); }
122_uimage_set32_f(magic)
123_uimage_set32_f(hcrc)
124_uimage_set32_f(time)
125_uimage_set32_f(size)
126_uimage_set32_f(load)
127_uimage_set32_f(ep)
128_uimage_set32_f(dcrc)
129#undef _uimage_set32_f
130
131#define _uimage_get8_f(name) \
132 static inline uint8_t uimage_get_##name(const struct uimage_header* uh) \
133 { return uh->ih_##name; }
134_uimage_get8_f(os)
135_uimage_get8_f(arch)
136_uimage_get8_f(type)
137_uimage_get8_f(comp)
138#undef _uimage_get8_f
139
140#define _uimage_set8_f(name) \
141 static inline void uimage_set_##name(struct uimage_header* uh, uint8_t val) \
142 { uh->ih_##name = val; }
143_uimage_set8_f(os)
144_uimage_set8_f(arch)
145_uimage_set8_f(type)
146_uimage_set8_f(comp)
147#undef _uimage_set8_f
148
149/*
150 * uImage utilities
151 */
152
153/** Reader callback for use with `uimage_load`
154 *
155 * \param buf Buffer to write into
156 * \param size Number of bytes to read
157 * \param ctx State argument
158 * \return Number of bytes actually read, or -1 on error.
159 */
160typedef ssize_t(*uimage_reader)(void* buf, size_t size, void* ctx);
161
162/** Calculate U-Boot style CRC */
163uint32_t uimage_crc(uint32_t crc, const void* data, size_t size);
164
165/** Calculate CRC of a uImage header */
166uint32_t uimage_calc_hcrc(const struct uimage_header* uh);
167
168/** Load and decompress a uImage
169 *
170 * \param uh Returned header struct (will be filled with read data)
171 * \param out_size Returned size of the decompressed data
172 * \param reader Data reader function
173 * \param rctx Context argument for the reader function
174 * \return Buflib handle containing the decompressed data, or negative on error
175 *
176 * This function will read a uImage, verify checksums and decompress the image
177 * data into a non-moveable buflib allocation. The length of the compressed
178 * data will be taken from the uImage header.
179 */
180int uimage_load(struct uimage_header* uh, size_t* out_size,
181 uimage_reader reader, void* rctx);
182
183/** File reader for use with `uimage_load`
184 *
185 * \param ctx File descriptor, casted to `void*`
186 */
187ssize_t uimage_fd_reader(void* buf, size_t size, void* ctx);
188
189#endif /* __LINUXBOOT_H__ */
diff --git a/firmware/linuxboot.c b/firmware/linuxboot.c
new file mode 100644
index 0000000000..5b6ab314b3
--- /dev/null
+++ b/firmware/linuxboot.c
@@ -0,0 +1,218 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2022 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "linuxboot.h"
23#include "system.h"
24#include "core_alloc.h"
25#include "crc32.h"
26#include "inflate.h"
27#include "file.h"
28#include <string.h>
29
30/* compression support options - can be decided per target if needed,
31 * for now default to enabling everything */
32#define HAVE_UIMAGE_COMP_NONE
33#define HAVE_UIMAGE_COMP_GZIP
34
35uint32_t uimage_crc(uint32_t crc, const void* data, size_t size)
36{
37 /* is this endian swapping required...? */
38 return letoh32(crc_32r(data, size, htole32(crc ^ 0xffffffff))) ^ 0xffffffff;
39}
40
41uint32_t uimage_calc_hcrc(const struct uimage_header* uh)
42{
43 struct uimage_header h = *uh;
44 uimage_set_hcrc(&h, 0);
45 return uimage_crc(0, &h, sizeof(h));
46}
47
48static int uimage_check_header(const struct uimage_header* uh)
49{
50 if(uimage_get_magic(uh) != IH_MAGIC)
51 return -1;
52
53 if(uimage_get_hcrc(uh) != uimage_calc_hcrc(uh))
54 return -2;
55
56 return 0;
57}
58
59static int uimage_alloc_state(const struct uimage_header* uh)
60{
61 size_t size;
62
63 switch(uimage_get_comp(uh)) {
64#ifdef HAVE_UIMAGE_COMP_NONE
65 case IH_COMP_NONE:
66 return 0;
67#endif
68
69#ifdef HAVE_UIMAGE_COMP_GZIP
70 case IH_COMP_GZIP:
71 size = inflate_size + inflate_align - 1;
72 return core_alloc_ex("inflate", size, &buflib_ops_locked);
73#endif
74
75 default:
76 return -1;
77 }
78}
79
80#ifdef HAVE_UIMAGE_COMP_GZIP
81struct uimage_inflatectx
82{
83 uimage_reader reader;
84 void* rctx;
85 uint32_t dcrc;
86 size_t remain;
87};
88
89static uint32_t uimage_inflate_reader(void* block, uint32_t block_size, void* ctx)
90{
91 struct uimage_inflatectx* c = ctx;
92 ssize_t len = c->reader(block, block_size, c->rctx);
93 if(len > 0) {
94 len = MIN(c->remain, (size_t)len);
95 c->remain -= len;
96 c->dcrc = uimage_crc(c->dcrc, block, len);
97 }
98
99 return len;
100}
101
102static int uimage_decompress_gzip(const struct uimage_header* uh, int state_h,
103 void* out, size_t* out_size,
104 uimage_reader reader, void* rctx)
105{
106 size_t hbufsz = inflate_size + inflate_align - 1;
107 void* hbuf = core_get_data(state_h);
108 ALIGN_BUFFER(hbuf, hbufsz, inflate_align);
109
110 struct uimage_inflatectx r_ctx;
111 r_ctx.reader = reader;
112 r_ctx.rctx = rctx;
113 r_ctx.dcrc = 0;
114 r_ctx.remain = uimage_get_size(uh);
115
116 struct inflate_bufferctx w_ctx;
117 w_ctx.buf = out;
118 w_ctx.end = out + *out_size;
119
120 int ret = inflate(hbuf, INFLATE_GZIP,
121 uimage_inflate_reader, &r_ctx,
122 inflate_buffer_writer, &w_ctx);
123 if(ret)
124 return ret;
125
126 if(r_ctx.remain > 0)
127 return -1;
128 if(r_ctx.dcrc != uimage_get_dcrc(uh))
129 return -2;
130
131 *out_size = w_ctx.end - w_ctx.buf;
132 return 0;
133}
134#endif /* HAVE_UIMAGE_COMP_GZIP */
135
136static int uimage_decompress(const struct uimage_header* uh, int state_h,
137 void* out, size_t* out_size,
138 uimage_reader reader, void* rctx)
139{
140 size_t in_size = uimage_get_size(uh);
141 ssize_t len;
142
143 switch(uimage_get_comp(uh)) {
144#ifdef HAVE_UIMAGE_COMP_NONE
145 case IH_COMP_NONE:
146 if(*out_size < in_size)
147 return -2;
148
149 len = reader(out, in_size, rctx);
150 if(len < 0 || (size_t)len != in_size)
151 return -3;
152
153 if(uimage_crc(0, out, in_size) != uimage_get_dcrc(uh))
154 return -4;
155
156 *out_size = in_size;
157 break;
158#endif
159
160#ifdef HAVE_UIMAGE_COMP_GZIP
161 case IH_COMP_GZIP:
162 return uimage_decompress_gzip(uh, state_h, out, out_size, reader, rctx);
163#endif
164
165 default:
166 return -1;
167 }
168
169 return 0;
170}
171
172int uimage_load(struct uimage_header* uh, size_t* out_size,
173 uimage_reader reader, void* rctx)
174{
175 if(reader(uh, sizeof(*uh), rctx) != (ssize_t)sizeof(*uh))
176 return -1; /* read error */
177
178 int ret = uimage_check_header(uh);
179 if(ret)
180 return ret;
181
182 int state_h = uimage_alloc_state(uh);
183 if(state_h < 0)
184 return state_h;
185
186 *out_size = 0;
187 int out_h = core_alloc_maximum("uimage", out_size, &buflib_ops_locked);
188 if(out_h <= 0) {
189 ret = -1;
190 goto err;
191 }
192
193 ret = uimage_decompress(uh, state_h, core_get_data(out_h), out_size,
194 reader, rctx);
195 if(ret)
196 goto err;
197
198 core_shrink(out_h, core_get_data(out_h), *out_size);
199 ret = 0;
200
201 err:
202 if(state_h > 0)
203 core_free(state_h);
204 if(out_h > 0) {
205 if(ret == 0)
206 ret = out_h;
207 else
208 core_free(out_h);
209 }
210
211 return ret;
212}
213
214ssize_t uimage_fd_reader(void* buf, size_t size, void* ctx)
215{
216 int fd = (intptr_t)ctx;
217 return read(fd, buf, size);
218}