[RFC PATCH 5/8] ppc/spapr: add block device backend to VOF open/read/write/seek services
[email protected] Mon, 17 Aug 2026 15:57:22 +0530
Newsgroups
gmane.comp.emulators.qemu
Message-ID
<[email protected] >
From: Utkarsh Verma <[email protected] >
Extend the VOF client interface to support block device I/O through the
sPAPR SCSI disk, which is needed by bootloaders such as GRUB during VOF
disk boot.
Add BlockBackend and position tracking fields to OfInstance and use them
in vof_seek(), vof_write() and vof_read() to extend their functionality
to handle block devices as well.
In vof_do_open(), detect FDT nodes named "disk@<srp-lun>" and resolve
the SRP LUN encoding to the matching SCSIDevice/BlockBackend by scanning
id/channel/lun.
Add vof_seek() to implement the OpenFirmware "seek" client service,
which was previously missing.
AI-used-for: code
Signed-off-by: Utkarsh Verma <[email protected] >
---
hw/ppc/trace-events | 2 +
hw/ppc/vof.c | 116 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index dbdcfada26..a61398ecb2 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -78,6 +78,7 @@ vof_error_unknown_service(const char *service, int nargs, int nret) "\"%s\" args
vof_error_unknown_method(const char *method) "\"%s\""
vof_error_unknown_ihandle_close(uint32_t ih) "ih=0x%x"
vof_error_unknown_path(const char *path) "\"%s\""
+vof_error_seek(uint32_t ih) "ih=0x%x"
vof_error_write(uint32_t ih) "ih=0x%x"
vof_error_read(uint32_t ih) "ih=0x%x"
vof_finddevice(const char *path, uint32_t ph) "\"%s\" => ph=0x%x"
@@ -92,6 +93,7 @@ vof_interpret(const char *cmd, uint32_t param1, uint32_t param2, uint32_t ret, u
vof_package_to_path(uint32_t ph, const char *tmp, int ret) "ph=0x%x => %s len=%d"
vof_instance_to_path(uint32_t ih, uint32_t ph, const char *tmp, int ret) "ih=0x%x ph=0x%x => %s len=%d"
vof_instance_to_package(uint32_t ih, uint32_t ph) "ih=0x%x => ph=0x%x"
+vof_seek(uint32_t ih, uint64_t pos) "ih=0x%x pos=0x%"PRIx64
vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\""
vof_read(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\""
vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64
diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c
index 45c197df9f..b1c574aabf 100644
--- a/hw/ppc/vof.c
+++ b/hw/ppc/vof.c
@@ -11,6 +11,7 @@
#include CONFIG_DEVICES /* CONFIG_PSERIES */
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
#include "qemu/timer.h"
#include "qemu/range.h"
#include "qemu/units.h"
@@ -24,6 +25,8 @@
#include "trace.h"
#include "hw/ppc/spapr_vio.h"
+#include "hw/scsi/scsi.h"
+#include "system/block-backend.h"
#include <libfdt.h>
/*
@@ -46,6 +49,8 @@ typedef struct {
typedef struct {
char *path; /* the path used to open the instance */
uint32_t phandle;
+ BlockBackend *blk;
+ uint64_t pos; /* current position for seek operations */
void *vty;
} OfInstance;
@@ -449,6 +454,7 @@ static uint32_t vof_do_open(void *fdt, Vof *vof, int offset, const char *path)
{
uint32_t ret = PROM_ERROR;
OfInstance *inst = NULL;
+ const char *node_name;
if (vof->of_instance_last == 0xFFFFFFFF) {
/* We do not recycle ihandles yet */
@@ -461,10 +467,42 @@ static uint32_t vof_do_open(void *fdt, Vof *vof, int offset, const char *path)
++vof->of_instance_last;
inst->path = g_strdup(path);
+ inst->blk = NULL;
+ inst->pos = 0;
inst->vty = NULL;
+ node_name = fdt_get_name(fdt, offset, NULL);
+
+ if (node_name && strncmp(node_name, "disk@", 5) == 0) {
+ uint64_t srp_lun;
+ uint32_t id, channel, lun;
+ BlockBackend *blk;
+
+ if (qemu_strtou64(node_name + 5, NULL, 16, &srp_lun) == 0) {
+ id = (srp_lun >> 56) & 0x3f;
+ channel = (srp_lun >> 53) & 0x7;
+ lun = (srp_lun >> 48) & 0x1f;
+
+ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+ DeviceState *attached = blk_get_attached_dev(blk);
+ SCSIDevice *sdev;
+
+ if (!attached) {
+ continue;
+ }
+ sdev = (SCSIDevice *)object_dynamic_cast(OBJECT(attached),
+ TYPE_SCSI_DEVICE);
+ if (sdev && sdev->id == (int)id &&
+ sdev->channel == (int)channel &&
+ sdev->lun == (int)lun) {
+ inst->blk = blk;
+ break;
+ }
+ }
+ }
+ }
+
#ifdef CONFIG_PSERIES
- const char *node_name = fdt_get_name(fdt, offset, NULL);
if (node_name && strncmp(node_name, "vty", 3) == 0) {
uint8_t discard_buf[VOF_VTY_BUF_SIZE];
MachineState *ms = MACHINE(qdev_get_machine());
@@ -601,6 +639,25 @@ static uint32_t vof_write(Vof *vof, uint32_t ihandle, uint32_t buf,
return PROM_ERROR;
}
+ if (inst->blk) {
+ g_autofree uint8_t *blkbuf = g_malloc(len);
+ int ret;
+
+ if (VOF_MEM_READ(buf, blkbuf, len) != MEMTX_OK) {
+ trace_vof_error_write(ihandle);
+ return PROM_ERROR;
+ }
+ ret = blk_pwrite(inst->blk, inst->pos, len, blkbuf, 0);
+ if (ret < 0) {
+ trace_vof_error_write(ihandle);
+ return PROM_ERROR;
+ }
+ blk_flush(inst->blk);
+ inst->pos += len;
+ trace_vof_write(ihandle, len, "(disk)");
+ return len;
+ }
+
#ifdef CONFIG_PSERIES
if (inst->vty) {
uint32_t total_written = 0;
@@ -646,6 +703,26 @@ static uint32_t vof_read(Vof *vof, uint32_t ihandle, uint32_t buf,
return PROM_ERROR;
}
+ if (inst->blk) {
+ g_autofree uint8_t *tmp = g_malloc(len);
+ int ret;
+
+ ret = blk_pread(inst->blk, inst->pos, len, tmp, 0);
+ if (ret < 0) {
+ trace_vof_error_read(ihandle);
+ return PROM_ERROR;
+ }
+
+ if (VOF_MEM_WRITE(buf, tmp, len) != MEMTX_OK) {
+ trace_vof_error_read(ihandle);
+ return PROM_ERROR;
+ }
+
+ inst->pos += len;
+ trace_vof_read(ihandle, len, "(disk)");
+ return len;
+ }
+
#ifdef CONFIG_PSERIES
if (inst->vty) {
uint8_t tmp[VOF_VTY_BUF_SIZE];
@@ -675,6 +752,41 @@ static uint32_t vof_read(Vof *vof, uint32_t ihandle, uint32_t buf,
return 0;
}
+static uint32_t vof_seek(Vof *vof, uint32_t ihandle, uint32_t pos_hi,
+ uint32_t pos_lo)
+{
+ OfInstance *inst = (OfInstance *)
+ g_hash_table_lookup(vof->of_instances, GINT_TO_POINTER(ihandle));
+ uint64_t pos = ((uint64_t)pos_hi << 32) | pos_lo;
+
+ if (!inst) {
+ trace_vof_error_seek(ihandle);
+ return PROM_ERROR;
+ }
+
+ if (inst->blk) {
+ int64_t size = blk_getlength(inst->blk);
+
+ if (size < 0) {
+ trace_vof_error_seek(ihandle);
+ return PROM_ERROR;
+ }
+
+ if (pos > (uint64_t)size) {
+ trace_vof_error_seek(ihandle);
+ return PROM_ERROR;
+ }
+
+ inst->pos = pos;
+ trace_vof_seek(ihandle, pos);
+ return 0;
+ }
+
+ /* VTY and other devices don't support seek */
+ trace_vof_error_seek(ihandle);
+ return PROM_ERROR;
+}
+
static void vof_claimed_dump(GArray *claimed)
{
int i;
@@ -985,6 +1097,8 @@ static uint32_t vof_client_handle(MachineState *ms, void *fdt, Vof *vof,
ret = vof_package_to_path(fdt, args[0], args[1], args[2]);
} else if (cmpserv("instance-to-path", 3, 1)) {
ret = vof_instance_to_path(fdt, vof, args[0], args[1], args[2]);
+ } else if (cmpserv("seek", 3, 1)) {
+ ret = vof_seek(vof, args[0], args[1], args[2]);
} else if (cmpserv("write", 3, 1)) {
ret = vof_write(vof, args[0], args[1], args[2]);
} else if (cmpserv("read", 3, 1)) {
--
2.54.0