On Arch Linux kernel decompression will fail when Xen has been unified
with the kernel and initramfs as a single binary. This change works for
both streaming and non-streaming ZSTD content.

Signed-off-by: Rafaël Kooi <rafael_andr...@hotmail.com>
---
 xen/common/decompress.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/xen/common/decompress.c b/xen/common/decompress.c
index 989336983f..cde754ffb1 100644
--- a/xen/common/decompress.c
+++ b/xen/common/decompress.c
@@ -3,11 +3,26 @@
 #include <xen/string.h>
 #include <xen/decompress.h>
 
+typedef struct _ZSTD_state
+{
+    void *write_buf;
+    unsigned int write_pos;
+} ZSTD_state;
+
 static void __init cf_check error(const char *msg)
 {
     printk("%s\n", msg);
 }
 
+static int __init cf_check ZSTD_flush(void *buf, unsigned int pos,
+                                      void *userptr)
+{
+    ZSTD_state *state = (ZSTD_state*)userptr;
+    memcpy(state->write_buf + state->write_pos, buf, pos);
+    state->write_pos += pos;
+    return pos;
+}
+
 int __init decompress(void *inbuf, unsigned int len, void *outbuf)
 {
 #if 0 /* Not needed here yet. */
@@ -17,22 +32,32 @@ int __init decompress(void *inbuf, unsigned int len, void 
*outbuf)
 #endif
 
     if ( len >= 3 && !memcmp(inbuf, "\x42\x5a\x68", 3) )
-        return bunzip2(inbuf, len, NULL, NULL, outbuf, NULL, error);
+        return bunzip2(inbuf, len, NULL, NULL, outbuf, NULL, error, NULL);
 
     if ( len >= 6 && !memcmp(inbuf, "\3757zXZ", 6) )
-        return unxz(inbuf, len, NULL, NULL, outbuf, NULL, error);
+        return unxz(inbuf, len, NULL, NULL, outbuf, NULL, error, NULL);
 
     if ( len >= 2 && !memcmp(inbuf, "\135\000", 2) )
-        return unlzma(inbuf, len, NULL, NULL, outbuf, NULL, error);
+        return unlzma(inbuf, len, NULL, NULL, outbuf, NULL, error, NULL);
 
     if ( len >= 5 && !memcmp(inbuf, "\x89LZO", 5) )
-        return unlzo(inbuf, len, NULL, NULL, outbuf, NULL, error);
+        return unlzo(inbuf, len, NULL, NULL, outbuf, NULL, error, NULL);
 
     if ( len >= 2 && !memcmp(inbuf, "\x02\x21", 2) )
-       return unlz4(inbuf, len, NULL, NULL, outbuf, NULL, error);
+        return unlz4(inbuf, len, NULL, NULL, outbuf, NULL, error, NULL);
 
     if ( len >= 4 && !memcmp(inbuf, "\x28\xb5\x2f\xfd", 4) )
-       return unzstd(inbuf, len, NULL, NULL, outbuf, NULL, error);
+    {
+        // NOTE (Rafaël): On Arch Linux the kernel is compressed in a way
+        // that requires streaming ZSTD decompression. Otherwise decompression
+        // will fail when using a unified EFI binary. Somehow decompression
+        // works when not using a unified EFI binary, I suspect this is the
+        // kernel self decompressing. Or there is a code path that I am not
+        // aware of that takes care of the use case properly.
+
+        ZSTD_state state = (ZSTD_state){ outbuf, 0 };
+        return unzstd(inbuf, len, NULL, ZSTD_flush, NULL, NULL, error, &state);
+    }
 
     return 1;
 }
-- 
2.40.0


Reply via email to