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 8/8] ppc/spapr: implement vscsi-report-luns call-method for PAPR vSCSI
Date: Mon, 17 Aug 2026 15:57:25 +0530 [thread overview]
Message-ID: <20260817102733.605346-9-uverma@linux.ibm.com> (raw)
In-Reply-To: <20260817102733.605346-1-uverma@linux.ibm.com>
From: Utkarsh Verma <uverma@linux.ibm.com>
GRUB (grub-core/disk/ieee1275/ofdisk.c) calls "vscsi-report-luns" on
any PAPR virtual SCSI controller ihandle to enumerate attached LUNs
before attempting to boot from disk. Without this method VOF returns
PROM_ERROR and GRUB cannot find vSCSI disks.
Add vof_find_vscsi_bus() to locate the SCSIBus from a v-scsi@<reg>
instance path, and vof_vscsi_report_luns() to build the response table
in the VOF firmware region (0..fw_size), which GRUB never claims.
The table layout and SRP LUN encoding follow SLOF (vio-vscsi.fs
dev-generate-srplun) each entry is an 8-byte big-endian cell whose
low 4 bytes hold a guest pointer to a null-terminated list of SRP LUNs
for that target; GRUB reads the pointer at offset table + 4 + 8*i.
AI-used-for: code
Signed-off-by: Utkarsh Verma <uverma@linux.ibm.com>
---
hw/ppc/vof.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 212 insertions(+)
diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c
index d3b0df30e4..b1bf5b1af0 100644
--- a/hw/ppc/vof.c
+++ b/hw/ppc/vof.c
@@ -991,6 +991,198 @@ static void vof_instantiate_rtas(Error **errp)
error_setg(errp, "The firmware should have instantiated RTAS");
}
+#ifdef CONFIG_PSERIES
+/* Combined (channel<<6)|id index space, matches SLOF dev-max-target. */
+#define VSCSI_MAX_TARGETS 512
+
+/*
+ * Build the vscsi-report-luns response in guest memory.
+ *
+ * Returns catch_result (0 = success). On success, *nentries_out and
+ * *table_addr_out describe the result table written into the VOF firmware
+ * region (0..fw_size), which GRUB never claims.
+ *
+ * Table layout (big-endian, 8 bytes per entry):
+ * [table_addr + 8*i + 0..3] : 0x00000000
+ * [table_addr + 8*i + 4..7] : guest pointer to null-terminated SRP LUN list
+ *
+ * GRUB reads each pointer as: *(uint32_t *)(table + 4 + 8 * i)
+ *
+ * SRP LUN encoding (SLOF dev-generate-srplun):
+ * srplun = (0x8000 | bus | target | lun) << 48
+ * where bus = (combined_target >> 1) & 0x70
+ * target = (combined_target & 0x3f) << 8
+ * combined_target = (channel << 6) | id
+ */
+static uint32_t vof_vscsi_report_luns(Vof *vof,
+ SCSIBus *sbus,
+ uint32_t *nentries_out,
+ uint32_t *table_addr_out)
+{
+ uint64_t *lun_lists[VSCSI_MAX_TARGETS];
+ int lun_counts[VSCSI_MAX_TARGETS];
+ uint32_t ptr_table_size;
+ uint32_t lun_data_size;
+ uint32_t total_size;
+ uint32_t base_addr;
+ uint32_t lun_data_base;
+ uint32_t lun_data_off;
+ uint32_t table_idx;
+ uint32_t table_addr;
+ BusChild *kid;
+ int nentries = 0;
+ int t;
+
+ memset(lun_lists, 0, sizeof(lun_lists));
+ memset(lun_counts, 0, sizeof(lun_counts));
+
+ QTAILQ_FOREACH(kid, &sbus->qbus.children, sibling) {
+ SCSIDevice *dev = SCSI_DEVICE(kid->child);
+ int combined_target;
+ uint64_t bus_field;
+ uint64_t target_field;
+ uint64_t lun_field;
+ uint64_t srplun;
+ int cnt;
+
+ /* combined_target = (channel << 6) | id, matching SLOF bus+target */
+ combined_target = ((dev->channel & 0x7) << 6) | (dev->id & 0x3f);
+ if (combined_target >= VSCSI_MAX_TARGETS) {
+ continue;
+ }
+
+ bus_field = ((uint64_t)combined_target >> 1) & 0x70ULL;
+ target_field = ((uint64_t)combined_target & 0x3fULL) << 8;
+ lun_field = (uint64_t)(dev->lun & 0x1f);
+ srplun = (0x8000ULL | bus_field | target_field | lun_field) << 48;
+
+ cnt = lun_counts[combined_target];
+ lun_lists[combined_target] = g_realloc(lun_lists[combined_target],
+ (cnt + 2) * sizeof(uint64_t));
+ lun_lists[combined_target][cnt] = cpu_to_be64(srplun);
+ lun_lists[combined_target][cnt + 1] = 0;
+ lun_counts[combined_target]++;
+ if (cnt == 0) {
+ nentries++;
+ }
+ }
+
+ if (nentries == 0) {
+ *nentries_out = 0;
+ *table_addr_out = 0;
+ return 0;
+ }
+
+ ptr_table_size = nentries * sizeof(uint64_t);
+ lun_data_size = 0;
+ for (t = 0; t < VSCSI_MAX_TARGETS; t++) {
+ if (lun_lists[t]) {
+ lun_data_size += (lun_counts[t] + 1) * sizeof(uint64_t);
+ }
+ }
+ total_size = ptr_table_size + lun_data_size;
+
+ /*
+ * Place the table in the VOF firmware region (0..fw_size), which GRUB
+ * never claims. Fit into the top of the first 4 KB page, 8-byte aligned.
+ */
+ if (total_size <= 0x1000 - (uint32_t)vof->fw_size) {
+ base_addr = (0x1000 - total_size) & ~7U;
+ } else {
+ base_addr = 0x800;
+ }
+
+ table_addr = base_addr;
+ lun_data_base = base_addr + ptr_table_size;
+ lun_data_off = 0;
+ table_idx = 0;
+
+ for (t = 0; t < VSCSI_MAX_TARGETS; t++) {
+ uint32_t lun_buf_size;
+ uint32_t lun_buf_addr;
+ uint64_t cell_be64;
+
+ if (!lun_lists[t]) {
+ continue;
+ }
+
+ lun_buf_size = (lun_counts[t] + 1) * sizeof(uint64_t);
+ lun_buf_addr = lun_data_base + lun_data_off;
+
+ if (VOF_MEM_WRITE(lun_buf_addr, lun_lists[t], lun_buf_size)
+ != MEMTX_OK) {
+ goto write_err;
+ }
+
+ cell_be64 = cpu_to_be64((uint64_t)lun_buf_addr);
+ if (VOF_MEM_WRITE(table_addr + table_idx * sizeof(uint64_t),
+ &cell_be64, sizeof(cell_be64)) != MEMTX_OK) {
+ goto write_err;
+ }
+
+ lun_data_off += lun_buf_size;
+ table_idx++;
+ }
+
+ *nentries_out = table_idx;
+ *table_addr_out = table_addr;
+
+ for (t = 0; t < VSCSI_MAX_TARGETS; t++) {
+ g_free(lun_lists[t]);
+ }
+ return 0;
+
+write_err:
+ for (t = 0; t < VSCSI_MAX_TARGETS; t++) {
+ g_free(lun_lists[t]);
+ }
+ return PROM_ERROR;
+}
+
+/*
+ * Return the SCSIBus for a v-scsi@<reg> path, or NULL if not found.
+ * Handles both "/vdevice/v-scsi@<reg>" and ".../v-scsi@<reg>/disk@..." paths.
+ */
+static SCSIBus *vof_find_vscsi_bus(MachineState *ms, const char *path)
+{
+ SpaprMachineState *spapr = SPAPR_MACHINE(ms);
+ const char *at;
+ const char *endptr;
+ unsigned long reg;
+ SpaprVioDevice *vdev;
+ BusState *bus;
+
+ if (!spapr || !spapr->vio_bus) {
+ return NULL;
+ }
+
+ at = strstr(path, "v-scsi@");
+ if (!at) {
+ return NULL;
+ }
+ at = strchr(at, '@');
+ if (!at) {
+ return NULL;
+ }
+
+ if (qemu_strtoul(at + 1, &endptr, 16, ®) || endptr == at + 1) {
+ return NULL;
+ }
+
+ vdev = spapr_vio_find_by_reg(spapr->vio_bus, (uint32_t)reg);
+ if (!vdev) {
+ return NULL;
+ }
+
+ QLIST_FOREACH(bus, &vdev->qdev.child_bus, sibling) {
+ if (object_dynamic_cast(OBJECT(bus), TYPE_SCSI_BUS)) {
+ return SCSI_BUS(bus);
+ }
+ }
+ return NULL;
+}
+#endif /* CONFIG_PSERIES */
+
static uint32_t vof_call_method(MachineState *ms, Vof *vof, uint32_t methodaddr,
uint32_t ihandle, uint32_t param1,
uint32_t param2, uint32_t param3,
@@ -1014,6 +1206,26 @@ static uint32_t vof_call_method(MachineState *ms, Vof *vof, uint32_t methodaddr,
goto trace_exit;
}
+#ifdef CONFIG_PSERIES
+ /* vscsi-report-luns: enumerate LUNs for GRUB */
+ if (strcmp(method, "vscsi-report-luns") == 0) {
+ SCSIBus *sbus = vof_find_vscsi_bus(ms, inst->path);
+
+ if (sbus) {
+ uint32_t nentries = 0, table_addr = 0;
+ ret = vof_vscsi_report_luns(vof, sbus, &nentries, &table_addr);
+ ret2[0] = nentries;
+ ret2[1] = table_addr;
+ } else {
+ ret = 1;
+ ret2[0] = 0;
+ ret2[1] = 0;
+ }
+ trace_vof_method(ihandle, method, param1, ret, ret2[0]);
+ goto trace_exit;
+ }
+#endif /* CONFIG_PSERIES */
+
if (strcmp(inst->path, "/") == 0) {
if (strcmp(method, "ibm,client-architecture-support") == 0) {
Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
--
2.54.0
prev parent reply other threads:[~2026-08-17 10:30 UTC|newest]
Thread overview: 9+ 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 ` [RFC PATCH 3/8] ppc/spapr: add baseline VOF disk boot support uverma
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 ` uverma [this message]
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-9-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.