QEMU-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: uverma@linux.ibm.com
To: qemu-devel@nongnu.org, qemu-ppc@nongnu.org, aik@ozlabs.ru
Cc: pbonzini@redhat.com, th.huth@posteo.eu, nnmlinux@linux.ibm.com,
	sbhat@linux.ibm.com, harshpb@linux.ibm.com,
	amachhiw@linux.ibm.com, rathc@linux.ibm.com, balaton@eik.bme.hu,
	philmd@oss.qualcomm.com, npiggin@gmail.com,
	marcandre.lureau@redhat.com, fam@euphon.net,
	Utkarsh Verma <uverma@linux.ibm.com>
Subject: [RFC PATCH 3/8] ppc/spapr: add baseline VOF disk boot support
Date: Mon, 17 Aug 2026 15:57:20 +0530	[thread overview]
Message-ID: <20260817102733.605346-4-uverma@linux.ibm.com> (raw)
In-Reply-To: <20260817102733.605346-1-uverma@linux.ibm.com>

From: Utkarsh Verma <uverma@linux.ibm.com>

Add initial support for disk boot via VOF.
Scan all the block devices, and for each device search for PReP partition,
load the ELF payload (GRUB), and set the '/chosen' node in FDT with boot kernel
and bootpath properties.

AI-used-for: code
Signed-off-by: Utkarsh Verma <uverma@linux.ibm.com>
---
 hw/ppc/spapr_vof.c   | 142 ++++++++++++++++++++++++++++++++++++++++---
 hw/ppc/vof.c         |   3 +
 include/hw/ppc/vof.h |   2 +
 3 files changed, 139 insertions(+), 8 deletions(-)

diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c
index 46d78756e6..5bf9613005 100644
--- a/hw/ppc/spapr_vof.c
+++ b/hw/ppc/spapr_vof.c
@@ -10,8 +10,15 @@
 #include "hw/ppc/spapr_cpu_core.h"
 #include "hw/ppc/fdt.h"
 #include "hw/ppc/vof.h"
+#include "hw/ppc/spapr_vof.h"
+#include "hw/core/qdev.h"
+#include "hw/core/loader.h"
 #include "system/system.h"
+#include "system/block-backend.h"
+#include "system/block-backend-global-state.h"
 #include "qom/qom-qobject.h"
+#include "target/ppc/cpu.h"
+#include "elf.h"
 #include "trace.h"
 
 target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr,
@@ -32,10 +39,10 @@ void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt)
 
     vof_build_dt(fdt, spapr->vof);
 
-    if (spapr->vof->bootargs) {
-        int chosen;
+    int chosen;
+    _FDT(chosen = fdt_path_offset(fdt, "/chosen"));
 
-        _FDT(chosen = fdt_path_offset(fdt, "/chosen"));
+    if (spapr->vof->bootargs) {
         /*
          * If the client did not change "bootargs", spapr_dt_chosen() must have
          * stored machine->kernel_cmdline in it before getting here.
@@ -43,6 +50,22 @@ void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt)
         _FDT(fdt_setprop_string(fdt, chosen, "bootargs", spapr->vof->bootargs));
     }
 
+    if (spapr->vof->disk_boot) {
+        /*
+         * If disk boot is detected change the "qemu,boot-kernel" to hold
+         * kernel_addr/kernel_size which contain the GRUB entry point and size
+         */
+        uint64_t kern[2];
+        kern[0] = cpu_to_be64(spapr->kernel_addr);
+        kern[1] = cpu_to_be64(spapr->kernel_size);
+        _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", &kern, sizeof(kern)));
+
+        if (spapr->vof->bootpath) {
+            _FDT(fdt_setprop_string(fdt, chosen, "bootpath",
+                                    spapr->vof->bootpath));
+        }
+    }
+
     /*
      * SLOF-less setup requires an open instance of stdout for early
      * kernel printk. By now all phandles are settled so we can open
@@ -54,11 +77,100 @@ void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt)
     }
 }
 
+static bool vof_elf_segment_cb(void *opaque,
+                                uint64_t paddr, uint64_t vaddr,
+                                uint64_t filesz, uint64_t memsz)
+{
+    Vof *vof = opaque;
+
+    if (memsz == 0) {
+        return true;
+    }
+
+    if (paddr != vaddr) {
+        error_report("spapr_vof_load_elf: segment paddr/vaddr mismatch "
+                     "(paddr=0x%" PRIx64 " vaddr=0x%" PRIx64 ")",
+                     paddr, vaddr);
+        return false;
+    }
+
+    if (vof_claim(vof, paddr, memsz, 0) == -1) {
+        error_report("spapr_vof_load_elf: vof_claim failed for "
+                     "paddr=0x%" PRIx64 " size=0x%" PRIx64, paddr, memsz);
+        return false;
+    }
+
+    return true;
+}
+
+static bool spapr_vof_try_prep_boot(SpaprMachineState *spapr, Vof *vof)
+{
+    BlockBackend *blk;
+    DeviceState *dev;
+    uint64_t partition_offset;
+    uint64_t partition_size;
+    uint8_t *prep_data;
+    uint64_t entry_point;
+    uint64_t load_size;
+
+    for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+
+        if (!blk_is_inserted(blk)) {
+            continue;
+        }
+
+        partition_offset = 0;
+        partition_size   = 0;
+
+        if (!spapr_vof_find_prep_partition(blk, &partition_offset,
+                                           &partition_size)) {
+            continue;
+        }
+
+        prep_data = g_malloc(partition_size);
+        if (blk_pread(blk, partition_offset, partition_size,
+                      prep_data, 0) < 0) {
+            g_free(prep_data);
+            continue;
+        }
+
+        uint64_t lowaddr = UINT64_MAX, highaddr = 0;
+        entry_point = 0;
+        ssize_t ret = load_elf_ram_sym_buf(prep_data, partition_size,
+                                           NULL, NULL, NULL,
+                                           &entry_point, &lowaddr, &highaddr,
+                                           NULL, ELFDATA2MSB,
+                                           PPC_ELF_MACHINE, 0, 0,
+                                           NULL, false, NULL,
+                                           vof_elf_segment_cb, vof);
+        if (ret <= 0) {
+            error_report("spapr_vof_try_prep_boot: %s", load_elf_strerror(ret));
+            g_free(prep_data);
+            continue;
+        }
+        load_size = highaddr - lowaddr;
+
+        g_free(prep_data);
+
+        spapr->kernel_addr = entry_point;
+        spapr->kernel_size = load_size;
+
+        vof->disk_boot = true;
+        dev = blk_get_attached_dev(blk);
+        if (dev) {
+            vof->bootpath = qdev_get_fw_dev_path(dev);
+        }
+        return true;
+    }
+    return false;
+}
+
 void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp)
 {
     target_ulong stack_ptr;
     Vof *vof = spapr->vof;
     PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
+    MachineState *machine = MACHINE(spapr);
 
     vof_init(vof, spapr->rma_size, errp);
 
@@ -70,7 +182,7 @@ void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp)
     /* Stack grows downwards plus reserve space for the minimum stack frame */
     stack_ptr += VOF_STACK_SIZE - 0x20;
 
-    if (spapr->kernel_size &&
+    if (machine->kernel_filename && spapr->kernel_size &&
         vof_claim(vof, spapr->kernel_addr, spapr->kernel_size, 0) == -1) {
         error_setg(errp, "Memory for kernel is in use");
         return;
@@ -82,6 +194,14 @@ void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp)
         return;
     }
 
+    /*
+     * Disk boot: load GRUB from the PReP boot partition on the block device, if
+     * no kernel/initrd are provided
+     */
+    if (!machine->kernel_filename) {
+        spapr_vof_try_prep_boot(spapr, vof);
+    }
+
     spapr_vof_client_dt_finalize(spapr, fdt);
 
     spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT,
@@ -91,10 +211,16 @@ void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp)
     /*
      * At this point the expected allocation map is:
      *
-     * 0..c38 - the initial firmware
-     * 8000..10000 - stack
-     * 400000.. - kernel
-     * 3ea0000.. - initramdisk
+     * Kernel + initrd boot:
+     *   0..c38      - the initial firmware
+     *   8000..10000 - stack
+     *   400000..    - kernel
+     *   3ea0000..   - initramdisk
+     *
+     * Disk (GRUB) boot:
+     *   0..c38      - the initial firmware
+     *   8000..10000 - stack
+     *   400000..    - GRUB (loaded from PReP partition)
      *
      * We skip writing FDT as nothing expects it; OF client interface is
      * going to be used for reading the device tree.
diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c
index fa7b73159a..a78bb1f116 100644
--- a/hw/ppc/vof.c
+++ b/hw/ppc/vof.c
@@ -1041,6 +1041,9 @@ void vof_cleanup(Vof *vof)
     vof->of_instances = NULL;
     vof->of_instance_last = 0;
     vof->claimed_base = 0;
+    g_free(vof->bootpath);
+    vof->bootpath = NULL;
+    vof->disk_boot = false;
 }
 
 void vof_build_dt(void *fdt, Vof *vof)
diff --git a/include/hw/ppc/vof.h b/include/hw/ppc/vof.h
index 3a0fbffe54..e17779ee8a 100644
--- a/include/hw/ppc/vof.h
+++ b/include/hw/ppc/vof.h
@@ -18,6 +18,8 @@ typedef struct Vof {
     GHashTable *of_instances; /* ihandle -> SpaprOfInstance */
     uint32_t of_instance_last;
     char *bootargs;
+    char *bootpath;
+    bool disk_boot;
     long fw_size;
 } Vof;
 
-- 
2.54.0



  parent reply	other threads:[~2026-08-17 10:30 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-17 10:27 [RFC PATCH 0/8] ppc/spapr: VOF disk image (qcow2) boot support uverma
2026-08-17 10:27 ` [RFC PATCH 1/8] ppc/spapr: add PReP boot partition detection uverma
2026-08-17 10:27 ` [RFC PATCH 2/8] hw/loader: add load_elf_ram_sym_buf() for in-memory ELF loading uverma
2026-08-17 10:27 ` uverma [this message]
2026-08-17 10:27 ` [RFC PATCH 4/8] ppc/spapr: add VTY backend support to OF read/write/open services uverma
2026-08-17 10:27 ` [RFC PATCH 5/8] ppc/spapr: add block device backend to VOF open/read/write/seek services uverma
2026-08-17 10:27 ` [RFC PATCH 6/8] spapr_vscsi: add VOF disk nodes to the device tree uverma
2026-08-17 10:27 ` [RFC PATCH 7/8] ppc/spapr: strip OF path argument suffix in path_offset uverma
2026-08-17 10:27 ` [RFC PATCH 8/8] ppc/spapr: implement vscsi-report-luns call-method for PAPR vSCSI uverma
2026-08-19  6:55 ` [RFC PATCH 0/8] ppc/spapr: VOF disk image (qcow2) boot support Utkarsh Verma

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260817102733.605346-4-uverma@linux.ibm.com \
    --to=uverma@linux.ibm.com \
    --cc=aik@ozlabs.ru \
    --cc=amachhiw@linux.ibm.com \
    --cc=balaton@eik.bme.hu \
    --cc=fam@euphon.net \
    --cc=harshpb@linux.ibm.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=nnmlinux@linux.ibm.com \
    --cc=npiggin@gmail.com \
    --cc=pbonzini@redhat.com \
    --cc=philmd@oss.qualcomm.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-ppc@nongnu.org \
    --cc=rathc@linux.ibm.com \
    --cc=sbhat@linux.ibm.com \
    --cc=th.huth@posteo.eu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox