hw/loongarch/boot: Support Linux raw boot image

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>
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
This commit is contained in:
Jiaxun Yang 2025-01-08 10:01:07 +08:00 committed by Bibo Mao
parent e3526d0fd7
commit cc4ba2c2d2

View File

@ -15,6 +15,26 @@
#include "system/reset.h" #include "system/reset.h"
#include "system/qtest.h" #include "system/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; struct memmap_entry *memmap_table;
unsigned memmap_entries; unsigned memmap_entries;
@ -171,6 +191,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); 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;
ssize_t 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) static int64_t load_kernel_info(struct loongarch_boot_info *info)
{ {
uint64_t kernel_entry, kernel_low, kernel_high; uint64_t kernel_entry, kernel_low, kernel_high;
@ -181,6 +245,11 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info)
&kernel_entry, &kernel_low, &kernel_entry, &kernel_low,
&kernel_high, NULL, 0, &kernel_high, NULL, 0,
EM_LOONGARCH, 1, 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) { if (kernel_size < 0) {
error_report("could not load kernel '%s': %s", error_report("could not load kernel '%s': %s",