summaryrefslogtreecommitdiff
path: root/firmware/linuxboot.c
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 /firmware/linuxboot.c
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
Diffstat (limited to 'firmware/linuxboot.c')
-rw-r--r--firmware/linuxboot.c218
1 files changed, 218 insertions, 0 deletions
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}