Commit 3461642bdb for qemu.org
commit 3461642bdb135ba74c9f70f0e2e093a2e46045e8
Author: Brian Cain <brian.cain@oss.qualcomm.com>
Date: Mon Nov 9 10:49:23 2026 -0800
hw/hexagon/virt: fix firmware boot
-bios on virt only loaded a raw image of at most 64 KiB at address
zero. This is insufficient for H2 firmware, which is usually hundreds of
KiB.
Boot a kernel given with -kernel through the firmware given with -bios.
Load both ELFs at their declared physical addresses, place the device tree
and initrd after the kernel, and enter the firmware through a boot stub
that passes the device tree in r1:r0.
Fixes: 88a8bc7f43ff ("hw/hexagon: Define hexagon "virt" machine")
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
diff --git a/hw/hexagon/hex-board.c b/hw/hexagon/hex-board.c
index c3b8c647e4..d2e958f9b5 100644
--- a/hw/hexagon/hex-board.c
+++ b/hw/hexagon/hex-board.c
@@ -337,7 +337,7 @@ void hex_board_check_initrd(MachineState *ms, bool firmware)
}
uint64_t hex_board_load_vm_guest(MachineState *ms, const char *firmware,
- hwaddr ddr_base, hwaddr kernel_addr,
+ hwaddr ddr_base, hwaddr kernel_load_offset,
hwaddr stub_base, int fdt_size,
hwaddr *fdt_addr)
{
@@ -345,15 +345,17 @@ uint64_t hex_board_load_vm_guest(MachineState *ms, const char *firmware,
hwaddr ram_end = ddr_base + ms->ram_size;
uint32_t stub[ARRAY_SIZE(vm_boot_stub)];
- if (ddr_base > stub_base || ram_end <= kernel_addr) {
+ if (ddr_base > stub_base || ram_end <= kernel_load_offset) {
error_report("RAM (%" PRIu64 " MiB at 0x%" HWADDR_PRIx ") is too "
"small: the kernel is loaded at 0x%" HWADDR_PRIx,
- ms->ram_size / MiB, ddr_base, kernel_addr);
+ ms->ram_size / MiB, ddr_base, kernel_load_offset);
exit(1);
}
firmware_entry = hex_board_load_elf(firmware, NULL, NULL, NULL);
- hex_board_load_elf(ms->kernel_filename, vm_guest_translate, &kernel_addr,
+ hex_board_load_elf(ms->kernel_filename,
+ kernel_load_offset ? vm_guest_translate : NULL,
+ kernel_load_offset ? &kernel_load_offset : NULL,
&image_high);
*fdt_addr = QEMU_ALIGN_UP(image_high + 16 * MiB, 4 * MiB);
hex_board_load_initrd(ms, ram_end, *fdt_addr, fdt_size);
diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
index d8e9177059..3327e28d12 100644
--- a/hw/hexagon/virt.c
+++ b/hw/hexagon/virt.c
@@ -7,11 +7,13 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "elf.h"
#include "hw/hexagon/virt.h"
#include "hw/hexagon/hexagon.h"
#include "hw/hexagon/hex-board.h"
#include "hw/hexagon/hex-subsys.h"
#include "hw/core/loader.h"
+#include "qemu/datadir.h"
#include "qemu/error-report.h"
#include "qemu/target-info.h"
#include "qemu/units.h"
@@ -22,6 +24,7 @@ enum {
VIRT_UART0,
VIRT_MMIO,
VIRT_FDT,
+ VIRT_BOOT,
};
/*
@@ -32,9 +35,10 @@ static const int VIRTIO_IRQ_BASE = 16;
static const int VIRT_UART0_IRQ = 15;
static const MemMapEntry base_memmap[] = {
- [VIRT_UART0] = { 0x10000000, 0x00000200 },
+ [VIRT_UART0] = { 0x10000000, 0x00001000 },
[VIRT_MMIO] = { 0x11000000, 0x00001000 },
[VIRT_FDT] = { 0x99800000, 0x00400000 },
+ [VIRT_BOOT] = { 0x99c00000, 0x00000200 },
};
@@ -43,20 +47,35 @@ static uint64_t load_kernel(const HexagonVirtMachineState *vms)
return hex_board_load_elf(MACHINE(vms)->kernel_filename, NULL, NULL, NULL);
}
-static uint64_t load_bios(HexagonVirtMachineState *vms)
+static bool is_elf(const char *filename)
{
- MachineState *ms = MACHINE(vms);
- uint64_t bios_addr = 0x0; /* Load BIOS at reset vector address 0x0 */
- int bios_size;
-
- bios_size = load_image_targphys(ms->firmware ?: "",
- bios_addr, 64 * 1024, NULL);
- if (bios_size < 0) {
- error_report("Could not load BIOS '%s'", ms->firmware ?: "");
- exit(1);
+ uint8_t magic[SELFMAG];
+ int fd = qemu_open_old(filename, O_RDONLY | O_BINARY);
+ bool elf;
+
+ if (fd < 0) {
+ return false;
}
+ elf = read(fd, magic, sizeof(magic)) == sizeof(magic) &&
+ memcmp(magic, ELFMAG, SELFMAG) == 0;
+ qemu_close(fd);
+ return elf;
+}
- return bios_addr; /* Return entry point at address 0x0 */
+/*
+ * An ELF firmware starts at its entry point. Any other firmware is a raw
+ * image that starts at the reset vector, address zero, where it is loaded.
+ */
+static uint64_t load_bios(MachineState *ms, const char *filename)
+{
+ if (is_elf(filename)) {
+ return hex_board_load_elf(filename, NULL, NULL, NULL);
+ }
+ if (load_image_targphys(filename, 0, ms->ram_size, NULL) < 0) {
+ error_report("Could not load BIOS '%s'", filename);
+ exit(1);
+ }
+ return 0;
}
static void virt_init(MachineState *ms)
@@ -66,9 +85,23 @@ static void virt_init(MachineState *ms)
int32_t l2vic_phandle;
uint64_t start = 0;
bool have_start = false;
+ hwaddr fdt_addr = base_memmap[VIRT_FDT].base;
+ g_autofree char *firmware = NULL;
+ bool vm_guest;
+
+ if (ms->firmware) {
+ firmware = qemu_find_file(QEMU_FILE_TYPE_BIOS, ms->firmware);
+ if (!firmware) {
+ error_report("Could not find BIOS '%s'", ms->firmware);
+ exit(1);
+ }
+ }
+ hex_board_check_initrd(ms, firmware);
+ vm_guest = ms->kernel_filename && firmware;
hex_board_create_fdt(ms, "hexagon-virt,qemu", "qcom,sm8150",
&vms->fdt_size);
+ qemu_fdt_setprop_string(ms->fdt, "/soc", "compatible", "simple-bus");
qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline);
hex_subsys_create(&vms->parent_obj, m_cfg, v68_rev);
@@ -80,11 +113,16 @@ static void virt_init(MachineState *ms)
base_memmap[VIRT_MMIO].size, VIRTIO_IRQ_BASE,
VIRTIO_DEV_COUNT, l2vic_phandle);
- if (ms->kernel_filename) {
+ if (vm_guest) {
+ start = hex_board_load_vm_guest(ms, firmware, 0, 0,
+ base_memmap[VIRT_BOOT].base,
+ vms->fdt_size, &fdt_addr);
+ have_start = true;
+ } else if (ms->kernel_filename) {
start = load_kernel(vms);
have_start = true;
- } else if (ms->firmware) {
- start = load_bios(vms);
+ } else if (firmware) {
+ start = load_bios(ms, firmware);
have_start = true;
}
hex_board_create_cpus(ms, have_start, start);
@@ -94,7 +132,7 @@ static void virt_init(MachineState *ms)
base_memmap[VIRT_UART0].size, VIRT_UART0_IRQ,
l2vic_phandle);
- hex_board_load_fdt(ms, vms->fdt_size, base_memmap[VIRT_FDT].base);
+ hex_board_load_fdt(ms, vms->fdt_size, fdt_addr);
}
diff --git a/include/hw/hexagon/hex-board.h b/include/hw/hexagon/hex-board.h
index 5cdea79ea8..79548dfa62 100644
--- a/include/hw/hexagon/hex-board.h
+++ b/include/hw/hexagon/hex-board.h
@@ -69,16 +69,17 @@ void hex_board_fdt_add_vm_timer(MachineState *ms, hwaddr base, hwaddr size,
/*
* Boot a guest kernel through the Hexagon VM host firmware (H2). Load the
- * firmware ELF @firmware as it is linked and the kernel ELF named by -kernel
- * relocated by @kernel_addr. Place the device tree, whose size is
- * @fdt_size, after the kernel, and the -initrd after that, in the RAM
- * that spans from @ddr_base for the machine's RAM size. Write a boot stub
- * at @stub_base that passes the device tree address in r1:r0 and enters the
- * firmware, and return @stub_base, where CPU 0 starts. The device tree
- * address is returned in @fdt_addr. Exits on failure.
+ * firmware ELF @firmware as it is linked and the kernel ELF named by -kernel.
+ * @kernel_load_offset is added to the kernel's physical addresses; zero loads
+ * the ELF as linked. Place the device tree, whose size is @fdt_size, after
+ * the kernel, and the -initrd after that, in the RAM that spans from
+ * @ddr_base for the machine's RAM size. Write a boot stub at @stub_base that
+ * passes the device tree address in r1:r0 and enters the firmware, and return
+ * @stub_base, where CPU 0 starts. The device tree address is returned in
+ * @fdt_addr. Exits on failure.
*/
uint64_t hex_board_load_vm_guest(MachineState *ms, const char *firmware,
- hwaddr ddr_base, hwaddr kernel_addr,
+ hwaddr ddr_base, hwaddr kernel_load_offset,
hwaddr stub_base, int fdt_size,
hwaddr *fdt_addr);
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c
index 37fee7a91c..e5d3e155d1 100644
--- a/tests/qtest/boot-serial-test.c
+++ b/tests/qtest/boot-serial-test.c
@@ -146,7 +146,7 @@ static const uint8_t bios_hexagon[] = {
0x00, 0x40, 0x00, 0x01, /* immext(#0x10000000) */
0x00, 0xc0, 0x00, 0x78, /* r0 = ##0x10000000 */
0x54, 0xc0, 0x00, 0x3c, /* memb(r0+#0) = #0x54 Write 'T' */
- 0xf8, 0xff, 0xff, 0x59 /* jump 0x0 ; Loop back to start */
+ 0xfa, 0xff, 0xff, 0x59 /* jump _start */
};
typedef struct testdef {
@@ -201,7 +201,8 @@ static const testdef_t tests[] = {
{ "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 },
{ "arm", "stm32vldiscovery", "", "T",
sizeof(kernel_stm32vldiscovery), kernel_stm32vldiscovery },
- { "hexagon", "virt", "", "TT", sizeof(bios_hexagon), NULL, bios_hexagon },
+ { "hexagon", "virt", "", "TT", sizeof(bios_hexagon), NULL,
+ bios_hexagon },
{ NULL }
};