[PATCH v3 #2a/10] Adding LASI's NCR710 SCSI Controller Wrapper.

Soumyajyotii Ssarkar posted 1 patch 3 weeks, 2 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20251022105703.12663-2-soumyajyotisarkar23@gmail.com
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Fam Zheng <fam@euphon.net>
hw/scsi/lasi_ncr710.c | 286 ++++++++++++++++++++++++++++++++++++++++++
hw/scsi/lasi_ncr710.h |  61 +++++++++
hw/scsi/trace-events  |  17 +++
3 files changed, 364 insertions(+)
create mode 100644 hw/scsi/lasi_ncr710.c
create mode 100644 hw/scsi/lasi_ncr710.h
[PATCH v3 #2a/10] Adding LASI's NCR710 SCSI Controller Wrapper.
Posted by Soumyajyotii Ssarkar 3 weeks, 2 days ago
>> Changes since v1:
>> - Removed testing callback timer.
>> - Improved Scsi fifo documentation and implementation.
>> - Fixed Sync/Async functionality.

Changes since v2:
- Breaking down the NCR710 SCSI Controller into two patches [PATCH v3 #2a/10] and [PATCH v3 #2b/10]. 
- Since the intial v2 patch was too long.

[PATCH v3 #2a/10] 
- Adding the Lasi-Wrapper for the NCR710 SCSI Controller.
- Adding trace-events for the LASI's wrapper for NCR710 SCSI Controller.

[PATCH v3 #2b/10]
- Adding the core NCR710 SCSI Controller driver code.
- The previous patch added the code for LASI to access this driver,
  while the core patch is generic code which could be used for other machines
  as well.
- Adding trace-events for the NCR710 Core.

---
 hw/scsi/lasi_ncr710.c | 286 ++++++++++++++++++++++++++++++++++++++++++
 hw/scsi/lasi_ncr710.h |  61 +++++++++
 hw/scsi/trace-events  |  17 +++
 3 files changed, 364 insertions(+)
 create mode 100644 hw/scsi/lasi_ncr710.c
 create mode 100644 hw/scsi/lasi_ncr710.h

diff --git a/hw/scsi/lasi_ncr710.c b/hw/scsi/lasi_ncr710.c
new file mode 100644
index 0000000000..5a1b667170
--- /dev/null
+++ b/hw/scsi/lasi_ncr710.c
@@ -0,0 +1,286 @@
+/*
+ * LASI Wrapper for NCR710 SCSI Controller
+ *
+ * Copyright (c) 2025 Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
+ * This driver was developed during the Google Summer of Code 2025 program.
+ * Mentored by Helge Deller <deller@gmx.de>
+ *
+ * NCR710 SCSI Controller implementation
+ * Based on the NCR53C710 Technical Manual Version 3.2, December 2000
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/scsi/lasi_ncr710.h"
+#include "hw/scsi/ncr53c710.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "system/blockdev.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "system/dma.h"
+
+#define LASI_710_SVERSION    0x00082
+#define SCNR                 0xBEEFBABE
+#define LASI_710_HVERSION    0x3D
+#define HPHW_FIO             5        /* Fixed I/O module */
+
+static uint64_t lasi_ncr710_reg_read(void *opaque, hwaddr addr,
+                                    unsigned size)
+{
+    LasiNCR710State *s = LASI_NCR710(opaque);
+    uint64_t val = 0;
+
+    trace_lasi_ncr710_reg_read(addr, 0, size);
+
+    if (addr == 0x00) {  /* Device ID */
+        val = (HPHW_FIO << 24) | LASI_710_SVERSION;
+        trace_lasi_ncr710_reg_read_id(HPHW_FIO, LASI_710_SVERSION, val);
+        return val;
+    }
+
+    if (addr == 0x08) {  /* HVersion */
+        val = LASI_710_HVERSION;
+        trace_lasi_ncr710_reg_read_hversion(val);
+        return val;
+    }
+
+    if (addr >= 0x100) {
+        hwaddr ncr_addr = addr - 0x100;
+        if (size == 1) {
+            ncr_addr ^= 3;
+            NCR710_DPRINTF("Reading value to LASI WRAPPER == 0x%lx%s, "
+                           "val=0x%lx, size=%u\n",
+                           addr - 0x100, size == 1 ? " (XORed)" : "",
+                           val, size);
+            val = ncr710_reg_read(&s->ncr710, ncr_addr, size);
+        } else {
+            val = 0;
+            for (unsigned i = 0; i < size; i++) {
+                uint8_t byte_val = ncr710_reg_read(&s->ncr710, ncr_addr + i, 1);
+                val |= ((uint64_t)byte_val) << (i * 8);
+                NCR710_DPRINTF("  Read byte %u from NCR addr 0x%lx: "
+                               "0x%02x\n", i, ncr_addr + i, byte_val);
+            }
+            NCR710_DPRINTF("  Reconstructed %u-byte value: 0x%lx\n",
+                           size, val);
+        }
+
+        trace_lasi_ncr710_reg_forward_read(addr, val);
+    } else {
+        val = 0;
+        trace_lasi_ncr710_reg_read(addr, val, size);
+    }
+    return val;
+}
+
+static void lasi_ncr710_reg_write(void *opaque, hwaddr addr,
+                                   uint64_t val, unsigned size)
+{
+    LasiNCR710State *s = LASI_NCR710(opaque);
+
+    trace_lasi_ncr710_reg_write(addr, val, size);
+
+    if (addr <= 0x0F) {
+        return;
+    }
+
+    if (addr >= 0x100) {
+        hwaddr ncr_addr = addr - 0x100;
+
+        if (size == 1) {
+            ncr_addr ^= 3;
+            NCR710_DPRINTF("Writing value to LASI WRAPPER == 0x%lx%s, "
+                           "val=0x%lx, size=%u\n",
+                           addr - 0x100, size == 1 ? " (XORed)" : "",
+                           val, size);
+            ncr710_reg_write(&s->ncr710, ncr_addr, val, size);
+        } else {
+            for (unsigned i = 0; i < size; i++) {
+                uint8_t byte_val = (val >> (i * 8)) & 0xff;
+                 NCR710_DPRINTF("  Writing byte %u to NCR addr 0x%lx: 0x%02x\n",
+                       i, ncr_addr + i, byte_val);
+                ncr710_reg_write(&s->ncr710, ncr_addr + i, byte_val, 1);
+            }
+        }
+
+        trace_lasi_ncr710_reg_forward_write(addr, val);
+    } else {
+        trace_lasi_ncr710_reg_write(addr, val, size);
+    }
+}
+
+/*
+ * req_cancelled, command_complete, transfer_data forwards
+ * commands to its core counterparts.
+ */
+static void lasi_ncr710_request_cancelled(SCSIRequest *req)
+{
+    trace_lasi_ncr710_request_cancelled(req);
+    ncr710_request_cancelled(req);
+}
+
+static void lasi_ncr710_command_complete(SCSIRequest *req, size_t resid)
+{
+    trace_lasi_ncr710_command_complete(req->status, resid);
+    ncr710_command_complete(req, resid);
+}
+
+ static void lasi_ncr710_transfer_data(SCSIRequest *req, uint32_t len)
+{
+    trace_lasi_ncr710_transfer_data(len);
+    ncr710_transfer_data(req, len);
+}
+
+static const struct SCSIBusInfo lasi_ncr710_scsi_info = {
+    .tcq = true,
+    .max_target = 8,
+    .max_lun = 8,  /* full LUN support */
+
+    .transfer_data = lasi_ncr710_transfer_data,
+    .complete = lasi_ncr710_command_complete,
+    .cancel = lasi_ncr710_request_cancelled,
+};
+
+static const MemoryRegionOps lasi_ncr710_mmio_ops = {
+    .read = lasi_ncr710_reg_read,
+    .write = lasi_ncr710_reg_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static const VMStateDescription vmstate_lasi_ncr710 = {
+    .name = "lasi-ncr710",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void lasi_ncr710_realize(DeviceState *dev, Error **errp)
+{
+    LasiNCR710State *s = LASI_NCR710(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    trace_lasi_ncr710_device_realize();
+
+    scsi_bus_init(&s->ncr710.bus, sizeof(s->ncr710.bus), dev,
+                  &lasi_ncr710_scsi_info);
+    s->ncr710.as = &address_space_memory;
+    s->ncr710.irq = s->lasi_irq;
+
+    s->ncr710.reselection_retry_timer =
+        timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                     ncr710_reselection_retry_callback,
+                     &s->ncr710);
+
+    ncr710_soft_reset(&s->ncr710);
+
+    trace_lasi_ncr710_timers_initialized(
+        (uint64_t)s->ncr710.reselection_retry_timer);
+
+    /* Initialize memory region */
+    memory_region_init_io(&s->mmio, OBJECT(dev), &lasi_ncr710_mmio_ops, s,
+                          "lasi-ncr710", 0x200);
+    sysbus_init_mmio(sbd, &s->mmio);
+}
+
+void lasi_ncr710_handle_legacy_cmdline(DeviceState *lasi_dev)
+{
+    LasiNCR710State *s = LASI_NCR710(lasi_dev);
+    SCSIBus *bus = &s->ncr710.bus;
+    int found_drives = 0;
+
+    if (!bus) {
+        return;
+    }
+
+    for (int unit = 0; unit <= 7; unit++) {
+        DriveInfo *dinfo = drive_get(IF_SCSI, bus->busnr, unit);
+        if (dinfo) {
+            trace_lasi_ncr710_legacy_drive_found(bus->busnr, unit);
+            found_drives++;
+        }
+    }
+
+    trace_lasi_ncr710_handle_legacy_cmdline(bus->busnr, found_drives);
+
+    scsi_bus_legacy_handle_cmdline(bus);
+    BusChild *kid;
+    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+        trace_lasi_ncr710_scsi_device_created(
+            object_get_typename(OBJECT(kid->child)));
+    }
+}
+
+DeviceState *lasi_ncr710_init(MemoryRegion *addr_space, hwaddr hpa,
+                               qemu_irq irq)
+{
+    DeviceState *dev;
+    LasiNCR710State *s;
+    SysBusDevice *sbd;
+
+    dev = qdev_new(TYPE_LASI_NCR710);
+    s = LASI_NCR710(dev);
+    sbd = SYS_BUS_DEVICE(dev);
+    s->lasi_irq = irq;
+    sysbus_realize_and_unref(sbd, &error_fatal);
+    memory_region_add_subregion(addr_space, hpa,
+                               sysbus_mmio_get_region(sbd, 0));
+    return dev;
+}
+
+static void lasi_ncr710_reset(DeviceState *dev)
+{
+    LasiNCR710State *s = LASI_NCR710(dev);
+    trace_lasi_ncr710_device_reset();
+    ncr710_soft_reset(&s->ncr710);
+}
+
+static void lasi_ncr710_instance_init(Object *obj)
+{
+    LasiNCR710State *s = LASI_NCR710(obj);
+
+    s->hw_type = HPHW_FIO;
+    s->sversion = LASI_710_SVERSION;
+    s->hversion = LASI_710_HVERSION;
+}
+
+static void lasi_ncr710_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = lasi_ncr710_realize;
+    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+    dc->fw_name = "scsi";
+    dc->desc = "HP-PARISC LASI NCR710 SCSI adapter";
+    device_class_set_legacy_reset(dc, lasi_ncr710_reset);
+    dc->vmsd = &vmstate_lasi_ncr710;
+    dc->user_creatable = false;
+}
+
+static const TypeInfo lasi_ncr710_info = {
+    .name          = TYPE_LASI_NCR710,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(LasiNCR710State),
+    .instance_init = lasi_ncr710_instance_init,
+    .class_init    = lasi_ncr710_class_init,
+};
+
+static void lasi_ncr710_register_types(void)
+{
+    type_register_static(&lasi_ncr710_info);
+}
+
+type_init(lasi_ncr710_register_types)
diff --git a/hw/scsi/lasi_ncr710.h b/hw/scsi/lasi_ncr710.h
new file mode 100644
index 0000000000..26e3105244
--- /dev/null
+++ b/hw/scsi/lasi_ncr710.h
@@ -0,0 +1,61 @@
+/*
+ * LASI Wrapper for NCR710 SCSI Controller
+ *
+ * Copyright (c) 2025 Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
+ * This driver was developed during the Google Summer of Code 2025 program.
+ * Mentored by Helge Deller <deller@gmx.de>
+ *
+ * NCR710 SCSI Controller implementation
+ * Based on the NCR53C710 Technical Manual Version 3.2, December 2000
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef HW_LASI_NCR710_H
+#define HW_LASI_NCR710_H
+
+#include "hw/sysbus.h"
+#include "qemu/osdep.h"
+#include "exec/memattrs.h"
+#include "hw/scsi/scsi.h"
+#include "hw/scsi/ncr53c710.h"
+
+#define TYPE_LASI_NCR710 "lasi-ncr710"
+OBJECT_DECLARE_SIMPLE_TYPE(LasiNCR710State, LASI_NCR710)
+
+#define LASI_SCSI_RESET         0x000   /* SCSI Reset Register */
+#define LASI_SCSI_NCR710_BASE   0x100   /* NCR710 Base Register Offset */
+
+#define PARISC_DEVICE_ID_OFF    0x00    /* HW type, HVERSION, SVERSION */
+#define PARISC_DEVICE_CONFIG_OFF 0x04   /* Configuration data */
+
+#define PHASE_MASK              7
+#define PHASE_DO                0
+
+#define NCR710_SCNTL1_RST       0x08    /* SCSI Reset */
+#define NCR710_ISTAT_RST        0x40    /* Device Reset */
+#define NCR710_ISTAT_ABRT       0x80    /* Script Abort */
+#define NCR710_ISTAT_CON        0x08    /* ISTAT_Connected */
+#define NCR710_DSTAT_DFE        0x80    /* DMA FIFO Empty */
+#define NCR710_CTEST2_DACK      0x01    /* DMA Acknowledge */
+
+typedef struct LasiNCR710State {
+    SysBusDevice parent_obj;
+    MemoryRegion mmio;
+    qemu_irq lasi_irq;       /* IRQ line to LASI controller */
+    uint32_t hw_type;        /* Hardware type (HPHW_*) */
+    uint32_t sversion;       /* Software version */
+    uint32_t hversion;       /* Hardware version */
+    SCSIBus bus;
+    NCR710State ncr710;
+} LasiNCR710State;
+
+DeviceState *lasi_ncr710_init(MemoryRegion *addr_space, hwaddr hpa,
+                               qemu_irq irq);
+void lasi_ncr710_handle_legacy_cmdline(DeviceState *lasi_dev);
+
+#endif
diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events
index 6c2788e202..0604050a67 100644
--- a/hw/scsi/trace-events
+++ b/hw/scsi/trace-events
@@ -306,6 +306,23 @@ lsi_reg_write(const char *name, int offset, uint8_t val) "Write reg %s 0x%x = 0x
 lsi_scripts_timer_triggered(void) "SCRIPTS timer triggered"
 lsi_scripts_timer_start(void) "SCRIPTS timer started"
 
+# lasi_ncr710.c
+lasi_ncr710_device_realize(void) "Device realized"
+lasi_ncr710_device_reset(void) "Device reset"
+lasi_ncr710_reg_read(uint32_t addr, uint32_t val, unsigned size) "addr=0x%03x val=0x%08x size=%u"
+lasi_ncr710_reg_write(uint32_t addr, uint32_t val, unsigned size) "addr=0x%03x val=0x%08x size=%u"
+lasi_ncr710_reg_read_id(uint32_t hw_type, uint32_t sversion, uint32_t val) "hw_type=%u sversion=0x%04x val=0x%08x"
+lasi_ncr710_reg_read_hversion(uint32_t hversion) "LASI NCR710: HVersion read -> 0x%02x"
+lasi_ncr710_reg_forward_read(uint32_t addr, uint32_t val) "LASI NCR710: Forward read to NCR710 core addr=0x%03x val=0x%08x"
+lasi_ncr710_reg_forward_write(uint32_t addr, uint32_t val) "LASI NCR710: Forward write to NCR710 core addr=0x%03x val=0x%08x"
+lasi_ncr710_command_complete(uint32_t status, size_t resid) "LASI NCR710: Command complete status=0x%02x resid=%zu"
+lasi_ncr710_transfer_data(uint32_t len) "LASI NCR710: Transfer data len=%u"
+lasi_ncr710_request_cancelled(void *req) "LASI NCR710: Request cancelled req=%p"
+lasi_ncr710_timers_initialized(uint64_t reselection) "Timers: reselection=0x%" PRIx64
+lasi_ncr710_handle_legacy_cmdline(int busnr, int found_drives) "LASI NCR710: Handle legacy cmdline busnr=%d found_drives=%d"
+lasi_ncr710_legacy_drive_found(int busnr, int unit) "LASI NCR710: Found legacy drive at bus=%d unit=%d"
+lasi_ncr710_scsi_device_created(const char *type) "LASI NCR710: SCSI device created: %s"
+
 # virtio-scsi.c
 virtio_scsi_cmd_req(int lun, uint32_t tag, uint8_t cmd) "virtio_scsi_cmd_req lun=%u tag=0x%x cmd=0x%x"
 virtio_scsi_cmd_resp(int lun, uint32_t tag, int response, uint8_t status) "virtio_scsi_cmd_resp lun=%u tag=0x%x response=%d status=0x%x"
-- 
2.49.0