summaryrefslogtreecommitdiff
path: root/firmware/linuxboot.c
diff options
context:
space:
mode:
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}