diff mbox series

[v2] hw/loongarch/boot: Support Linux raw boot image

Message ID 20241224-la-direct-kernel-boot-v2-1-3e8336c54c60@flygoat.com (mailing list archive)
State New
Headers show
Series [v2] hw/loongarch/boot: Support Linux raw boot image | expand

Commit Message

Jiaxun Yang Dec. 24, 2024, 3:06 p.m. UTC
Support booting such image by parsing header as per Linux's
specification [1].

This enabled booting vmlinux.efi/vmlinuz.efi shipped by
distros without supplying BIOS.

[1]: https://docs.kernel.org/arch/loongarch/booting.html

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
Changes in v2:
- Use extract API for getting bit fields (philmd)
- Mimic arm's load_aarch64_image to handle vmlinuz.efi
- Link to v1: https://lore.kernel.org/r/20241223-la-direct-kernel-boot-v1-1-a79995d8b15e@flygoat.com
---
 hw/loongarch/boot.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)


---
base-commit: c69612063e1844b76ac01e3a781b979548c3585c
change-id: 20241222-la-direct-kernel-boot-c598264710e7

Best regards,

Comments

Richard Henderson Dec. 24, 2024, 3:26 p.m. UTC | #1
On 12/24/24 07:06, Jiaxun Yang wrote:
> +static int64_t load_loongarch_linux_image(const char *filename,
> +                                          uint64_t *kernel_entry,
> +                                          uint64_t *kernel_low,
> +                                          uint64_t *kernel_high)
> +{
> +    gsize len;
> +    int size;
> +    uint8_t *buffer;
> +    struct loongarch_linux_hdr *hdr;
> +
> +    /* Load as raw file otherwise */
> +    if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) {
> +        return -1;
> +    }
> +    size = len;

This truncates from size_t to int, which will start to fail in odd ways at 2GB.

The code in load_aarch64_image has the same problem, so the best fix would be to change 
unpack_efi_zboot_image to take size_t.

> +
> +    /* Unpack the image if it is a EFI zboot image */
> +    if (unpack_efi_zboot_image(&buffer, &size) < 0) {
> +        g_free(buffer);
> +        return -1;
> +    }


r~
diff mbox series

Patch

diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c
index 93847b0eaf8e50ce1a990b91267780e6785e1c2f..8b3fa35695a5226535fe160eeb3707955f44d9ec 100644
--- a/hw/loongarch/boot.c
+++ b/hw/loongarch/boot.c
@@ -16,6 +16,26 @@ 
 #include "sysemu/reset.h"
 #include "sysemu/qtest.h"
 
+/*
+ * Linux Image Format
+ * https://docs.kernel.org/arch/loongarch/booting.html
+ */
+#define LINUX_PE_MAGIC  0x818223cd
+#define MZ_MAGIC        0x5a4d /* "MZ" */
+
+struct loongarch_linux_hdr {
+    uint32_t mz_magic;
+    uint32_t res0;
+    uint64_t kernel_entry;
+    uint64_t kernel_size;
+    uint64_t load_offset;
+    uint64_t res1;
+    uint64_t res2;
+    uint64_t res3;
+    uint32_t linux_pe_magic;
+    uint32_t pe_header_offset;
+} QEMU_PACKED;
+
 struct memmap_entry *memmap_table;
 unsigned memmap_entries;
 
@@ -260,6 +280,50 @@  static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
     return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS);
 }
 
+static int64_t load_loongarch_linux_image(const char *filename,
+                                          uint64_t *kernel_entry,
+                                          uint64_t *kernel_low,
+                                          uint64_t *kernel_high)
+{
+    gsize len;
+    int size;
+    uint8_t *buffer;
+    struct loongarch_linux_hdr *hdr;
+
+    /* Load as raw file otherwise */
+    if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) {
+        return -1;
+    }
+    size = len;
+
+    /* Unpack the image if it is a EFI zboot image */
+    if (unpack_efi_zboot_image(&buffer, &size) < 0) {
+        g_free(buffer);
+        return -1;
+    }
+
+    hdr = (struct loongarch_linux_hdr *)buffer;
+
+    if (extract32(le32_to_cpu(hdr->mz_magic), 0, 16) != MZ_MAGIC ||
+        le32_to_cpu(hdr->linux_pe_magic) != LINUX_PE_MAGIC) {
+        g_free(buffer);
+        return -1;
+    }
+
+    /* Early kernel versions may have those fields in virtual address */
+    *kernel_entry = extract64(le64_to_cpu(hdr->kernel_entry),
+                              0, TARGET_PHYS_ADDR_SPACE_BITS);
+    *kernel_low = extract64(le64_to_cpu(hdr->load_offset),
+                            0, TARGET_PHYS_ADDR_SPACE_BITS);
+    *kernel_high = *kernel_low + size;
+
+    rom_add_blob_fixed(filename, buffer, size, *kernel_low);
+
+    g_free(buffer);
+
+    return size;
+}
+
 static int64_t load_kernel_info(struct loongarch_boot_info *info)
 {
     uint64_t kernel_entry, kernel_low, kernel_high;
@@ -270,6 +334,11 @@  static int64_t load_kernel_info(struct loongarch_boot_info *info)
                            &kernel_entry, &kernel_low,
                            &kernel_high, NULL, 0,
                            EM_LOONGARCH, 1, 0);
+    if (kernel_size < 0) {
+        kernel_size = load_loongarch_linux_image(info->kernel_filename,
+                                                 &kernel_entry, &kernel_low,
+                                                 &kernel_high);
+    }
 
     if (kernel_size < 0) {
         error_report("could not load kernel '%s': %s",