git: dc6c193bb4a6 - main - loader.efi: Add xz decompression

From: Warner Losh <imp_at_FreeBSD.org>
Date: Sat, 11 Jul 2026 19:30:24 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=dc6c193bb4a67e2e9ee97d1e3ac3e2950bd16979

commit dc6c193bb4a67e2e9ee97d1e3ac3e2950bd16979
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-07-11 14:44:40 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-07-11 19:28:47 +0000

    loader.efi: Add xz decompression
    
    We can now decompress .xz compressed memory disks, like
    FreeBSD-15.1-RELEASE-amd64-disc1.iso.xz
    
    Sponsored by:           Netflix
    Differential Revision:  https://reviews.freebsd.org/D58073
---
 stand/defs.mk                               |   5 ++
 stand/efi/loader/Makefile                   |   2 +-
 stand/efi/loader/decompress.c               | 101 ++++++++++++++++++++++++++--
 stand/efi/loader/decompress.h               |   2 +-
 stand/libsa/Makefile                        |   7 ++
 sys/contrib/xz-embedded/freebsd/xz_config.h |  11 +--
 6 files changed, 117 insertions(+), 11 deletions(-)

diff --git a/stand/defs.mk b/stand/defs.mk
index 05108404c538..e3908678b53a 100644
--- a/stand/defs.mk
+++ b/stand/defs.mk
@@ -190,6 +190,11 @@ BZIP2_CFLAGS=-I${SRCTOP}/contrib/bzip2  -DBZ_NO_STDIO -DBZ_NO_COMPRESS
 # ZSTD client cflags
 ZSTD_CFLAGS=-I${SYSDIR}/contrib/zstd/lib
 
+# XZ flags
+XZ_DIR=${SRCTOP}/sys/contrib/xz-embedded
+XZ_CFLAGS=-DXZ_USE_CRC64 -I${XZ_DIR}/freebsd -I${XZ_DIR}/linux/include/linux
+
+
 # The boot loader build uses dd status=none, where possible, for reproducible
 # build output (since performance varies from run to run). Trouble is that
 # option was recently (10.3) added to FreeBSD and is non-standard. Only use it
diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
index 164b16ff2a59..b29f0b8f8259 100644
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -47,7 +47,7 @@ HAVE_ZFS=	yes
 
 CFLAGS.bootinfo.c += -I$(SRCTOP)/sys/teken
 CFLAGS.bootinfo.c += -I${SRCTOP}/contrib/pnglite
-CFLAGS.decompress.c += ${ZLIB_CFLAGS} ${BZIP2_CFLAGS} ${ZSTD_CFLAGS}
+CFLAGS.decompress.c += ${ZLIB_CFLAGS} ${BZIP2_CFLAGS} ${ZSTD_CFLAGS} ${XZ_CFLAGS}
 CFLAGS.framebuffer.c += -I$(SRCTOP)/sys/teken
 CFLAGS.framebuffer.c += -I${SRCTOP}/contrib/pnglite
 CFLAGS.main.c += -I$(SRCTOP)/sys/teken
diff --git a/stand/efi/loader/decompress.c b/stand/efi/loader/decompress.c
index 5c16f7d9bb2a..fb3c6314e4b6 100644
--- a/stand/efi/loader/decompress.c
+++ b/stand/efi/loader/decompress.c
@@ -11,7 +11,8 @@
 
 #include <zlib.h>
 #include <bzlib.h>
-#ifdef LOADER_ZFS_SUPPORT	/* ZSTD only available with ZFS */
+#include <xz.h>
+#ifdef LOADER_ZFS_SUPPORT	/* ZSTD and lzma only available with ZFS */
 #include <zstd.h>
 #endif
 #include <sys/_param.h>
@@ -32,6 +33,7 @@ struct decomp_state
 	union {
 		z_stream zstrm;
 		bz_stream bzstrm;
+		struct xz_dec *xzstrm;
 #ifdef LOADER_ZFS_SUPPORT
 		ZSTD_DStream *zstdstrm;
 #endif
@@ -63,10 +65,10 @@ what_compressed(uint8_t *buf, size_t len)
 		return (zstd);
 	}
 	if (memcmp(buf, "\xfd""7zXZ\x00", 6) == 0) {
-		printf("xz -- unsupproted\n");
-	} else {
-		printf("Not compressed\n");
+		printf("xz\n");
+		return (xz);
 	}
+	printf("Not compressed\n");
 	return (none);
 }
 
@@ -220,7 +222,7 @@ bzip2_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
 
         if (ret == BZ_STREAM_END)
                 return (done);
-        if (ret != Z_OK)
+        if (ret != BZ_OK)
                 return (err);
         if (dctx->buf_cur < dctx->buf_end) /* Have output space */
 		return (ok);
@@ -247,6 +249,90 @@ bzip2_fini(decomp_state *dctx, bool flush)
 	free_buffer(dctx);
 }
 
+/*
+ * XZ support
+ */
+static EFI_STATUS
+xz_init(decomp_state *dctx, uint8_t *first_buf, size_t buflen, size_t size_hint)
+{
+	/*
+	 * Assume 4x compression, but start at 64MB
+	 */
+	dctx->size = max(size_hint * 4, M(64));
+	EFI_STATUS status = alloc_buffer(dctx, dctx->size);
+	if (EFI_ERROR(status))
+		return (status);
+	xz_crc32_init();
+	xz_crc64_init();
+	dctx->xzstrm = xz_dec_init(XZ_DYNALLOC, (uint32_t)-1);
+        return (dctx->xzstrm != NULL ? EFI_SUCCESS : EFI_VOLUME_CORRUPTED);	
+}
+
+
+static enum step_return
+xz_step(decomp_state *dctx, uint8_t *buf, size_t len, size_t offset)
+{
+	struct xz_dec *strm = dctx->xzstrm;
+	size_t outlen = dctx->buf_end - dctx->buf_cur;
+	struct xz_buf b = { .in = buf, .in_size = len, .in_pos = 0,
+		.out = dctx->buf_cur, .out_size = outlen, .out_pos = 0 };
+	int ret;
+	
+        ret = xz_dec_run(strm, &b);
+	dctx->buf_cur += b.out_pos;
+
+        if (ret == XZ_STREAM_END)
+                return (done);
+        if (ret != XZ_OK) {
+		switch(ret) {
+		case XZ_MEM_ERROR:
+			printf("xz no memory ");
+			break;
+		case XZ_DATA_ERROR:
+			printf("xz file corrupted ");
+			break;
+		case XZ_FORMAT_ERROR:
+			printf("xz format not found ");
+			break;
+		case XZ_OPTIONS_ERROR:
+			printf("unsupported xz option ");
+			break;
+		case XZ_MEMLIMIT_ERROR:
+			printf("xz dictionary too small ");
+			break;
+		default:
+			printf("xz step error %d ", ret);
+			break;
+		}
+		printf(" len %d offset %d\n", (int)len, (int)offset);
+                return (err);
+	}
+        if (dctx->buf_cur < dctx->buf_end) /* Have output space */
+		return (ok);
+
+	/*
+	 * We're out of space, grow the buffer and try again if there's buffer
+	 * space. We try again recursively since we know that will usually go
+	 * only 1 deep.
+	 */
+	if (EFI_ERROR(grow_buffer(dctx)))
+		return (err);
+	if (b.in_pos == b.in_size)
+		return (ok);
+	size_t consumed = b.in_pos;
+	return (xz_step(dctx, buf + consumed, len - consumed, offset + consumed));
+}
+
+static void
+xz_fini(decomp_state *dctx, bool flush)
+{
+	xz_dec_end(dctx->xzstrm);
+	dctx->xzstrm = NULL;
+	if (!flush)
+		return;
+	free_buffer(dctx);
+}
+
 /*
  * ZSTD supprot
  */
@@ -373,6 +459,11 @@ decomp_init(uint8_t *buf, size_t buflen, size_t size_hint)
 		dctx->step = bzip2_step;
 		dctx->fini = bzip2_fini;
 		break;
+	case xz:
+		dctx->init = xz_init;
+		dctx->step = xz_step;
+		dctx->fini = xz_fini;
+		break;
 #ifdef LOADER_ZFS_SUPPORT
 	case zstd:
 		dctx->init = zstd_init;
diff --git a/stand/efi/loader/decompress.h b/stand/efi/loader/decompress.h
index 43c48078f822..f9d4fe445028 100644
--- a/stand/efi/loader/decompress.h
+++ b/stand/efi/loader/decompress.h
@@ -6,7 +6,7 @@
 
 #pragma once
 
-enum compression { none, zlib, bzip2, zstd };
+enum compression { none, zlib, bzip2, xz, zstd };
 enum step_return { ok, done, err };
 
 typedef struct decomp_state decomp_state;
diff --git a/stand/libsa/Makefile b/stand/libsa/Makefile
index a0bc47f343b5..ab7a0f5a59f2 100644
--- a/stand/libsa/Makefile
+++ b/stand/libsa/Makefile
@@ -110,6 +110,13 @@ SRCS+=	${i}
 SRCS+=	lz4.c
 CFLAGS.lz4.c+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
 
+# xz decompression
+.PATH: ${XZ_DIR}/linux/lib/xz
+.for i in xz_crc32.c xz_crc64.c xz_dec_bcj.c xz_dec_lzma2.c xz_dec_stream.c xz.c
+CFLAGS.${i}+=${XZ_CFLAGS}
+SRCS+=	${i}
+.endfor
+
 # io routines
 SRCS+=	closeall.c dev.c ioctl.c nullfs.c stat.c mount.c \
 	fstat.c close.c lseek.c open.c read.c write.c readdir.c preload.c
diff --git a/sys/contrib/xz-embedded/freebsd/xz_config.h b/sys/contrib/xz-embedded/freebsd/xz_config.h
index 7fb9a07b5982..4667b64805af 100644
--- a/sys/contrib/xz-embedded/freebsd/xz_config.h
+++ b/sys/contrib/xz-embedded/freebsd/xz_config.h
@@ -27,10 +27,17 @@
 #ifndef __FREEBSD_XZ_CONFIG_H__
 #define __FREEBSD_XZ_CONFIG_H__
 
+#if defined(_KERNEL)
 #include <sys/param.h>
 #include <sys/endian.h>
 #include <sys/types.h>
 #include <sys/systm.h>
+#elif defined(_STANDALONE)
+#include "stand.h"
+#include <sys/param.h>
+#else
+/* nothing */
+#endif
 
 #include <contrib/xz-embedded/linux/include/linux/xz.h>
 #include "xz_malloc.h"
@@ -61,10 +68,6 @@
 #define	memeq(a, b, size)	(memcmp((a), (b), (size)) == 0)
 #define	memzero(buf, size)	bzero((buf), (size))
 
-#ifndef min
-#	define min(x, y)	MIN((x), (y))
-#endif
-
 #define	min_t(type, x, y)	min((x), (y))
 
 #define	get_le32(ptr)	le32toh(*(const uint32_t *)(ptr))