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


Reply via email to