xref: /freebsd/stand/efi/loader/decompress.c (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
1 /*
2  * Copyright (c) 2026 Netflix, Inc. Written by Warner Losh
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include "stand.h"
8 #include "loader_efi.h"
9 #include <efilib.h>
10 #include "decompress.h"
11 
12 #include <zlib.h>
13 #include <bzlib.h>
14 #include <xz.h>
15 #ifdef LOADER_ZFS_SUPPORT	/* ZSTD and lzma only available with ZFS */
16 #include <zstd.h>
17 #endif
18 #include <sys/_param.h>
19 
20 #define ULL(x) ((unsigned long long)(x))
21 
22 /*
23  * While memdisk_uefi.efi used EfiReservedMemoryType, we need to use
24  * EfiLoaderData instead, the standard memory type the loader uses to pass
25  * memory to the kernel. This is also safe if we're chain booting to another efi
26  * progarm since the memory will be available to that program. The other type
27  * isn't necessarily in the pmap, so the md driver fails.
28  */
29 static EFI_MEMORY_TYPE mem_type = EfiLoaderData;
30 
31 struct decomp_state
32 {
33 	enum compression type;
34 	size_t size;		/* Best guess at final size */
35 	size_t alloc_size;	/* Current size of `buf` */
36 	size_t pages;		/* Alloc_size in pages */
37 	uint8_t *buf_cur;	/* Current output buffer */
38 	uint8_t *buf_end;	/* Current end of allocated buffer */
39 	EFI_PHYSICAL_ADDRESS buf; /* Decompression buffer */
40 	union {
41 		z_stream zstrm;
42 		bz_stream bzstrm;
43 		struct xz_dec *xzstrm;
44 #ifdef LOADER_ZFS_SUPPORT
45 		ZSTD_DStream *zstdstrm;
46 #endif
47 	};
48 	EFI_STATUS (*init)(decomp_state *dctx, uint8_t *first_buf, size_t buflen,
49 	    size_t size_hint);
50 	enum step_return (*step)(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset);
51 	void (*fini)(decomp_state *dctx, bool flush);
52 };
53 
54 static enum compression
55 what_compressed(uint8_t *buf, size_t len)
56 {
57 	/*
58 	 * Failsafe
59 	 */
60 	if (len < 32)
61 		return (none);
62 	if (memcmp(buf, "\x1f\x8b", 2) == 0) {
63 		printf("GZIP\n");
64 		return (zlib);
65 	}
66 	if (memcmp(buf, "BZh", 3) == 0) {
67 		printf("BZIP2\n");
68 		return (bzip2);
69 	}
70 	if (memcmp(buf, "\x28\xb5\x2f\xfd", 4) == 0) {
71 		printf("zstd\n");
72 		return (zstd);
73 	}
74 	if (memcmp(buf, "\xfd""7zXZ\x00", 6) == 0) {
75 		printf("xz\n");
76 		return (xz);
77 	}
78 	printf("Not compressed\n");
79 	return (none);
80 }
81 
82 static EFI_STATUS
83 alloc_buffer(decomp_state *dctx, size_t size)
84 {
85 	dctx->alloc_size = roundup2(size, EFI_PAGE_SIZE);
86 	dctx->pages = dctx->alloc_size / EFI_PAGE_SIZE;
87 	EFI_STATUS status = BS->AllocatePages(AllocateAnyPages, mem_type, dctx->pages, &dctx->buf);
88 	if (EFI_ERROR(status)) {
89 		printf("Failed to allocate memory for %llu bytes\n", ULL(dctx->alloc_size));
90 		return (status);
91 	}
92 	BS->SetMem((void *)(uintptr_t)dctx->buf, dctx->alloc_size, 0);
93 	dctx->buf_cur = (uint8_t *)(uintptr_t)dctx->buf;
94 	dctx->buf_end = (uint8_t *)(uintptr_t)dctx->buf + dctx->alloc_size;
95 	return (EFI_SUCCESS);
96 }
97 
98 static EFI_STATUS
99 grow_buffer(decomp_state *dctx)
100 {
101 	/*
102 	 * a 1.5 exp growth trades a few more copies for a little less waste.
103 	 */
104 	size_t newsz = roundup2(dctx->alloc_size * 3 / 2, EFI_PAGE_SIZE);
105 	size_t newpages = newsz / EFI_PAGE_SIZE;
106 	EFI_PHYSICAL_ADDRESS newbuf;
107 	EFI_STATUS status = BS->AllocatePages(AllocateAnyPages, mem_type, newpages, &newbuf);
108 	if (EFI_ERROR(status)) {
109 		printf("Failed to allocate memory for %llu bytes\n", ULL(newsz));
110 		return (status);
111 	}
112 	memcpy((void *)(uintptr_t)newbuf, (void *)(uintptr_t)dctx->buf, dctx->alloc_size);
113 	BS->FreePages(dctx->buf, dctx->pages);
114 	dctx->buf = newbuf;
115 	dctx->pages = newpages;
116 	dctx->buf_cur = (uint8_t *)(uintptr_t)dctx->buf + dctx->alloc_size;
117 	dctx->buf_end = (uint8_t *)(uintptr_t)dctx->buf + newsz;
118 	BS->SetMem(dctx->buf_cur, newsz - dctx->alloc_size, 0);
119 	dctx->alloc_size = newsz;
120 	return (EFI_SUCCESS);
121 }
122 
123 static void
124 free_buffer(decomp_state *dctx)
125 {
126 	if (dctx->buf)
127 		BS->FreePages(dctx->buf, dctx->pages);
128 	dctx->buf = 0;
129 }
130 
131 /*
132  * zlib supprot
133  */
134 static EFI_STATUS
135 zlib_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
136 {
137 	z_stream *strm = &dctx->zstrm;
138 
139 	/*
140 	 * Assume 4x compression, but start at 64MB
141 	 */
142 	dctx->size = max(size_hint * 4, M(64));
143 	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
144 	if (EFI_ERROR(status))
145 		return (status);
146 	memset(strm, 0, sizeof(*strm));
147 	strm->next_in = first_buf;
148 	strm->avail_in = buflen;
149 	return (inflateInit2(strm, 15 + 16) == Z_OK ? EFI_SUCCESS : EFI_VOLUME_CORRUPTED);
150 }
151 
152 static enum step_return
153 zlib_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
154 {
155 	z_stream *strm = &dctx->zstrm;
156 	size_t outlen = dctx->buf_end - dctx->buf_cur;
157 
158 	strm->next_in = buf;
159 	strm->avail_in = len;
160 	strm->next_out = dctx->buf_cur;
161 	strm->avail_out = outlen;
162 
163 	int ret = inflate(strm, Z_NO_FLUSH);
164 	dctx->buf_cur += outlen - strm->avail_out;
165 
166 	if (ret == Z_STREAM_END)
167 		return (done);
168 	if (ret != Z_OK)
169 		return (err);
170 	if (dctx->buf_cur < dctx->buf_end) /* Have output space */
171 		return (ok);
172 
173 	/*
174 	 * We're out of space, grow the buffer and try again if there's buffer
175 	 * space. We try again recursively since we know that will usually go
176 	 * only 1 deep.
177 	 */
178 	if (EFI_ERROR(grow_buffer(dctx)))
179 		return (err);
180 	if (strm->avail_in == 0)
181 		return (ok);
182 	size_t consumed = len - strm->avail_in;
183 	return (zlib_step(dctx, buf + consumed, strm->avail_in, offset + consumed));
184 }
185 
186 static void
187 zlib_fini(decomp_state *dctx, bool flush)
188 {
189 	inflateEnd(&dctx->zstrm);
190 	if (!flush)
191 		return;
192 	free_buffer(dctx);
193 }
194 
195 /*
196  * Bzip2 supprot
197  */
198 static EFI_STATUS
199 bzip2_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
200 {
201 	bz_stream *strm = &dctx->bzstrm;
202 
203 	/*
204 	 * Assume 4x compression, but start at 64MB
205 	 */
206 	dctx->size = max(size_hint * 4, M(64));
207 	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
208 	if (EFI_ERROR(status))
209 		return (status);
210 	memset(strm, 0, sizeof(*strm));
211 	strm->next_in = first_buf;
212 	strm->avail_in = buflen;
213 	return (BZ2_bzDecompressInit(strm, 0, 0) == BZ_OK ? EFI_SUCCESS : EFI_VOLUME_CORRUPTED);
214 }
215 
216 static enum step_return
217 bzip2_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
218 {
219 	bz_stream *strm = &dctx->bzstrm;
220 	size_t outlen = dctx->buf_end - dctx->buf_cur;
221 
222 	strm->next_in = buf;
223 	strm->avail_in = len;
224 	strm->next_out = dctx->buf_cur;
225 	strm->avail_out = outlen;
226 
227 	int ret = BZ2_bzDecompress(strm);
228 	dctx->buf_cur += outlen - strm->avail_out;
229 
230 	if (ret == BZ_STREAM_END)
231 		return (done);
232 	if (ret != BZ_OK)
233 		return (err);
234 	if (dctx->buf_cur < dctx->buf_end) /* Have output space */
235 		return (ok);
236 
237 	/*
238 	 * We're out of space, grow the buffer and try again if there's buffer
239 	 * space. We try again recursively since we know that will usually go
240 	 * only 1 deep.
241 	 */
242 	if (EFI_ERROR(grow_buffer(dctx)))
243 		return (err);
244 	if (strm->avail_in == 0)
245 		return (ok);
246 	size_t consumed = len - strm->avail_in;
247 	return (bzip2_step(dctx, buf + consumed, strm->avail_in, offset + consumed));
248 }
249 
250 static void
251 bzip2_fini(decomp_state *dctx, bool flush)
252 {
253 	BZ2_bzDecompressEnd(&dctx->bzstrm);
254 	if (!flush)
255 		return;
256 	free_buffer(dctx);
257 }
258 
259 /*
260  * XZ support
261  */
262 static EFI_STATUS
263 xz_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
264 {
265 	/*
266 	 * Assume 4x compression, but start at 64MB
267 	 */
268 	dctx->size = max(size_hint * 4, M(64));
269 	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
270 	if (EFI_ERROR(status))
271 		return (status);
272 	xz_crc32_init();
273 	xz_crc64_init();
274 	dctx->xzstrm = xz_dec_init(XZ_DYNALLOC, (uint32_t)-1);
275 	return (dctx->xzstrm != NULL ? EFI_SUCCESS : EFI_VOLUME_CORRUPTED);
276 }
277 
278 
279 static enum step_return
280 xz_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
281 {
282 	struct xz_dec *strm = dctx->xzstrm;
283 	size_t outlen = dctx->buf_end - dctx->buf_cur;
284 	struct xz_buf b = { .in = buf, .in_size = len, .in_pos = 0,
285 		.out = dctx->buf_cur, .out_size = outlen, .out_pos = 0 };
286 	int ret;
287 
288 	ret = xz_dec_run(strm, &b);
289 	dctx->buf_cur += b.out_pos;
290 
291 	if (ret == XZ_STREAM_END)
292 		return (done);
293 	if (ret != XZ_OK) {
294 		switch(ret) {
295 		case XZ_MEM_ERROR:
296 			printf("xz no memory ");
297 			break;
298 		case XZ_DATA_ERROR:
299 			printf("xz file corrupted ");
300 			break;
301 		case XZ_FORMAT_ERROR:
302 			printf("xz format not found ");
303 			break;
304 		case XZ_OPTIONS_ERROR:
305 			printf("unsupported xz option ");
306 			break;
307 		case XZ_MEMLIMIT_ERROR:
308 			printf("xz dictionary too small ");
309 			break;
310 		default:
311 			printf("xz step error %d ", ret);
312 			break;
313 		}
314 		printf(" len %d offset %d\n", (int)len, (int)offset);
315 		return (err);
316 	}
317 	if (dctx->buf_cur < dctx->buf_end) /* Have output space */
318 		return (ok);
319 
320 	/*
321 	 * We're out of space, grow the buffer and try again if there's buffer
322 	 * space. We try again recursively since we know that will usually go
323 	 * only 1 deep.
324 	 */
325 	if (EFI_ERROR(grow_buffer(dctx)))
326 		return (err);
327 	if (b.in_pos == b.in_size)
328 		return (ok);
329 	size_t consumed = b.in_pos;
330 	return (xz_step(dctx, buf + consumed, len - consumed, offset + consumed));
331 }
332 
333 static void
334 xz_fini(decomp_state *dctx, bool flush)
335 {
336 	xz_dec_end(dctx->xzstrm);
337 	dctx->xzstrm = NULL;
338 	if (!flush)
339 		return;
340 	free_buffer(dctx);
341 }
342 
343 /*
344  * ZSTD supprot
345  */
346 #ifdef LOADER_ZFS_SUPPORT
347 static EFI_STATUS
348 zstd_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
349 {
350 	unsigned long long size = ZSTD_getFrameContentSize(first_buf, buflen);
351 	if (size == ZSTD_CONTENTSIZE_ERROR)
352 		return (EFI_VOLUME_CORRUPTED);
353 	if (size == ZSTD_CONTENTSIZE_UNKNOWN)
354 		dctx->size = max(size_hint * 4, M(64));		/* Guess 4x compression or 64M */
355 	else
356 		dctx->size = size;				/* We know the size */
357 	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
358 	if (EFI_ERROR(status))
359 		return (status);
360 
361 	dctx->zstdstrm = ZSTD_createDStream();
362 	if (dctx->zstdstrm == NULL)
363 		return (EFI_OUT_OF_RESOURCES);
364 	if (ZSTD_isError(ZSTD_initDStream(dctx->zstdstrm))) {
365 		ZSTD_freeDStream(dctx->zstdstrm);
366 		dctx->zstdstrm = NULL;
367 		return (EFI_OUT_OF_RESOURCES);
368 	}
369 	return (EFI_SUCCESS);
370 }
371 
372 static enum step_return
373 zstd_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
374 {
375 	size_t outlen = dctx->buf_end - dctx->buf_cur;
376 	ZSTD_inBuffer inbuf = { buf, len, 0 };
377 	ZSTD_outBuffer outbuf = { dctx->buf_cur, outlen, 0 };
378 	size_t ret;
379 
380 	ret = ZSTD_decompressStream(dctx->zstdstrm, &outbuf, &inbuf);
381 	dctx->buf_cur += outbuf.pos;
382 
383 	if (ZSTD_isError(ret))
384 		return (err);
385 	if (ret == 0)
386 		return (done);
387 	if (dctx->buf_cur < dctx->buf_end) /* Have output space */
388 		return (ok);
389 
390 	/*
391 	 * We're out of space, grow the buffer and try again if there's buffer
392 	 * space. We try again recursively since we know that will usually go
393 	 * only 1 deep.
394 	 */
395 	if (EFI_ERROR(grow_buffer(dctx)))
396 		return (err);
397 	if (inbuf.size == inbuf.pos)
398 		return (ok);
399 	return (zstd_step(dctx, buf + inbuf.pos, inbuf.size - inbuf.pos, offset + inbuf.pos));
400 }
401 
402 static void
403 zstd_fini(decomp_state *dctx, bool flush)
404 {
405 	ZSTD_freeDStream(dctx->zstdstrm);
406 	if (!flush)
407 		return;
408 	free_buffer(dctx);
409 }
410 #endif
411 
412 /*
413  * No / Unknown decompression fallback
414  */
415 static EFI_STATUS
416 null_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
417 {
418 	dctx->size = size_hint;
419 	return (alloc_buffer(dctx, size_hint));
420 }
421 
422 static enum step_return
423 null_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
424 {
425 	size_t end = offset + len;
426 
427 	if (end > dctx->size) {
428 		printf("Too much data recieved!");
429 		return (err);
430 	}
431 	if ((uintptr_t)dctx->buf_cur - (uintptr_t)dctx->buf != offset) {
432 		printf("OH NO! The offset is %llu but I expected %llu\n", ULL(offset),
433 		    ULL((uintptr_t)dctx->buf_cur - (uintptr_t)dctx->buf));
434 		return (err);
435 	}
436 
437 	memcpy(dctx->buf_cur, buf, len);
438 	dctx->buf_cur += len;
439 	return (end == dctx->size ? done : ok);
440 }
441 
442 static void
443 null_fini(decomp_state *dctx, bool flush)
444 {
445 	if (!flush)
446 		return;
447 	free_buffer(dctx);
448 }
449 
450 decomp_state *
451 decomp_init(uint8_t *buf, size_t buflen, size_t size_hint)
452 {
453 	decomp_state *dctx;
454 
455 	dctx = malloc(sizeof(*dctx));
456 	memset(dctx, 0, sizeof(*dctx));
457 	dctx->type = what_compressed(buf, buflen);
458 	switch (dctx->type) {
459 	case zlib:
460 		dctx->init = zlib_init;
461 		dctx->step = zlib_step;
462 		dctx->fini = zlib_fini;
463 		break;
464 	case bzip2:
465 		dctx->init = bzip2_init;
466 		dctx->step = bzip2_step;
467 		dctx->fini = bzip2_fini;
468 		break;
469 	case xz:
470 		dctx->init = xz_init;
471 		dctx->step = xz_step;
472 		dctx->fini = xz_fini;
473 		break;
474 #ifdef LOADER_ZFS_SUPPORT
475 	case zstd:
476 		dctx->init = zstd_init;
477 		dctx->step = zstd_step;
478 		dctx->fini = zstd_fini;
479 		break;
480 #endif
481 	case none:
482 		dctx->init = null_init;
483 		dctx->step = null_step;
484 		dctx->fini = null_fini;
485 		break;
486 	default:
487 		return (NULL);
488 	}
489 
490 	if (EFI_ERROR(dctx->init(dctx, buf, buflen, size_hint))) {
491 		free(dctx);
492 		dctx = NULL;
493 	}
494 	return (dctx);
495 }
496 
497 enum step_return
498 decomp_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
499 {
500 	return (dctx->step(dctx, buf, len, offset));
501 }
502 
503 void
504 decomp_fini(decomp_state *dctx, bool flush)
505 {
506 	return (dctx->fini(dctx, flush));
507 }
508 
509 EFI_PHYSICAL_ADDRESS
510 decomp_buffer(decomp_state *dctx)
511 {
512 	if (dctx == NULL)
513 		return (0);
514 	return (dctx->buf);
515 }
516 
517 size_t
518 decomp_buffer_length(decomp_state *dctx)
519 {
520 	if (dctx == NULL)
521 		return (0);
522 	return ((uintptr_t)dctx->buf_cur - (uintptr_t)dctx->buf);
523 }
524