HP-PARISC 715 machine with NCR710 SCSI chip

This series adds a new emulation for a HP PA-RISC 715/64 model,
 as descrived here: https://www.openpa.net/systems/hp-9000_715.html.
 
 That machine has no PCI bus and instead uses a "LASI" chip which has built-in
 NCR 53c710 SCSI and i82596 network chips. Compared to the other already
 emulated machines B160L and C3700, this machine should be able to support older
 operating systems like HP-UX 9 as well.
 
 The QEMU project participated in the Google Summer of Code 2025 program by
 "Implementing LASI Network Card and NCR 710 SCSI Controller Device Models", and
 Soumyajyotii Ssarkar stepped up to develop those drivers.
 
 This patch series includes the code for the NCR710 SCSI controller,
 the network code will follow in later patch series.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQS86RI+GtKfB8BJu973ErUQojoPXwUCaP/g8wAKCRD3ErUQojoP
 X1hOAP9WOYVNz7UIpNztQ9p3tUKApALaPR3aaiORMLrjktaDMQD/TIHExaKIKtn6
 iLy+RXzPFdpqmI/cBQx57q8lWzUd2QI=
 =Y7lX
 -----END PGP SIGNATURE-----

Merge tag 'devel-scsi-ncr710-pull-request' of https://github.com/hdeller/qemu-hppa into staging

HP-PARISC 715 machine with NCR710 SCSI chip

This series adds a new emulation for a HP PA-RISC 715/64 model,
as descrived here: https://www.openpa.net/systems/hp-9000_715.html.

That machine has no PCI bus and instead uses a "LASI" chip which has built-in
NCR 53c710 SCSI and i82596 network chips. Compared to the other already
emulated machines B160L and C3700, this machine should be able to support older
operating systems like HP-UX 9 as well.

The QEMU project participated in the Google Summer of Code 2025 program by
"Implementing LASI Network Card and NCR 710 SCSI Controller Device Models", and
Soumyajyotii Ssarkar stepped up to develop those drivers.

This patch series includes the code for the NCR710 SCSI controller,
the network code will follow in later patch series.

# -----BEGIN PGP SIGNATURE-----
#
# iHUEABYKAB0WIQS86RI+GtKfB8BJu973ErUQojoPXwUCaP/g8wAKCRD3ErUQojoP
# X1hOAP9WOYVNz7UIpNztQ9p3tUKApALaPR3aaiORMLrjktaDMQD/TIHExaKIKtn6
# iLy+RXzPFdpqmI/cBQx57q8lWzUd2QI=
# =Y7lX
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 27 Oct 2025 10:15:31 PM CET
# gpg:                using EDDSA key BCE9123E1AD29F07C049BBDEF712B510A23A0F5F
# gpg: Good signature from "Helge Deller <deller@gmx.de>" [unknown]
# gpg:                 aka "Helge Deller <deller@kernel.org>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 4544 8228 2CD9 10DB EF3D  25F8 3E5F 3D04 A7A2 4603
#      Subkey fingerprint: BCE9 123E 1AD2 9F07 C049  BBDE F712 B510 A23A 0F5F

* tag 'devel-scsi-ncr710-pull-request' of https://github.com/hdeller/qemu-hppa:
  hw/hppa: Add 715 machine type including NCR710 SCSI
  hw/hppa: Require SeaBIOS version 19 for 715 machine
  hw/hppa: PCI devices depend on availability of PCI bus
  hw/hppa: Add NCR 710 SCSI driver to LASI chip Kconfig entry
  hw/hppa: Fix interrupt of LASI parallel port
  hw/hppa: Fix firmware end address for LASI chip
  hw/scsi: Add config option for new ncr710 driver
  lasi: Forward LASI SCSI ports to NCR 53c710 driver
  ncr710: Add driver for the NCR 53c710 SCSI chip
  lasi_ncr710: Add LASI wrapper for NCR 53c710 SCSI chip
  target/hppa: Update SeaBIOS-hppa to version 19

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2025-10-28 11:47:14 +01:00
commit 76929d6117
17 changed files with 3190 additions and 28 deletions

View file

@ -1286,6 +1286,8 @@ F: hw/net/*i82596*
F: hw/misc/lasi.c
F: hw/pci-host/astro.c
F: hw/pci-host/dino.c
F: hw/scsi/lasi_ncr710.*
F: hw/scsi/ncr53c710.*
F: include/hw/input/lasips2.h
F: include/hw/misc/lasi.h
F: include/hw/net/lasi_82596.h

View file

@ -17,7 +17,5 @@ config HPPA_B160L
select IDE_CMD646
select MC146818RTC
select LSI_SCSI_PCI
select LASI_82596
select LASIPS2
select PARALLEL
select ARTIST

View file

@ -5,7 +5,7 @@
#define HW_HPPA_HPPA_HARDWARE_H
#define FIRMWARE_START 0xf0000000
#define FIRMWARE_END 0xf0800000
#define FIRMWARE_END 0xf0100000
#define FIRMWARE_HIGH 0xfffffff0 /* upper 32-bits of 64-bit firmware address */
#define RAM_MAP_HIGH 0x0100000000 /* memory above 3.75 GB is mapped here */
@ -19,6 +19,7 @@
#define DINO_UART_HPA 0xfff83000
#define DINO_UART_BASE 0xfff83800
#define DINO_SCSI_HPA 0xfff8c000
#define LASI_HPA_715 0xf0100000
#define LASI_HPA 0xffd00000
#define LASI_UART_HPA 0xffd05000
#define LASI_SCSI_HPA 0xffd06000

View file

@ -30,6 +30,8 @@
#include "hw/pci-host/astro.h"
#include "hw/pci-host/dino.h"
#include "hw/misc/lasi.h"
#include "hw/scsi/ncr53c710.h"
#include "hw/scsi/lasi_ncr710.h"
#include "hppa_hardware.h"
#include "qemu/units.h"
#include "qapi/error.h"
@ -43,7 +45,7 @@ struct HppaMachineState {
MachineState parent_obj;
};
#define MIN_SEABIOS_HPPA_VERSION 12 /* require at least this fw version */
#define MIN_SEABIOS_HPPA_VERSION 19 /* require at least this fw version */
#define HPA_POWER_BUTTON (FIRMWARE_END - 0x10)
static hwaddr soft_power_reg;
@ -346,7 +348,7 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine)
}
/*
* Last creation step: Add SCSI discs, NICs, graphics & load firmware
* Last creation step: Add NICs, graphics & load firmware
*/
static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
TranslateFn *translate)
@ -361,12 +363,6 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
MemoryRegion *rom_region;
SysBusDevice *s;
/* SCSI disk setup. */
if (drive_get_max_bus(IF_SCSI) >= 0) {
dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a"));
lsi53c8xx_handle_legacy_cmdline(dev);
}
/* Graphics setup. */
if (machine->enable_graphics && vga_interface_type != VGA_NONE) {
dev = qdev_new("artist");
@ -387,20 +383,22 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
enable_lasi_lan());
}
pci_init_nic_devices(pci_bus, mc->default_nic);
if (pci_bus) {
pci_init_nic_devices(pci_bus, mc->default_nic);
/* BMC board: HP Diva GSP */
dev = qdev_new("diva-gsp");
if (!object_property_get_bool(OBJECT(dev), "disable", NULL)) {
pci_dev = pci_new_multifunction(PCI_DEVFN(2, 0), "diva-gsp");
if (!lasi_dev) {
/* bind default keyboard/serial to Diva card */
qdev_prop_set_chr(DEVICE(pci_dev), "chardev1", serial_hd(0));
qdev_prop_set_chr(DEVICE(pci_dev), "chardev2", serial_hd(1));
qdev_prop_set_chr(DEVICE(pci_dev), "chardev3", serial_hd(2));
qdev_prop_set_chr(DEVICE(pci_dev), "chardev4", serial_hd(3));
/* BMC board: HP Diva GSP PCI card */
dev = qdev_new("diva-gsp");
if (dev && !object_property_get_bool(OBJECT(dev), "disable", NULL)) {
pci_dev = pci_new_multifunction(PCI_DEVFN(2, 0), "diva-gsp");
if (!lasi_dev) {
/* bind default keyboard/serial to Diva card */
qdev_prop_set_chr(DEVICE(pci_dev), "chardev1", serial_hd(0));
qdev_prop_set_chr(DEVICE(pci_dev), "chardev2", serial_hd(1));
qdev_prop_set_chr(DEVICE(pci_dev), "chardev3", serial_hd(2));
qdev_prop_set_chr(DEVICE(pci_dev), "chardev4", serial_hd(3));
}
pci_realize_and_unref(pci_dev, pci_bus, &error_fatal);
}
pci_realize_and_unref(pci_dev, pci_bus, &error_fatal);
}
/* create USB OHCI controller for USB keyboard & mouse on Astro machines */
@ -535,6 +533,71 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
cpu[0]->env.kernel_entry = kernel_entry;
}
/*
* Create HP 715/64 workstation
*/
static void machine_HP_715_init(MachineState *machine)
{
DeviceState *dev;
MemoryRegion *addr_space = get_system_memory();
TranslateFn *translate;
ISABus *isa_bus;
/* Create CPUs and RAM. */
translate = machine_HP_common_init_cpus(machine);
if (hppa_is_pa20(&cpu[0]->env)) {
error_report("The HP 715/64 workstation requires a 32-bit "
"CPU. Use '-machine 715' instead.");
exit(1);
}
/* Create ISA bus, needed for PS/2 kbd/mouse port emulation */
isa_bus = hppa_isa_bus(translate(NULL, IDE_HPA));
assert(isa_bus);
/* Init Lasi chip */
lasi_dev = DEVICE(lasi_init());
memory_region_add_subregion(addr_space, translate(NULL, LASI_HPA_715),
sysbus_mmio_get_region(
SYS_BUS_DEVICE(lasi_dev), 0));
/* Serial ports: Lasi use a 7.272727 MHz clock. */
serial_mm_init(addr_space, translate(NULL, LASI_HPA_715 + LASI_UART + 0x800), 0,
qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16,
serial_hd(0), DEVICE_BIG_ENDIAN);
/* Parallel port */
parallel_mm_init(addr_space, translate(NULL, LASI_HPA_715 + LASI_LPT + 0x800), 0,
qdev_get_gpio_in(lasi_dev, LASI_IRQ_LPT_HPA),
parallel_hds[0]);
/* PS/2 Keyboard/Mouse */
dev = qdev_new(TYPE_LASIPS2);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
qdev_get_gpio_in(lasi_dev, LASI_IRQ_PS2KBD_HPA));
memory_region_add_subregion(addr_space,
translate(NULL, LASI_HPA_715 + LASI_PS2),
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
0));
memory_region_add_subregion(addr_space,
translate(NULL, LASI_HPA_715 + LASI_PS2 + 0x100),
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
1));
/* SCSI disk setup. */
if (drive_get_max_bus(IF_SCSI) >= 0) {
dev = lasi_ncr710_init(addr_space,
translate(NULL, LASI_HPA_715 + 0x6000),
qdev_get_gpio_in(lasi_dev, LASI_IRQ_SCSI_HPA));
assert(dev);
lasi_ncr710_handle_legacy_cmdline(dev);
}
/* Add NICs, graphics & load firmware */
machine_HP_common_init_tail(machine, NULL, translate);
}
/*
* Create HP B160L workstation
*/
@ -584,7 +647,7 @@ static void machine_HP_B160L_init(MachineState *machine)
/* Parallel port */
parallel_mm_init(addr_space, translate(NULL, LASI_LPT_HPA + 0x800), 0,
qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA),
qdev_get_gpio_in(lasi_dev, LASI_IRQ_LPT_HPA),
parallel_hds[0]);
/* PS/2 Keyboard/Mouse */
@ -601,7 +664,13 @@ static void machine_HP_B160L_init(MachineState *machine)
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
1));
/* Add SCSI discs, NICs, graphics & load firmware */
/* SCSI disk setup. */
if (drive_get_max_bus(IF_SCSI) >= 0) {
dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a"));
lsi53c8xx_handle_legacy_cmdline(dev);
}
/* Add NICs, graphics & load firmware */
machine_HP_common_init_tail(machine, pci_bus, translate);
}
@ -644,7 +713,13 @@ static void machine_HP_C3700_init(MachineState *machine)
pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(astro->elroy[0]), "pci"));
assert(pci_bus);
/* Add SCSI discs, NICs, graphics & load firmware */
/* SCSI disk setup. */
if (drive_get_max_bus(IF_SCSI) >= 0) {
DeviceState *dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a"));
lsi53c8xx_handle_legacy_cmdline(dev);
}
/* Add NICs, graphics & load firmware */
machine_HP_common_init_tail(machine, pci_bus, translate);
}
@ -741,6 +816,25 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, const void *data)
mc->default_ram_size = 1024 * MiB;
}
static void HP_715_machine_init_class_init(ObjectClass *oc, const void *data)
{
static const char * const valid_cpu_types[] = {
TYPE_HPPA_CPU,
NULL
};
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "HP 715/64 workstation";
mc->default_cpu_type = TYPE_HPPA_CPU;
mc->valid_cpu_types = valid_cpu_types;
mc->init = machine_HP_715_init;
/* can only support up to max. 8 CPUs due inventory major numbers */
mc->max_cpus = MIN_CONST(HPPA_MAX_CPUS, 8);
mc->default_ram_size = 256 * MiB;
mc->default_nic = NULL;
}
static const TypeInfo hppa_machine_types[] = {
{
.name = TYPE_HPPA_COMMON_MACHINE,
@ -760,6 +854,10 @@ static const TypeInfo hppa_machine_types[] = {
.name = MACHINE_TYPE_NAME("C3700"),
.parent = TYPE_HPPA_COMMON_MACHINE,
.class_init = HP_C3700_machine_init_class_init,
}, {
.name = MACHINE_TYPE_NAME("715"),
.parent = TYPE_HPPA_COMMON_MACHINE,
.class_init = HP_715_machine_init_class_init,
},
};

View file

@ -212,6 +212,11 @@ config VIRT_CTRL
config LASI
bool
select LASI_82596
select LASIPS2
select NCR710_SCSI
select PARALLEL
select SERIAL_MM
config ALLWINNER_SRAMC
bool

View file

@ -43,7 +43,7 @@ static bool lasi_chip_mem_valid(void *opaque, hwaddr addr,
case LASI_LAN + 12: /* LASI LAN MAC */
case LASI_RTC:
case LASI_FDC:
case LASI_SCSI ... LASI_SCSI + 0xFF:
case LASI_PCR ... LASI_AMR:
ret = true;
}
@ -84,6 +84,7 @@ static MemTxResult lasi_chip_read_with_attrs(void *opaque, hwaddr addr,
case LASI_LAN:
case LASI_LAN + 12:
case LASI_FDC:
case LASI_SCSI ... LASI_SCSI + 0xFF:
val = 0;
break;
case LASI_RTC:
@ -156,6 +157,9 @@ static MemTxResult lasi_chip_write_with_attrs(void *opaque, hwaddr addr,
case LASI_UART:
/* XXX: reset serial port */
break;
case LASI_SCSI ... LASI_SCSI + 0xFF:
/* XXX: reset SCSI Controller */
break;
case LASI_LAN:
/* XXX: reset LAN card */
break;

View file

@ -7,6 +7,11 @@ config LSI_SCSI_PCI
depends on PCI
select SCSI
config NCR710_SCSI
bool
default y if LASI
select SCSI
config MPTSAS_SCSI_PCI
bool
default y if PCI_DEVICES

282
hw/scsi/lasi_ncr710.c Normal file
View file

@ -0,0 +1,282 @@
/*
* 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
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#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)

57
hw/scsi/lasi_ncr710.h Normal file
View file

@ -0,0 +1,57 @@
/*
* 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
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#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

View file

@ -12,6 +12,7 @@ scsi_ss.add(files(
scsi_ss.add(when: 'CONFIG_ESP', if_true: files('esp.c'))
scsi_ss.add(when: 'CONFIG_ESP_PCI', if_true: files('esp-pci.c'))
scsi_ss.add(when: 'CONFIG_LSI_SCSI_PCI', if_true: files('lsi53c895a.c'))
scsi_ss.add(when: 'CONFIG_NCR710_SCSI', if_true: files('ncr53c710.c', 'lasi_ncr710.c'))
scsi_ss.add(when: 'CONFIG_MEGASAS_SCSI_PCI', if_true: files('megasas.c'))
scsi_ss.add(when: 'CONFIG_MPTSAS_SCSI_PCI', if_true: files('mptsas.c', 'mptconfig.c', 'mptendian.c'))
scsi_ss.add(when: 'CONFIG_VMW_PVSCSI_SCSI_PCI', if_true: files('vmw_pvscsi.c'))

2432
hw/scsi/ncr53c710.c Normal file

File diff suppressed because it is too large Load diff

246
hw/scsi/ncr53c710.h Normal file
View file

@ -0,0 +1,246 @@
/*
* QEMU NCR710 SCSI Controller
*
* Copyright (c) 2025 Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
*
* NCR710 SCSI Controller implementation
* Based on the NCR53C710 Technical Manual Version 3.2, December 2000
*
* Developed from the hackish implementation of NCR53C710 by Helge Deller
* which was interim based on the hackish implementation by Toni Wilen for UAE
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef HW_NCR53C710_H
#define HW_NCR53C710_H
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/scsi/scsi.h"
#include "qemu/fifo8.h"
#include "qom/object.h"
#include "system/memory.h"
#include "hw/irq.h"
#include "qemu/timer.h"
#define TYPE_NCR710_SCSI "ncr710-scsi"
#define TYPE_SYSBUS_NCR710_SCSI "sysbus-ncr710-scsi"
#define SYSBUS_NCR710_SCSI(obj) \
OBJECT_CHECK(SysBusNCR710State, (obj), TYPE_SYSBUS_NCR710_SCSI)
#define ENABLE_DEBUG 0
#if ENABLE_DEBUG
#define DBG(x) x
#define NCR710_DPRINTF(fmt, ...) \
fprintf(stderr, "QEMU: " fmt, ## __VA_ARGS__)
#define BADF(fmt, ...) \
fprintf(stderr, "QEMU: error: " fmt, ## __VA_ARGS__)
#else
#define DBG(x) do { } while (0)
#define NCR710_DPRINTF(fmt, ...) do { } while (0)
#define BADF(fmt, ...) do { } while (0)
#endif
/* NCR710 - Little Endian register Ordering */
#define NCR710_SCNTL0_REG 0x00 /* SCSI Control Zero */
#define NCR710_SCNTL1_REG 0x01 /* SCSI Control One */
#define NCR710_SDID_REG 0x02 /* SCSI Destination ID */
#define NCR710_SIEN_REG 0x03 /* SCSI Interrupt Enable */
#define NCR710_SCID_REG 0x04 /* SCSI Chip ID */
#define NCR710_SXFER_REG 0x05 /* SCSI Transfer */
#define NCR710_SODL_REG 0x06 /* SCSI Output Data Latch */
#define NCR710_SOCL_REG 0x07 /* SCSI Output Control Latch */
#define NCR710_SFBR_REG 0x08 /* SCSI First Byte Received */
#define NCR710_SIDL_REG 0x09 /* SCSI Input Data Latch */
#define NCR710_SBDL_REG 0x0A /* SCSI Bus Data Lines */
#define NCR710_SBCL_REG 0x0B /* SCSI Bus Control Lines */
#define NCR710_DSTAT_REG 0x0C /* DMA Status */
#define NCR710_SSTAT0_REG 0x0D /* SCSI Status Zero */
#define NCR710_SSTAT1_REG 0x0E /* SCSI Status One */
#define NCR710_SSTAT2_REG 0x0F /* SCSI Status Two */
#define NCR710_DSA_REG 0x10 /* Data Structure Address */
#define NCR710_CTEST0_REG 0x14 /* Chip Test Zero */
#define NCR710_CTEST1_REG 0x15 /* Chip Test One */
#define NCR710_CTEST2_REG 0x16 /* Chip Test Two */
#define NCR710_CTEST3_REG 0x17 /* Chip Test Three */
#define NCR710_CTEST4_REG 0x18 /* Chip Test Four */
#define NCR710_CTEST5_REG 0x19 /* Chip Test Five */
#define NCR710_CTEST6_REG 0x1A /* Chip Test Six */
#define NCR710_CTEST7_REG 0x1B /* Chip Test Seven */
#define NCR710_TEMP_REG 0x1C /* Temporary Stack */
#define NCR710_DFIFO_REG 0x20 /* DMA FIFO */
#define NCR710_ISTAT_REG 0x21 /* Interrupt Status */
#define NCR710_CTEST8_REG 0x22 /* Chip Test Eight */
#define NCR710_LCRC_REG 0x23 /* Longitudinal Parity */
#define NCR710_DBC_REG 0x24 /* DMA Byte Counter (24-bit, LE) */
#define NCR710_DCMD_REG 0x27 /* DMA Command */
#define NCR710_DNAD_REG 0x28 /* DMA Next Data Address (32-bit, LE) */
#define NCR710_DSP_REG 0x2C /* DMA SCRIPTS Pointer (32-bit, LE) */
#define NCR710_DSPS_REG 0x30 /* DMA SCRIPTS Pointer Save */
#define NCR710_SCRATCH_REG 0x34 /* Scratch (32-bit, LE) */
#define NCR710_DMODE_REG 0x38 /* DMA Mode */
#define NCR710_DIEN_REG 0x39 /* DMA Interrupt Enable */
#define NCR710_DWT_REG 0x3A /* DMA Watchdog Timer */
#define NCR710_DCNTL_REG 0x3B /* DMA Control */
#define NCR710_ADDER_REG 0x3C /* Adder Sum Output (32-bit, LE) */
#define NCR710_REG_SIZE 0x100
#define NCR710_BUF_SIZE 4096
#define NCR710_HOST_ID 7
#define NCR710_MAX_MSGIN_LEN 8
#define NCR710_SCSI_FIFO_SIZE 8
typedef enum {
NCR710_WAIT_NONE = 0,
NCR710_WAIT_RESELECT = 1,
NCR710_WAIT_DMA = 2,
NCR710_WAIT_RESERVED = 3
} NCR710WaitState;
typedef enum {
NCR710_CMD_PENDING = 0,
NCR710_CMD_DATA_READY = 1,
NCR710_CMD_COMPLETE = 2
} NCR710CommandState;
typedef enum {
NCR710_MSG_ACTION_NONE = 0,
NCR710_MSG_ACTION_DISCONNECT = 1,
NCR710_MSG_ACTION_DATA_OUT = 2,
NCR710_MSG_ACTION_DATA_IN = 3
} NCR710MessageAction;
typedef struct NCR710State NCR710State;
typedef struct NCR710Request NCR710Request;
/*
* SCSI FIFO structure - 8 transfers deep, 1 byte per transfer
* (9-bit wide with parity)
*/
typedef struct {
uint8_t data[NCR710_SCSI_FIFO_SIZE];
uint8_t parity[NCR710_SCSI_FIFO_SIZE];
int head;
int count;
} NCR710_SCSI_FIFO;
struct NCR710Request {
SCSIRequest *req;
uint32_t tag;
uint32_t dma_len;
uint32_t pending;
uint8_t status;
bool active;
uint8_t *dma_buf;
bool out;
uint32_t resume_offset;
uint32_t saved_dnad;
};
struct NCR710State {
SysBusDevice parent_obj;
MemoryRegion mmio;
qemu_irq irq;
SCSIBus bus;
AddressSpace *as;
/* Registers */
uint8_t scntl0;
uint8_t scntl1;
uint8_t sdid;
uint8_t sien0;
uint8_t scid;
uint8_t sxfer;
uint8_t sodl;
uint8_t socl;
uint8_t sfbr;
uint8_t sidl;
uint8_t sbdl;
uint8_t sbcl;
uint8_t dstat;
uint8_t sstat0;
uint8_t sstat1;
uint8_t sstat2;
uint32_t dsa;
uint8_t ctest0;
uint8_t ctest1;
uint8_t ctest2;
uint8_t ctest3;
uint8_t ctest4;
uint8_t ctest5;
uint8_t ctest6;
uint8_t ctest7;
uint8_t ctest8;
uint32_t temp;
uint8_t dfifo;
uint8_t istat;
uint8_t lcrc;
uint32_t dbc;
uint8_t dcmd;
uint32_t dnad;
uint32_t dsp;
uint32_t dsps;
uint32_t scratch;
uint8_t dmode;
uint8_t dien;
uint8_t dwt;
uint8_t dcntl;
uint32_t adder;
NCR710_SCSI_FIFO scsi_fifo;
NCR710Request *current;
uint8_t status;
uint8_t msg[NCR710_MAX_MSGIN_LEN];
uint8_t msg_len;
uint8_t msg_action; /* NCR710MessageAction values */
int carry;
bool script_active;
int32_t waiting; /* NCR710WaitState values */
uint8_t command_complete; /* NCR710CommandState values */
QEMUTimer *reselection_retry_timer;
uint32_t saved_dsps;
uint32_t select_tag;
uint8_t current_lun;
uint8_t reselection_id;
bool wait_reselect;
};
typedef struct SysBusNCR710State {
SysBusDevice parent_obj;
MemoryRegion mmio;
MemoryRegion iomem;
qemu_irq irq;
NCR710State ncr710;
} SysBusNCR710State;
static inline NCR710State *ncr710_from_scsi_bus(SCSIBus *bus)
{
return container_of(bus, NCR710State, bus);
}
static inline SysBusNCR710State *sysbus_from_ncr710(NCR710State *s)
{
return container_of(s, SysBusNCR710State, ncr710);
}
DeviceState *ncr53c710_init(MemoryRegion *address_space, hwaddr addr,
qemu_irq irq);
DeviceState *ncr710_device_create_sysbus(hwaddr addr, qemu_irq irq);
void ncr710_reg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size);
uint64_t ncr710_reg_read(void *opaque, hwaddr addr, unsigned size);
void ncr710_soft_reset(NCR710State *s);
void ncr710_request_cancelled(SCSIRequest *req);
void ncr710_command_complete(SCSIRequest *req, size_t resid);
void ncr710_transfer_data(SCSIRequest *req, uint32_t len);
void ncr710_execute_script(NCR710State *s);
void ncr710_set_phase(NCR710State *s, int phase);
void ncr710_reselection_retry_callback(void *opaque);
#endif /* HW_NCR53C710_H */

View file

@ -306,6 +306,35 @@ 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"
# ncr53c710.c
ncr710_reset(void) "Reset"
ncr710_reg_read(const char *name, int offset, uint8_t ret) "Read %s [0x%02x] = 0x%02x"
ncr710_reg_write(const char *name, int offset, uint8_t val) "Write %s [0x%02x] = 0x%02x"
ncr710_script_scsi_interrupt(uint8_t stat0, uint8_t sstat0) "SCSI interrupt stat=0x%02x sstat=0x%02x"
ncr710_script_dma_interrupt(uint8_t stat, uint8_t dstat) "DMA interrupt stat=0x%02x dstat=0x%02x"
ncr710_command_complete(uint32_t tag, uint8_t status) "tag=0x%x status=0x%02x"
ncr710_disconnect(uint8_t waiting) "waiting=%d"
ncr710_bad_selection(uint32_t target) "target=%d"
ncr710_parity_sense_changed(const char *parity) "Parity sense changed to %s"
ncr710_device_realize(void) "Device realized"
# 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"

View file

@ -28,7 +28,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(LasiState, LASI_CHIP)
#define LASI_LPT 0x02000
#define LASI_AUDIO 0x04000
#define LASI_UART 0x05000
#define LASI_SCSI 0x06000
#define LASI_LAN 0x07000
#define LASI_PS2 0x08000
#define LASI_RTC 0x09000
#define LASI_FDC 0x0A000

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
Subproject commit 3391c580960febcb9fa8f686f9666adaa462c349
Subproject commit d0d359929a009549f7cecac9d2a29c8bdbc07a98