ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
unarch_xz.c
Go to the documentation of this file.
1
29#include "unarch_xz.h"
30
31#include "ra8_attributes.h"
32#include "ra8_check.h"
33#include "unarch_xz_pool.h"
34#include "xz_config.h"
35
37static const char* const s_tag_xz = "unarch_xz";
38
39/* The XZ session driver pumps the vendored xz-embedded stream decoder through a
40 * bounded refill/flush loop with fail-closed guards on every return code: the
41 * run and unwrap bodies clear clang-tidy's statement/nesting/cognitive
42 * thresholds while staying within the 60-line NASA Rule 4 gate. Same
43 * disposition as the other decode state machines (ra8_jpeg_sw_encode). */
44
54typedef enum : uint16_t {
55 k_xz_chunk = 512U,
60
68typedef enum : uint8_t {
69 k_xz_magic_b0 = 0xFDU,
70 k_xz_magic_b1 = 0x37U,
71 k_xz_magic_b2 = 0x7AU,
72 k_xz_magic_b3 = 0x58U,
73 k_xz_magic_b4 = 0x5AU,
74 k_xz_magic_b5 = 0x00U,
76
77bool unarch_xz_magic(const uint8_t* sig, size_t sig_len)
78{
79 if (sig == nullptr) {
80 return false;
81 }
82 if (sig_len < (size_t)k_unarch_xz_sig_len) {
83 return false;
84 }
85 const uint8_t want[k_unarch_xz_sig_len] = {
86 (uint8_t)k_xz_magic_b0,
87 (uint8_t)k_xz_magic_b1,
88 (uint8_t)k_xz_magic_b2,
89 (uint8_t)k_xz_magic_b3,
90 (uint8_t)k_xz_magic_b4,
91 (uint8_t)k_xz_magic_b5,
92 };
93 return memeq(sig, want, sizeof(want));
94}
95
96ra8_err_t unarch_xz_stream_begin(unarch_xz_stream_t* xs, void* scratch, uint32_t scratch_len)
97{
98 RA8_CHECK_NULL_PTR(xs, s_tag_xz, "begin: null session");
99 *xs = (unarch_xz_stream_t){};
100 RA8_CHECK_NULL_PTR(scratch, s_tag_xz, "begin: null scratch");
101 if (scratch_len <= (uint32_t)k_unarch_xz_state_reserve) {
103 }
104 const ra8_err_t perr = unarch_xz_pool_install(scratch, scratch_len);
105 if (perr != k_ra8_ok) {
106 return perr;
107 }
108 /* The CRC lookup tables must exist before any other xz_* call; both
109 * initialisers are idempotent, so calling per-session is safe. */
110 xz_crc32_init();
111 xz_crc64_init();
112 const uint32_t dict_max = scratch_len - (uint32_t)k_unarch_xz_state_reserve;
113 xs->dec = xz_dec_init(XZ_PREALLOC, dict_max);
114 if (xs->dec == nullptr) {
116 return k_ra8_err_no_mem;
117 }
118 xs->live = true;
119 return k_ra8_ok;
120}
121
146static ra8_err_t internal_xz_map_err(enum xz_ret ret)
147{
148 if (ret == XZ_MEMLIMIT_ERROR) {
149 return k_ra8_err_no_mem;
150 }
151 if (ret == XZ_FORMAT_ERROR) {
153 }
154 if (ret == XZ_OPTIONS_ERROR) {
156 }
157 if (ret == XZ_DATA_ERROR) {
159 }
160 if (ret == XZ_BUF_ERROR) {
162 }
163 return k_ra8_err_validation_failed; /* GCOVR_EXCL_LINE -- XZ_MEM_ERROR /
164 * XZ_UNSUPPORTED_CHECK are impossible in
165 * this XZ_PREALLOC, no-ANY_CHECK build */
166}
167// NOLINTNEXTLINE(readability-function-size) -- atomic decoder cursor transaction.
169 const uint8_t* in,
170 size_t in_len,
171 size_t* in_used,
172 uint8_t* out,
173 size_t out_cap,
174 size_t* out_used,
175 bool* end)
176{
177 RA8_CHECK_NULL_PTR(xs, s_tag_xz, "run: null session");
178 RA8_CHECK_NULL_PTR(in_used, s_tag_xz, "run: null in_used");
179 RA8_CHECK_NULL_PTR(out, s_tag_xz, "run: null out");
180 RA8_CHECK_NULL_PTR(out_used, s_tag_xz, "run: null out_used");
181 RA8_CHECK_NULL_PTR(end, s_tag_xz, "run: null end");
182 *in_used = 0U;
183 *out_used = 0U;
184 *end = false;
185 if (in_len > 0U) {
186 RA8_CHECK_NULL_PTR(in, s_tag_xz, "run: null in with input");
187 }
188 if (!xs->live) {
190 }
191 struct xz_buf b = {
192 .in = in,
193 .in_pos = 0U,
194 .in_size = in_len,
195 .out = out,
196 .out_pos = 0U,
197 .out_size = out_cap,
198 };
199 const enum xz_ret ret = xz_dec_run(xs->dec, &b);
200 *in_used = b.in_pos;
201 *out_used = b.out_pos;
202 if (ret == XZ_STREAM_END) {
203 *end = true;
204 return k_ra8_ok;
205 }
206 if (ret == XZ_OK) {
207 return k_ra8_ok;
208 }
209 return internal_xz_map_err(ret);
210}
211
213{
214 if (xs == nullptr) {
215 return; /* teardown paths call unconditionally */
216 }
217 if (!xs->live) {
218 return; /* never begun (or already ended): nothing to release */
219 }
220 xz_dec_end(xs->dec); /* frees are no-ops; the pool reset reclaims all */
222 xs->dec = nullptr;
223 xs->live = false;
224}
225
246 const uint8_t* out,
247 const void* scratch,
248 const size_t* out_len)
249{
250 if (read == nullptr) {
251 ra8_log_error(s_tag_xz, "unwrap: null read");
252 return k_ra8_err_null_ptr;
253 }
254 RA8_CHECK_NULL_PTR(out, s_tag_xz, "unwrap: null out");
255 RA8_CHECK_NULL_PTR(scratch, s_tag_xz, "unwrap: null scratch");
256 RA8_CHECK_NULL_PTR(out_len, s_tag_xz, "unwrap: null out_len");
257 return k_ra8_ok;
258}
259
281
308{
310 if (ierr != k_ra8_ok) {
311 return ierr;
312 }
313 const uint64_t remain = st->size - st->in_off;
314 const size_t want = (remain > (uint64_t)k_xz_chunk) ? (size_t)k_xz_chunk : (size_t)remain;
315 size_t got = 0U;
316 if (want > 0U) {
317 got = st->read(st->ctx, st->in_off, st->chunk, want);
318 }
319 if (got > want) {
320 got = want; /* clamp a misbehaving backing to the request */
321 }
322 if (got == 0U) {
323 return k_ra8_err_validation_failed; /* backing truncated under the stream */
324 }
325 const size_t space = st->out_cap - st->total_out;
326 const size_t owin = (space > (size_t)k_xz_out_window) ? (size_t)k_xz_out_window : space;
327 size_t used = 0U;
328 size_t produced = 0U;
329 const ra8_err_t rerr = unarch_xz_stream_run(&st->xs,
330 st->chunk,
331 got,
332 &used,
333 &st->out[st->total_out],
334 owin,
335 &produced,
336 end);
337 st->in_off += (uint64_t)used;
338 st->total_out += produced;
339 if (rerr != k_ra8_ok) {
340 if (rerr == k_ra8_err_validation_failed) {
341 if (st->total_out == st->out_cap) {
342 return k_ra8_err_no_mem; /* stuck only because the arena is full */
343 }
344 }
345 return rerr;
346 }
347 return ra8_decomp_budget_charge_output(&st->budget, st->in_off, (uint64_t)produced);
348}
349
350// NOLINTNEXTLINE(readability-function-size) -- contract macros expand the bounded decode driver.
352 void* ctx,
353 uint64_t size,
354 uint8_t* out,
355 size_t out_cap,
356 void* scratch,
357 uint32_t scratch_len,
358 const ra8_decomp_limits_t* limits,
359 size_t* out_len)
360{
361 const ra8_err_t nerr = internal_unwrap_reject_null(read, out, scratch, out_len);
362 if (nerr != k_ra8_ok) {
363 return nerr;
364 }
365 *out_len = 0U;
366 if (size == 0U) {
368 }
369 if (out_cap == 0U) {
371 }
372 xz_unwrap_state_t st = {};
373 st.read = read;
374 st.ctx = ctx;
375 st.size = size;
376 st.out = out;
377 st.out_cap = out_cap;
378 const ra8_err_t berr = ra8_decomp_budget_init(&st.budget, limits);
379 if (berr != k_ra8_ok) {
380 return berr;
381 }
382 const ra8_err_t serr = unarch_xz_stream_begin(&st.xs, scratch, scratch_len);
383 if (serr != k_ra8_ok) {
384 return serr;
385 }
386 bool end = false;
387 ra8_err_t perr = k_ra8_ok;
388 while (!end) { /* bound: every pass charges the policy iteration budget */
389 perr = internal_unwrap_pass(&st, &end);
390 if (perr != k_ra8_ok) {
391 break;
392 }
393 }
395 if (perr != k_ra8_ok) {
396 return perr;
397 }
398 if (st.in_off < size) {
399 return k_ra8_err_validation_failed; /* trailing bytes after the footer */
400 }
401 *out_len = st.total_out;
402 return k_ra8_ok;
403}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
ra8_err_t ra8_decomp_budget_charge_output(ra8_decomp_budget_t *b, uint64_t in_total, uint64_t out_delta)
Charge decompressed output against the cap and ratio bounds.
ra8_err_t ra8_decomp_budget_charge_iter(ra8_decomp_budget_t *b)
Charge one decode-loop turn against the iteration budget.
ra8_err_t ra8_decomp_budget_init(ra8_decomp_budget_t *b, const ra8_decomp_limits_t *limits)
Bind a budget to a policy (or the default policy) and zero it.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Running consumption tracker charged by a decoder against its policy.
One decompression policy: the five resource bounds decoders enforce.
One multi-call XZ decode session (opaque decoder + liveness flag).
Definition unarch_xz.h:98
struct xz_dec * dec
xz-embedded decoder state (pool-allocated).
Definition unarch_xz.h:99
bool live
Session is begun and not yet ended.
Definition unarch_xz.h:100
Loop-carried state of one unwrap: cursors, session, and budget.
Definition unarch_xz.c:269
size_t out_cap
Arena capacity in bytes.
Definition unarch_xz.c:274
unarch_read_fn read
Byte reader over the stream.
Definition unarch_xz.c:270
void * ctx
Context for read.
Definition unarch_xz.c:271
uint64_t size
Stream length in bytes.
Definition unarch_xz.c:272
ra8_decomp_budget_t budget
Unified decompression budget.
Definition unarch_xz.c:278
uint8_t chunk[k_xz_chunk]
Input refill window (one pass).
Definition unarch_xz.c:279
uint8_t * out
Destination arena.
Definition unarch_xz.c:273
size_t total_out
Output bytes produced so far.
Definition unarch_xz.c:276
unarch_xz_stream_t xs
Live decode session.
Definition unarch_xz.c:277
uint64_t in_off
Input bytes consumed so far.
Definition unarch_xz.c:275
size_t(* unarch_read_fn)(void *ctx, uint64_t offset, void *buf, size_t len)
Seek+read backing over an archive's bytes.
Definition unarch_io.h:55
static ra8_err_t internal_xz_map_err(enum xz_ret ret)
Map an xz-embedded return code onto the wrapper's error space.
Definition unarch_xz.c:146
static ra8_err_t internal_unwrap_pass(xz_unwrap_state_t *st, bool *end)
Run one refill + decode pass of the unwrap loop.
Definition unarch_xz.c:307
static const char *const s_tag_xz
Log tag for XZ-wrapper diagnostics.
Definition unarch_xz.c:37
bool unarch_xz_magic(const uint8_t *sig, size_t sig_len)
Whether sig begins with the XZ stream header magic.
Definition unarch_xz.c:77
static ra8_err_t internal_unwrap_reject_null(unarch_read_fn read, const uint8_t *out, const void *scratch, const size_t *out_len)
Reject any NULL required argument to unarch_xz_unwrap.
Definition unarch_xz.c:245
ra8_err_t unarch_xz_stream_run(unarch_xz_stream_t *xs, const uint8_t *in, size_t in_len, size_t *in_used, uint8_t *out, size_t out_cap, size_t *out_used, bool *end)
Feed one input chunk through a live session, producing output.
Definition unarch_xz.c:168
void unarch_xz_stream_end(unarch_xz_stream_t *xs)
End a session: free the decoder and release the allocation pool.
Definition unarch_xz.c:212
ra8_err_t unarch_xz_unwrap(unarch_read_fn read, void *ctx, uint64_t size, uint8_t *out, size_t out_cap, void *scratch, uint32_t scratch_len, const ra8_decomp_limits_t *limits, size_t *out_len)
Decode one whole XZ stream from a read seam into a caller arena.
Definition unarch_xz.c:351
xz_magic_t
The six XZ stream header magic bytes (FD 37 7A 58 5A 00).
Definition unarch_xz.c:68
@ k_xz_magic_b5
NUL terminator of the magic.
Definition unarch_xz.c:74
@ k_xz_magic_b0
Non-ASCII lead-in byte.
Definition unarch_xz.c:69
@ k_xz_magic_b3
'X'.
Definition unarch_xz.c:72
@ k_xz_magic_b1
'7'.
Definition unarch_xz.c:70
@ k_xz_magic_b2
'z'.
Definition unarch_xz.c:71
@ k_xz_magic_b4
'Z'.
Definition unarch_xz.c:73
xz_wrap_dims_t
Fixed sizes for the unwrap input loop.
Definition unarch_xz.c:54
@ k_xz_chunk
Input refill window per decode pass, bytes.
Definition unarch_xz.c:55
@ k_xz_out_window
Output window per decode pass, bytes; keeps the budget charge granularity (and thus bomb detection) w...
Definition unarch_xz.c:56
ra8_err_t unarch_xz_stream_begin(unarch_xz_stream_t *xs, void *scratch, uint32_t scratch_len)
Begin a multi-call XZ decode session over a caller scratch.
Definition unarch_xz.c:96
Bounded, fail-closed XZ/LZMA2 decoding over the vendored xz-embedded.
@ k_unarch_xz_sig_len
XZ stream header magic length.
Definition unarch_xz.h:72
@ k_unarch_xz_state_reserve
Decoder-state slice of the scratch.
Definition unarch_xz.h:71
Caller-owned bump arena backing xz-embedded's allocator seam.
void unarch_xz_pool_reset(void)
Release the installed arena (invalidates every pool allocation).
ra8_err_t unarch_xz_pool_install(void *base, uint32_t len)
Install a caller-owned scratch buffer as the XZ allocation arena.
First-party porting header for the vendored xz-embedded decoder.
#define memeq(a, b, size)
Byte-equality shim the SOUP uses for magic/footer comparison.
Definition xz_config.h:135