[RFC-PATCH-for-11.0] loader: Add support for U-Boot Zstandard-compressed image

Philippe Mathieu-Daudé posted 1 patch 2 months, 2 weeks ago
Failed in applying to current master (apply log)
hw/core/loader.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
[RFC-PATCH-for-11.0] loader: Add support for U-Boot Zstandard-compressed image
Posted by Philippe Mathieu-Daudé 2 months, 2 weeks ago
U-Boot is able to decompress Zstandard images since almost
6 years (u-boot commit 8509f22a).

Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
Based-on: <20251011081553.4065883-1-daan.j.demeyer@gmail.com>
---
 hw/core/loader.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/hw/core/loader.c b/hw/core/loader.c
index e5904cdafa1..97fa8248ce7 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -635,7 +635,7 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
     uboot_image_header_t *hdr = &h;
     uint8_t *data = NULL;
     int ret = -1;
-    int do_uncompress = 0;
+    bool do_uncompress = false;
 
     fd = open(filename, O_RDONLY | O_BINARY);
     if (fd < 0)
@@ -685,7 +685,8 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
         case IH_COMP_NONE:
             break;
         case IH_COMP_GZIP:
-            do_uncompress = 1;
+        case IH_COMP_ZSTD:
+            do_uncompress = true;
             break;
         default:
             fprintf(stderr,
@@ -747,10 +748,23 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
         max_bytes = UBOOT_MAX_GUNZIP_BYTES;
         data = g_malloc(max_bytes);
 
-        bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
+        switch (hdr->ih_comp) {
+        case IH_COMP_GZIP:
+            bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
+            break;
+#ifdef CONFIG_ZSTD
+        case IH_COMP_ZSTD: {
+            size_t ret = ZSTD_decompress(data, max_bytes,
+                                         compressed_data, hdr->ih_size);
+            bytes = ZSTD_isError(ret) ? -1 : (ssize_t) ret;
+            } break;
+#endif
+        default:
+            g_assert_not_reached();
+        }
         g_free(compressed_data);
         if (bytes < 0) {
-            fprintf(stderr, "Unable to decompress gzipped image!\n");
+            fprintf(stderr, "Unable to decompress image!\n");
             goto out;
         }
         hdr->ih_size = bytes;
-- 
2.51.0


Re: [RFC-PATCH-for-11.0] loader: Add support for U-Boot Zstandard-compressed image
Posted by Peter Maydell 2 months, 2 weeks ago
On Mon, 24 Nov 2025 at 12:22, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> U-Boot is able to decompress Zstandard images since almost
> 6 years (u-boot commit 8509f22a).
>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> ---
> Based-on: <20251011081553.4065883-1-daan.j.demeyer@gmail.com>
> ---
>  hw/core/loader.c | 22 ++++++++++++++++++----
>  1 file changed, 18 insertions(+), 4 deletions(-)
>
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index e5904cdafa1..97fa8248ce7 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -635,7 +635,7 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
>      uboot_image_header_t *hdr = &h;
>      uint8_t *data = NULL;
>      int ret = -1;
> -    int do_uncompress = 0;
> +    bool do_uncompress = false;
>
>      fd = open(filename, O_RDONLY | O_BINARY);
>      if (fd < 0)
> @@ -685,7 +685,8 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
>          case IH_COMP_NONE:
>              break;
>          case IH_COMP_GZIP:
> -            do_uncompress = 1;
> +        case IH_COMP_ZSTD:
> +            do_uncompress = true;
>              break;

Here we set do_uncompress whether QEMU was built with
CONFIG_ZSTD or not...

>          default:
>              fprintf(stderr,
> @@ -747,10 +748,23 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
>          max_bytes = UBOOT_MAX_GUNZIP_BYTES;
>          data = g_malloc(max_bytes);
>
> -        bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
> +        switch (hdr->ih_comp) {
> +        case IH_COMP_GZIP:
> +            bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
> +            break;
> +#ifdef CONFIG_ZSTD
> +        case IH_COMP_ZSTD: {
> +            size_t ret = ZSTD_decompress(data, max_bytes,
> +                                         compressed_data, hdr->ih_size);
> +            bytes = ZSTD_isError(ret) ? -1 : (ssize_t) ret;
> +            } break;
> +#endif

...so here if the user passes a ZSTD image to a QEMU
built without CONFIG_ZSTD I think we fall into the default
case and assert.

I think the neatest fix for this would be to have the
switch (hdr->ih_comp) that sets do_uncompress to have
an ifdef for CONFIG_ZSTD so that we can tell the user
"couldn't load this image because QEMU was compiled without
zstd support".

PS: do we need to cast ret to a ssize_t ? bytes is
already a ssize_t variable.

thanks
-- PMM
Re: [RFC-PATCH-for-11.0] loader: Add support for U-Boot Zstandard-compressed image
Posted by Philippe Mathieu-Daudé 2 months, 2 weeks ago
On 24/11/25 13:22, Philippe Mathieu-Daudé wrote:
> U-Boot is able to decompress Zstandard images since almost
> 6 years (u-boot commit 8509f22a).
> 
> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> ---
> Based-on: <20251011081553.4065883-1-daan.j.demeyer@gmail.com>

Same patch applies on Daan's v4:
Based-on: <20251124123521.1058183-1-daan.j.demeyer@gmail.com>

> ---
>   hw/core/loader.c | 22 ++++++++++++++++++----
>   1 file changed, 18 insertions(+), 4 deletions(-)
> 
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index e5904cdafa1..97fa8248ce7 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -635,7 +635,7 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
>       uboot_image_header_t *hdr = &h;
>       uint8_t *data = NULL;
>       int ret = -1;
> -    int do_uncompress = 0;
> +    bool do_uncompress = false;
>   
>       fd = open(filename, O_RDONLY | O_BINARY);
>       if (fd < 0)
> @@ -685,7 +685,8 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
>           case IH_COMP_NONE:
>               break;
>           case IH_COMP_GZIP:
> -            do_uncompress = 1;
> +        case IH_COMP_ZSTD:
> +            do_uncompress = true;
>               break;
>           default:
>               fprintf(stderr,
> @@ -747,10 +748,23 @@ static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
>           max_bytes = UBOOT_MAX_GUNZIP_BYTES;
>           data = g_malloc(max_bytes);
>   
> -        bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
> +        switch (hdr->ih_comp) {
> +        case IH_COMP_GZIP:
> +            bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
> +            break;
> +#ifdef CONFIG_ZSTD
> +        case IH_COMP_ZSTD: {
> +            size_t ret = ZSTD_decompress(data, max_bytes,
> +                                         compressed_data, hdr->ih_size);
> +            bytes = ZSTD_isError(ret) ? -1 : (ssize_t) ret;
> +            } break;
> +#endif
> +        default:
> +            g_assert_not_reached();
> +        }
>           g_free(compressed_data);
>           if (bytes < 0) {
> -            fprintf(stderr, "Unable to decompress gzipped image!\n");
> +            fprintf(stderr, "Unable to decompress image!\n");
>               goto out;
>           }
>           hdr->ih_size = bytes;