aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS5
-rw-r--r--hw/hppa/Kconfig1
-rw-r--r--hw/hppa/hppa_hardware.h1
-rw-r--r--hw/hppa/machine.c367
-rw-r--r--hw/input/lasips2.c10
-rw-r--r--hw/net/tulip.c2
-rw-r--r--hw/pci-host/Kconfig4
-rw-r--r--hw/pci-host/astro.c885
-rw-r--r--hw/pci-host/meson.build1
-rw-r--r--hw/pci-host/trace-events11
-rw-r--r--include/hw/pci-host/astro.h92
-rw-r--r--include/hw/pci/pci_ids.h2
-rw-r--r--pc-bios/hppa-firmware.imgbin732376 -> 755480 bytes
m---------roms/seabios-hppa0
14 files changed, 1300 insertions, 81 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 628e6d7..e1a96c1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1174,7 +1174,7 @@ F: hw/*/etraxfs_*.c
HP-PARISC Machines
------------------
-HP B160L
+HP B160L, HP C3700
M: Richard Henderson <richard.henderson@linaro.org>
R: Helge Deller <deller@gmx.de>
S: Odd Fixes
@@ -1183,12 +1183,15 @@ F: hw/hppa/
F: hw/input/lasips2.c
F: hw/net/*i82596*
F: hw/misc/lasi.c
+F: hw/pci-host/astro.c
F: hw/pci-host/dino.c
F: include/hw/input/lasips2.h
F: include/hw/misc/lasi.h
F: include/hw/net/lasi_82596.h
+F: include/hw/pci-host/astro.h
F: include/hw/pci-host/dino.h
F: pc-bios/hppa-firmware.img
+F: roms/seabios-hppa/
LoongArch Machines
------------------
diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig
index 5dd8b5b..ff8528a 100644
--- a/hw/hppa/Kconfig
+++ b/hw/hppa/Kconfig
@@ -3,6 +3,7 @@ config HPPA_B160L
imply PCI_DEVICES
imply E1000_PCI
imply VIRTIO_VGA
+ select ASTRO
select DINO
select LASI
select SERIAL
diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h
index a5ac3dd..a9be7bb 100644
--- a/hw/hppa/hppa_hardware.h
+++ b/hw/hppa/hppa_hardware.h
@@ -18,7 +18,6 @@
#define LASI_UART_HPA 0xffd05000
#define LASI_SCSI_HPA 0xffd06000
#define LASI_LAN_HPA 0xffd07000
-#define LASI_RTC_HPA 0xffd09000
#define LASI_LPT_HPA 0xffd02000
#define LASI_AUDIO_HPA 0xffd04000
#define LASI_PS2KBD_HPA 0xffd08000
diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c
index cf28cb9..67d4d1b 100644
--- a/hw/hppa/machine.c
+++ b/hw/hppa/machine.c
@@ -1,6 +1,8 @@
/*
* QEMU HPPA hardware system emulator.
- * Copyright 2018 Helge Deller <deller@gmx.de>
+ * (C) Copyright 2018-2023 Helge Deller <deller@gmx.de>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
*/
#include "qemu/osdep.h"
@@ -20,7 +22,10 @@
#include "hw/input/lasips2.h"
#include "hw/net/lasi_82596.h"
#include "hw/nmi.h"
+#include "hw/usb.h"
#include "hw/pci/pci.h"
+#include "hw/pci/pci_device.h"
+#include "hw/pci-host/astro.h"
#include "hw/pci-host/dino.h"
#include "hw/misc/lasi.h"
#include "hppa_hardware.h"
@@ -29,12 +34,13 @@
#include "net/net.h"
#include "qemu/log.h"
-#define MIN_SEABIOS_HPPA_VERSION 6 /* require at least this fw version */
+#define MIN_SEABIOS_HPPA_VERSION 10 /* require at least this fw version */
#define HPA_POWER_BUTTON (FIRMWARE_END - 0x10)
#define enable_lasi_lan() 0
+static DeviceState *lasi_dev;
static void hppa_powerdown_req(Notifier *n, void *opaque)
{
@@ -95,14 +101,69 @@ static ISABus *hppa_isa_bus(void)
isa_bus = isa_bus_new(NULL, get_system_memory(), isa_region,
&error_abort);
- isa_irqs = i8259_init(isa_bus,
- /* qemu_allocate_irq(dino_set_isa_irq, s, 0)); */
- NULL);
+ isa_irqs = i8259_init(isa_bus, NULL);
isa_bus_register_input_irqs(isa_bus, isa_irqs);
return isa_bus;
}
+/*
+ * Helper functions to emulate RTC clock and DebugOutputPort
+ */
+static time_t rtc_ref;
+
+static uint64_t io_cpu_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t val = 0;
+
+ switch (addr) {
+ case 0: /* RTC clock */
+ val = time(NULL);
+ val += rtc_ref;
+ break;
+ case 8: /* DebugOutputPort */
+ return 0xe9; /* readback */
+ }
+ return val;
+}
+
+static void io_cpu_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ unsigned char ch;
+ Chardev *debugout;
+
+ switch (addr) {
+ case 0: /* RTC clock */
+ rtc_ref = val - time(NULL);
+ break;
+ case 8: /* DebugOutputPort */
+ ch = val;
+ debugout = serial_hd(0);
+ if (debugout) {
+ qemu_chr_fe_write_all(debugout->be, &ch, 1);
+ } else {
+ fprintf(stderr, "%c", ch);
+ }
+ break;
+ }
+}
+
+static const MemoryRegionOps hppa_io_helper_ops = {
+ .read = io_cpu_read,
+ .write = io_cpu_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+};
+
+
static uint64_t cpu_hppa_to_phys(void *opaque, uint64_t addr)
{
addr &= (0x10000000 - 1);
@@ -118,11 +179,13 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device,
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
-static FWCfgState *create_fw_cfg(MachineState *ms)
+static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus)
{
FWCfgState *fw_cfg;
uint64_t val;
const char qemu_version[] = QEMU_VERSION;
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ int len;
fw_cfg = fw_cfg_init_mem(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4);
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, ms->smp.cpus);
@@ -137,8 +200,24 @@ static FWCfgState *create_fw_cfg(MachineState *ms)
fw_cfg_add_file(fw_cfg, "/etc/cpu/tlb_entries",
g_memdup(&val, sizeof(val)), sizeof(val));
+ val = cpu_to_le64(HPPA_BTLB_ENTRIES);
+ fw_cfg_add_file(fw_cfg, "/etc/cpu/btlb_entries",
+ g_memdup(&val, sizeof(val)), sizeof(val));
+
+ len = strlen(mc->name) + 1;
+ fw_cfg_add_file(fw_cfg, "/etc/hppa/machine",
+ g_memdup(mc->name, len), len);
+
val = cpu_to_le64(HPA_POWER_BUTTON);
- fw_cfg_add_file(fw_cfg, "/etc/power-button-addr",
+ fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr",
+ g_memdup(&val, sizeof(val)), sizeof(val));
+
+ val = cpu_to_le64(CPU_HPA + 16);
+ fw_cfg_add_file(fw_cfg, "/etc/hppa/rtc-addr",
+ g_memdup(&val, sizeof(val)), sizeof(val));
+
+ val = cpu_to_le64(CPU_HPA + 24);
+ fw_cfg_add_file(fw_cfg, "/etc/hppa/DebugOutputPort",
g_memdup(&val, sizeof(val)), sizeof(val));
fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ms->boot_config.order[0]);
@@ -148,6 +227,8 @@ static FWCfgState *create_fw_cfg(MachineState *ms)
g_memdup(qemu_version, sizeof(qemu_version)),
sizeof(qemu_version));
+ fw_cfg_add_extra_pci_roots(pci_bus, fw_cfg);
+
return fw_cfg;
}
@@ -173,29 +254,20 @@ static DinoState *dino_init(MemoryRegion *addr_space)
return DINO_PCI_HOST_BRIDGE(dev);
}
-static void machine_hppa_init(MachineState *machine)
+/*
+ * Step 1: Create CPUs and Memory
+ */
+static void machine_HP_common_init_cpus(MachineState *machine)
{
- const char *kernel_filename = machine->kernel_filename;
- const char *kernel_cmdline = machine->kernel_cmdline;
- const char *initrd_filename = machine->initrd_filename;
- MachineClass *mc = MACHINE_GET_CLASS(machine);
- DeviceState *dev, *dino_dev, *lasi_dev;
- PCIBus *pci_bus;
- ISABus *isa_bus;
- char *firmware_filename;
- uint64_t firmware_low, firmware_high;
- long size;
- uint64_t kernel_entry = 0, kernel_low, kernel_high;
MemoryRegion *addr_space = get_system_memory();
- MemoryRegion *rom_region;
MemoryRegion *cpu_region;
long i;
unsigned int smp_cpus = machine->smp.cpus;
- SysBusDevice *s;
+ char *name;
/* Create CPUs. */
for (i = 0; i < smp_cpus; i++) {
- char *name = g_strdup_printf("cpu%ld-io-eir", i);
+ name = g_strdup_printf("cpu%ld-io-eir", i);
cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type));
cpu_region = g_new(MemoryRegion, 1);
@@ -206,51 +278,40 @@ static void machine_hppa_init(MachineState *machine)
g_free(name);
}
+ /* RTC and DebugOutputPort on CPU #0 */
+ cpu_region = g_new(MemoryRegion, 1);
+ memory_region_init_io(cpu_region, OBJECT(cpu[0]), &hppa_io_helper_ops,
+ cpu[0], "cpu0-io-rtc", 2 * sizeof(uint64_t));
+ memory_region_add_subregion(addr_space, CPU_HPA + 16, cpu_region);
+
/* Main memory region. */
if (machine->ram_size > 3 * GiB) {
error_report("RAM size is currently restricted to 3GB");
exit(EXIT_FAILURE);
}
memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1);
+}
-
- /* Init Lasi chip */
- lasi_dev = DEVICE(lasi_init());
- memory_region_add_subregion(addr_space, LASI_HPA,
- sysbus_mmio_get_region(
- SYS_BUS_DEVICE(lasi_dev), 0));
-
- /* Init Dino (PCI host bus chip). */
- dino_dev = DEVICE(dino_init(addr_space));
- memory_region_add_subregion(addr_space, DINO_HPA,
- sysbus_mmio_get_region(
- SYS_BUS_DEVICE(dino_dev), 0));
- pci_bus = PCI_BUS(qdev_get_child_bus(dino_dev, "pci"));
- assert(pci_bus);
-
- /* Create ISA bus. */
- isa_bus = hppa_isa_bus();
- assert(isa_bus);
-
- /* Realtime clock, used by firmware for PDC_TOD call. */
- mc146818_rtc_init(isa_bus, 2000, NULL);
-
- /* Serial ports: Lasi and Dino use a 7.272727 MHz clock. */
- serial_mm_init(addr_space, LASI_UART_HPA + 0x800, 0,
- qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16,
- serial_hd(0), DEVICE_BIG_ENDIAN);
-
- serial_mm_init(addr_space, DINO_UART_HPA + 0x800, 0,
- qdev_get_gpio_in(dino_dev, DINO_IRQ_RS232INT), 7272727 / 16,
- serial_hd(1), DEVICE_BIG_ENDIAN);
-
- /* Parallel port */
- parallel_mm_init(addr_space, LASI_LPT_HPA + 0x800, 0,
- qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA),
- parallel_hds[0]);
-
- /* fw_cfg configuration interface */
- create_fw_cfg(machine);
+/*
+ * Last creation step: Add SCSI discs, NICs, graphics & load firmware
+ */
+static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus)
+{
+ const char *kernel_filename = machine->kernel_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *initrd_filename = machine->initrd_filename;
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ DeviceState *dev;
+ PCIDevice *pci_dev;
+ char *firmware_filename;
+ uint64_t firmware_low, firmware_high;
+ long size;
+ uint64_t kernel_entry = 0, kernel_low, kernel_high;
+ MemoryRegion *addr_space = get_system_memory();
+ MemoryRegion *rom_region;
+ long i;
+ unsigned int smp_cpus = machine->smp.cpus;
+ SysBusDevice *s;
/* SCSI disk setup. */
dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a"));
@@ -278,21 +339,42 @@ static void machine_hppa_init(MachineState *machine)
}
}
- /* 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, LASI_PS2KBD_HPA,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
- 0));
- memory_region_add_subregion(addr_space, LASI_PS2KBD_HPA + 0x100,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
- 1));
+ /* BMC board: HP Powerbar SP2 Diva (with console only) */
+ pci_dev = pci_new(-1, "pci-serial");
+ if (!lasi_dev) {
+ /* bind default keyboard/serial to Diva card */
+ qdev_prop_set_chr(DEVICE(pci_dev), "chardev", serial_hd(0));
+ }
+ qdev_prop_set_uint8(DEVICE(pci_dev), "prog_if", 0);
+ pci_realize_and_unref(pci_dev, pci_bus, &error_fatal);
+ pci_config_set_vendor_id(pci_dev->config, PCI_VENDOR_ID_HP);
+ pci_config_set_device_id(pci_dev->config, 0x1048);
+ pci_set_word(&pci_dev->config[PCI_SUBSYSTEM_VENDOR_ID], PCI_VENDOR_ID_HP);
+ pci_set_word(&pci_dev->config[PCI_SUBSYSTEM_ID], 0x1227); /* Powerbar */
+
+ /* create a second serial PCI card when running Astro */
+ if (!lasi_dev) {
+ pci_dev = pci_new(-1, "pci-serial-4x");
+ qdev_prop_set_chr(DEVICE(pci_dev), "chardev1", serial_hd(1));
+ qdev_prop_set_chr(DEVICE(pci_dev), "chardev2", serial_hd(2));
+ qdev_prop_set_chr(DEVICE(pci_dev), "chardev3", serial_hd(3));
+ qdev_prop_set_chr(DEVICE(pci_dev), "chardev4", serial_hd(4));
+ pci_realize_and_unref(pci_dev, pci_bus, &error_fatal);
+ }
+
+ /* create USB OHCI controller for USB keyboard & mouse on Astro machines */
+ if (!lasi_dev && machine->enable_graphics) {
+ pci_create_simple(pci_bus, -1, "pci-ohci");
+ usb_create_simple(usb_bus_find(-1), "usb-kbd");
+ usb_create_simple(usb_bus_find(-1), "usb-mouse");
+ }
/* register power switch emulation */
qemu_register_powerdown_notifier(&hppa_system_powerdown_notifier);
+ /* fw_cfg configuration interface */
+ create_fw_cfg(machine, pci_bus);
+
/* Load firmware. Given that this is not "real" firmware,
but one explicitly written for the emulation, we might as
well load it directly from an ELF image. */
@@ -410,6 +492,103 @@ static void machine_hppa_init(MachineState *machine)
cpu[0]->env.gr[19] = FW_CFG_IO_BASE;
}
+/*
+ * Create HP B160L workstation
+ */
+static void machine_HP_B160L_init(MachineState *machine)
+{
+ DeviceState *dev, *dino_dev;
+ MemoryRegion *addr_space = get_system_memory();
+ ISABus *isa_bus;
+ PCIBus *pci_bus;
+
+ /* Create CPUs and RAM. */
+ machine_HP_common_init_cpus(machine);
+
+ /* Init Lasi chip */
+ lasi_dev = DEVICE(lasi_init());
+ memory_region_add_subregion(addr_space, LASI_HPA,
+ sysbus_mmio_get_region(
+ SYS_BUS_DEVICE(lasi_dev), 0));
+
+ /* Init Dino (PCI host bus chip). */
+ dino_dev = DEVICE(dino_init(addr_space));
+ memory_region_add_subregion(addr_space, DINO_HPA,
+ sysbus_mmio_get_region(
+ SYS_BUS_DEVICE(dino_dev), 0));
+ pci_bus = PCI_BUS(qdev_get_child_bus(dino_dev, "pci"));
+ assert(pci_bus);
+
+ /* Create ISA bus, needed for PS/2 kbd/mouse port emulation */
+ isa_bus = hppa_isa_bus();
+ assert(isa_bus);
+
+ /* Serial ports: Lasi and Dino use a 7.272727 MHz clock. */
+ serial_mm_init(addr_space, LASI_UART_HPA + 0x800, 0,
+ qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16,
+ serial_hd(0), DEVICE_BIG_ENDIAN);
+
+ serial_mm_init(addr_space, DINO_UART_HPA + 0x800, 0,
+ qdev_get_gpio_in(dino_dev, DINO_IRQ_RS232INT), 7272727 / 16,
+ serial_hd(1), DEVICE_BIG_ENDIAN);
+
+ /* Parallel port */
+ parallel_mm_init(addr_space, LASI_LPT_HPA + 0x800, 0,
+ qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_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, LASI_PS2KBD_HPA,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
+ 0));
+ memory_region_add_subregion(addr_space, LASI_PS2KBD_HPA + 0x100,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
+ 1));
+
+ /* Add SCSI discs, NICs, graphics & load firmware */
+ machine_HP_common_init_tail(machine, pci_bus);
+}
+
+static AstroState *astro_init(void)
+{
+ DeviceState *dev;
+
+ dev = qdev_new(TYPE_ASTRO_CHIP);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ return ASTRO_CHIP(dev);
+}
+
+/*
+ * Create HP C3700 workstation
+ */
+static void machine_HP_C3700_init(MachineState *machine)
+{
+ PCIBus *pci_bus;
+ AstroState *astro;
+ DeviceState *astro_dev;
+ MemoryRegion *addr_space = get_system_memory();
+
+ /* Create CPUs and RAM. */
+ machine_HP_common_init_cpus(machine);
+
+ /* Init Astro and the Elroys (PCI host bus chips). */
+ astro = astro_init();
+ astro_dev = DEVICE(astro);
+ memory_region_add_subregion(addr_space, ASTRO_HPA,
+ sysbus_mmio_get_region(
+ SYS_BUS_DEVICE(astro_dev), 0));
+ pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(astro->elroy[0]), "pci"));
+ assert(pci_bus);
+
+ /* Add SCSI discs, NICs, graphics & load firmware */
+ machine_HP_common_init_tail(machine, pci_bus);
+}
+
static void hppa_machine_reset(MachineState *ms, ShutdownCause reason)
{
unsigned int smp_cpus = ms->smp.cpus;
@@ -458,14 +637,14 @@ static void hppa_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
-static void hppa_machine_init_class_init(ObjectClass *oc, void *data)
+static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
- mc->desc = "HPPA B160L machine";
+ mc->desc = "HP B160L workstation";
mc->default_cpu_type = TYPE_HPPA_CPU;
- mc->init = machine_hppa_init;
+ mc->init = machine_HP_B160L_init;
mc->reset = hppa_machine_reset;
mc->block_default_type = IF_SCSI;
mc->max_cpus = HPPA_MAX_CPUS;
@@ -479,10 +658,41 @@ static void hppa_machine_init_class_init(ObjectClass *oc, void *data)
nc->nmi_monitor_handler = hppa_nmi;
}
-static const TypeInfo hppa_machine_init_typeinfo = {
- .name = MACHINE_TYPE_NAME("hppa"),
+static const TypeInfo HP_B160L_machine_init_typeinfo = {
+ .name = MACHINE_TYPE_NAME("B160L"),
+ .parent = TYPE_MACHINE,
+ .class_init = HP_B160L_machine_init_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_NMI },
+ { }
+ },
+};
+
+static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ NMIClass *nc = NMI_CLASS(oc);
+
+ mc->desc = "HP C3700 workstation";
+ mc->default_cpu_type = TYPE_HPPA_CPU;
+ mc->init = machine_HP_C3700_init;
+ mc->reset = hppa_machine_reset;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = HPPA_MAX_CPUS;
+ mc->default_cpus = 1;
+ mc->is_default = false;
+ mc->default_ram_size = 1024 * MiB;
+ mc->default_boot_order = "cd";
+ mc->default_ram_id = "ram";
+ mc->default_nic = "tulip";
+
+ nc->nmi_monitor_handler = hppa_nmi;
+}
+
+static const TypeInfo HP_C3700_machine_init_typeinfo = {
+ .name = MACHINE_TYPE_NAME("C3700"),
.parent = TYPE_MACHINE,
- .class_init = hppa_machine_init_class_init,
+ .class_init = HP_C3700_machine_init_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_NMI },
{ }
@@ -491,7 +701,8 @@ static const TypeInfo hppa_machine_init_typeinfo = {
static void hppa_machine_init_register_types(void)
{
- type_register_static(&hppa_machine_init_typeinfo);
+ type_register_static(&HP_B160L_machine_init_typeinfo);
+ type_register_static(&HP_C3700_machine_init_typeinfo);
}
type_init(hppa_machine_init_register_types)
diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c
index ea7c07a..6075121 100644
--- a/hw/input/lasips2.c
+++ b/hw/input/lasips2.c
@@ -351,6 +351,11 @@ static void lasips2_port_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ /*
+ * The PS/2 mouse port is integreal part of LASI and can not be
+ * created by users without LASI.
+ */
+ dc->user_creatable = false;
dc->realize = lasips2_port_realize;
}
@@ -397,6 +402,11 @@ static void lasips2_kbd_port_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass);
+ /*
+ * The PS/2 keyboard port is integreal part of LASI and can not be
+ * created by users without LASI.
+ */
+ dc->user_creatable = false;
device_class_set_parent_realize(dc, lasips2_kbd_port_realize,
&lpdc->parent_realize);
}
diff --git a/hw/net/tulip.c b/hw/net/tulip.c
index 915e5fb..11d866e 100644
--- a/hw/net/tulip.c
+++ b/hw/net/tulip.c
@@ -1020,7 +1020,7 @@ static void tulip_class_init(ObjectClass *klass, void *data)
k->exit = pci_tulip_exit;
k->vendor_id = PCI_VENDOR_ID_DEC;
k->device_id = PCI_DEVICE_ID_DEC_21143;
- k->subsystem_vendor_id = 0x103c;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_HP;
k->subsystem_id = 0x104f;
k->class_id = PCI_CLASS_NETWORK_ETHERNET;
dc->vmsd = &vmstate_pci_tulip;
diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig
index a07070e..54a609d 100644
--- a/hw/pci-host/Kconfig
+++ b/hw/pci-host/Kconfig
@@ -82,6 +82,10 @@ config DINO
bool
select PCI
+config ASTRO
+ bool
+ select PCI
+
config GT64120
bool
select PCI
diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c
new file mode 100644
index 0000000..4b2d7ca
--- /dev/null
+++ b/hw/pci-host/astro.c
@@ -0,0 +1,885 @@
+/*
+ * HP-PARISC Astro/Pluto/Ike/REO system bus adapter (SBA)
+ * with Elroy PCI bus (LBA) adapter emulation
+ * Found in C3000 and similar machines
+ *
+ * (C) 2023 by Helge Deller <deller@gmx.de>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ * Chip documentation is available at:
+ * https://parisc.wiki.kernel.org/index.php/Technical_Documentation
+ *
+ * TODO:
+ * - All user-added devices are currently attached to the first
+ * Elroy (PCI bus) only for now. To fix this additional work in
+ * SeaBIOS and this driver is needed. See "user_creatable" flag below.
+ * - GMMIO (Greater than 4 GB MMIO) register
+ */
+
+#define TYPE_ASTRO_IOMMU_MEMORY_REGION "astro-iommu-memory-region"
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "hw/irq.h"
+#include "hw/pci/pci_device.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/qdev-properties.h"
+#include "hw/pci-host/astro.h"
+#include "hw/hppa/hppa_hardware.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+#include "qom/object.h"
+
+/*
+ * Helper functions
+ */
+
+static uint64_t mask_32bit_val(hwaddr addr, unsigned size, uint64_t val)
+{
+ if (size == 8) {
+ return val;
+ }
+ if (addr & 4) {
+ val >>= 32;
+ } else {
+ val = (uint32_t) val;
+ }
+ return val;
+}
+
+static void put_val_in_int64(uint64_t *p, hwaddr addr, unsigned size,
+ uint64_t val)
+{
+ if (size == 8) {
+ *p = val;
+ } else if (size == 4) {
+ if (addr & 4) {
+ *p = ((*p << 32) >> 32) | (val << 32);
+ } else {
+ *p = ((*p >> 32) << 32) | (uint32_t) val;
+ }
+ }
+}
+
+static void put_val_in_arrary(uint64_t *array, hwaddr start_addr,
+ hwaddr addr, unsigned size, uint64_t val)
+{
+ int index;
+
+ index = (addr - start_addr) / 8;
+ put_val_in_int64(&array[index], addr, size, val);
+}
+
+
+/*
+ * The Elroy PCI host bridge. We have at least 4 of those under Astro.
+ */
+
+static MemTxResult elroy_chip_read_with_attrs(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ MemTxResult ret = MEMTX_OK;
+ ElroyState *s = opaque;
+ uint64_t val = -1;
+ int index;
+
+ switch ((addr >> 3) << 3) {
+ case 0x0008:
+ val = 0x6000005; /* func_class */
+ break;
+ case 0x0058:
+ /*
+ * Scratch register, but firmware initializes it with the
+ * PCI BUS number and Linux/HP-UX uses it then.
+ */
+ val = s->pci_bus_num;
+ /* Upper byte holds the end of this bus number */
+ val |= s->pci_bus_num << 8;
+ break;
+ case 0x0080:
+ val = s->arb_mask; /* set ARB mask */
+ break;
+ case 0x0108:
+ val = s->status_control;
+ break;
+ case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */
+ index = (addr - 0x200) / 8;
+ val = s->mmio_base[index];
+ break;
+ case 0x0680:
+ val = s->error_config;
+ break;
+ case 0x0688:
+ val = 0; /* ERROR_STATUS */
+ break;
+ case 0x0800: /* IOSAPIC_REG_SELECT */
+ val = s->iosapic_reg_select;
+ break;
+ case 0x0808:
+ val = UINT64_MAX; /* XXX: tbc. */
+ g_assert_not_reached();
+ break;
+ case 0x0810: /* IOSAPIC_REG_WINDOW */
+ switch (s->iosapic_reg_select) {
+ case 0x01: /* IOSAPIC_REG_VERSION */
+ val = (32 << 16) | 1; /* upper 16bit holds max entries */
+ break;
+ default:
+ if (s->iosapic_reg_select < ARRAY_SIZE(s->iosapic_reg)) {
+ val = s->iosapic_reg[s->iosapic_reg_select];
+ } else {
+ trace_iosapic_reg_read(s->iosapic_reg_select, size, val);
+ g_assert_not_reached();
+ }
+ }
+ trace_iosapic_reg_read(s->iosapic_reg_select, size, val);
+ break;
+ default:
+ trace_elroy_read(addr, size, val);
+ g_assert_not_reached();
+ }
+ trace_elroy_read(addr, size, val);
+
+ /* for 32-bit accesses mask return value */
+ val = mask_32bit_val(addr, size, val);
+
+ trace_astro_chip_read(addr, size, val);
+ *data = val;
+ return ret;
+}
+
+
+static MemTxResult elroy_chip_write_with_attrs(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ ElroyState *s = opaque;
+ int i;
+
+ trace_elroy_write(addr, size, val);
+
+ switch ((addr >> 3) << 3) {
+ case 0x080:
+ put_val_in_int64(&s->arb_mask, addr, size, val);
+ break;
+ case 0x0108:
+ put_val_in_int64(&s->status_control, addr, size, val);
+ break;
+ case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */
+ put_val_in_arrary(s->mmio_base, 0x200, addr, size, val);
+ break;
+ case 0x0680:
+ put_val_in_int64(&s->error_config, addr, size, val);
+ break;
+ case 0x0800: /* IOSAPIC_REG_SELECT */
+ s->iosapic_reg_select = val;
+ break;
+ case 0x0810: /* IOSAPIC_REG_WINDOW */
+ trace_iosapic_reg_write(s->iosapic_reg_select, size, val);
+ if (s->iosapic_reg_select < ARRAY_SIZE(s->iosapic_reg)) {
+ s->iosapic_reg[s->iosapic_reg_select] = val;
+ } else {
+ g_assert_not_reached();
+ }
+ break;
+ case 0x0840: /* IOSAPIC_REG_EOI */
+ val = le64_to_cpu(val);
+ val &= 63;
+ for (i = 0; i < ELROY_IRQS; i++) {
+ if ((s->iosapic_reg[0x10 + 2 * i] & 63) == val) {
+ s->ilr &= ~(1ull << i);
+ }
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps elroy_chip_ops = {
+ .read_with_attrs = elroy_chip_read_with_attrs,
+ .write_with_attrs = elroy_chip_write_with_attrs,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
+
+/* Unlike pci_config_data_le_ops, no check of high bit set in config_reg. */
+
+static uint64_t elroy_config_data_read(void *opaque, hwaddr addr, unsigned len)
+{
+ uint64_t val;
+
+ PCIHostState *s = opaque;
+ val = pci_data_read(s->bus, s->config_reg | (addr & 3), len);
+ trace_elroy_pci_config_data_read(s->config_reg | (addr & 3), len, val);
+ return val;
+}
+
+static void elroy_config_data_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ PCIHostState *s = opaque;
+ pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
+ trace_elroy_pci_config_data_write(s->config_reg | (addr & 3), len, val);
+}
+
+static const MemoryRegionOps elroy_config_data_ops = {
+ .read = elroy_config_data_read,
+ .write = elroy_config_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t elroy_config_addr_read(void *opaque, hwaddr addr, unsigned len)
+{
+ ElroyState *s = opaque;
+ return s->config_reg_elroy;
+}
+
+static void elroy_config_addr_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ PCIHostState *s = opaque;
+ ElroyState *es = opaque;
+ es->config_reg_elroy = val; /* keep a copy of original value */
+ s->config_reg = val;
+}
+
+static const MemoryRegionOps elroy_config_addr_ops = {
+ .read = elroy_config_addr_read,
+ .write = elroy_config_addr_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 8,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+
+/*
+ * A subroutine of astro_translate_iommu that builds an IOMMUTLBEntry using the
+ * given translated address and mask.
+ */
+static bool make_iommu_tlbe(hwaddr addr, hwaddr taddr, hwaddr mask,
+ IOMMUTLBEntry *ret)
+{
+ hwaddr tce_mask = ~((1ull << 12) - 1);
+ ret->target_as = &address_space_memory;
+ ret->iova = addr & tce_mask;
+ ret->translated_addr = taddr & tce_mask;
+ ret->addr_mask = ~tce_mask;
+ ret->perm = IOMMU_RW;
+ return true;
+}
+
+/* Handle PCI-to-system address translation. */
+static IOMMUTLBEntry astro_translate_iommu(IOMMUMemoryRegion *iommu,
+ hwaddr addr,
+ IOMMUAccessFlags flag,
+ int iommu_idx)
+{
+ AstroState *s = container_of(iommu, AstroState, iommu);
+ IOMMUTLBEntry ret = {
+ .target_as = &address_space_memory,
+ .iova = addr,
+ .translated_addr = 0,
+ .addr_mask = ~(hwaddr)0,
+ .perm = IOMMU_NONE,
+ };
+ hwaddr pdir_ptr, index, a, ibase;
+ hwaddr addr_mask = 0xfff; /* 4k translation */
+ uint64_t entry;
+
+#define IOVP_SHIFT 12 /* equals PAGE_SHIFT */
+#define PDIR_INDEX(iovp) ((iovp) >> IOVP_SHIFT)
+#define IOVP_MASK PAGE_MASK
+#define SBA_PDIR_VALID_BIT 0x8000000000000000ULL
+
+ /* "range enable" flag cleared? */
+ if ((s->tlb_ibase & 1) == 0) {
+ make_iommu_tlbe(addr, addr, addr_mask, &ret);
+ return ret;
+ }
+
+ a = addr;
+ ibase = s->tlb_ibase & ~1ULL;
+ if ((a & s->tlb_imask) != ibase) {
+ /* do not translate this one! */
+ make_iommu_tlbe(addr, addr, addr_mask, &ret);
+ return ret;
+ }
+ index = PDIR_INDEX(a);
+ pdir_ptr = s->tlb_pdir_base + index * sizeof(entry);
+ entry = ldq_le_phys(&address_space_memory, pdir_ptr);
+ if (!(entry & SBA_PDIR_VALID_BIT)) { /* I/O PDIR entry valid ? */
+ g_assert_not_reached();
+ goto failure;
+ }
+ entry &= ~SBA_PDIR_VALID_BIT;
+ entry >>= IOVP_SHIFT;
+ entry <<= 12;
+ entry |= addr & 0xfff;
+ make_iommu_tlbe(addr, entry, addr_mask, &ret);
+ goto success;
+
+ failure:
+ ret = (IOMMUTLBEntry) { .perm = IOMMU_NONE };
+ success:
+ return ret;
+}
+
+static AddressSpace *elroy_pcihost_set_iommu(PCIBus *bus, void *opaque,
+ int devfn)
+{
+ ElroyState *s = opaque;
+ return &s->astro->iommu_as;
+}
+
+/*
+ * Encoding in IOSAPIC:
+ * base_addr == 0xfffa0000, we want to get 0xa0ff0000.
+ * eid 0x0ff00000 -> 0x00ff0000
+ * id 0x000ff000 -> 0xff000000
+ */
+#define SWIZZLE_HPA(a) \
+ ((((a) & 0x0ff00000) >> 4) | (((a) & 0x000ff000) << 12))
+#define UNSWIZZLE_HPA(a) \
+ (((((a) << 4) & 0x0ff00000) | (((a) >> 12) & 0x000ff000) | 0xf0000000))
+
+/* bits in the "low" I/O Sapic IRdT entry */
+#define IOSAPIC_IRDT_DISABLE 0x10000 /* if bit is set, mask this irq */
+#define IOSAPIC_IRDT_PO_LOW 0x02000
+#define IOSAPIC_IRDT_LEVEL_TRIG 0x08000
+#define IOSAPIC_IRDT_MODE_LPRI 0x00100
+
+#define CPU_IRQ_OFFSET 2
+
+static void elroy_set_irq(void *opaque, int irq, int level)
+{
+ ElroyState *s = opaque;
+ uint32_t bit;
+ uint32_t old_ilr = s->ilr;
+ hwaddr cpu_hpa;
+ uint32_t val;
+
+ val = s->iosapic_reg[0x10 + 2 * irq];
+ cpu_hpa = s->iosapic_reg[0x11 + 2 * irq];
+ /* low nibble of val has value to write into CPU irq reg */
+ bit = 1u << (val & (ELROY_IRQS - 1));
+ cpu_hpa = UNSWIZZLE_HPA(cpu_hpa);
+
+ if (level && (!(val & IOSAPIC_IRDT_DISABLE)) && cpu_hpa) {
+ uint32_t ena = bit & ~old_ilr;
+ s->ilr = old_ilr | bit;
+ if (ena != 0) {
+ stl_be_phys(&address_space_memory, cpu_hpa, val & 63);
+ }
+ } else {
+ s->ilr = old_ilr & ~bit;
+ }
+}
+
+static int elroy_pci_map_irq(PCIDevice *d, int irq_num)
+{
+ int slot = PCI_SLOT(d->devfn);
+
+ assert(irq_num >= 0 && irq_num < ELROY_IRQS);
+ return slot & (ELROY_IRQS - 1);
+}
+
+static void elroy_reset(DeviceState *dev)
+{
+ ElroyState *s = ELROY_PCI_HOST_BRIDGE(dev);
+ int irq;
+
+ /*
+ * Make sure to disable interrupts at reboot, otherwise the Linux kernel
+ * serial8250_config_port() in drivers/tty/serial/8250/8250_port.c
+ * will hang during autoconfig().
+ */
+ s->ilr = 0;
+ for (irq = 0; irq < ELROY_IRQS; irq++) {
+ s->iosapic_reg[0x10 + 2 * irq] = IOSAPIC_IRDT_PO_LOW |
+ IOSAPIC_IRDT_LEVEL_TRIG | (irq + CPU_IRQ_OFFSET) |
+ IOSAPIC_IRDT_DISABLE;
+ s->iosapic_reg[0x11 + 2 * irq] = SWIZZLE_HPA(CPU_HPA);
+ }
+}
+
+static void elroy_pcihost_init(Object *obj)
+{
+ ElroyState *s = ELROY_PCI_HOST_BRIDGE(obj);
+ PCIHostState *phb = PCI_HOST_BRIDGE(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ /* Elroy config access from CPU. */
+ memory_region_init_io(&s->this_mem, OBJECT(s), &elroy_chip_ops,
+ s, "elroy", 0x2000);
+
+ /* Elroy PCI config. */
+ memory_region_init_io(&phb->conf_mem, OBJECT(phb),
+ &elroy_config_addr_ops, DEVICE(s),
+ "pci-conf-idx", 8);
+ memory_region_init_io(&phb->data_mem, OBJECT(phb),
+ &elroy_config_data_ops, DEVICE(s),
+ "pci-conf-data", 8);
+ memory_region_add_subregion(&s->this_mem, 0x40,
+ &phb->conf_mem);
+ memory_region_add_subregion(&s->this_mem, 0x48,
+ &phb->data_mem);
+
+ /* Elroy PCI bus memory. */
+ memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", UINT64_MAX);
+ memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj,
+ "pci-isa-mmio",
+ ((uint32_t) IOS_DIST_BASE_SIZE) / ROPES_PER_IOC);
+
+ phb->bus = pci_register_root_bus(DEVICE(s), "pci",
+ elroy_set_irq, elroy_pci_map_irq, s,
+ &s->pci_mmio, &s->pci_io,
+ PCI_DEVFN(0, 0), ELROY_IRQS, TYPE_PCI_BUS);
+
+ sysbus_init_mmio(sbd, &s->this_mem);
+
+ qdev_init_gpio_in(DEVICE(obj), elroy_set_irq, ELROY_IRQS);
+}
+
+static Property elroy_pcihost_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_elroy = {
+ .name = "Elroy",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(hpa, ElroyState),
+ VMSTATE_UINT32(pci_bus_num, ElroyState),
+ VMSTATE_UINT64(config_address, ElroyState),
+ VMSTATE_UINT64(config_reg_elroy, ElroyState),
+ VMSTATE_UINT64(status_control, ElroyState),
+ VMSTATE_UINT64(arb_mask, ElroyState),
+ VMSTATE_UINT64_ARRAY(mmio_base, ElroyState, (0x0250 - 0x200) / 8),
+ VMSTATE_UINT64(error_config, ElroyState),
+ VMSTATE_UINT32(iosapic_reg_select, ElroyState),
+ VMSTATE_UINT64_ARRAY(iosapic_reg, ElroyState, 0x20),
+ VMSTATE_UINT32(ilr, ElroyState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void elroy_pcihost_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = elroy_reset;
+ device_class_set_props(dc, elroy_pcihost_properties);
+ dc->vmsd = &vmstate_elroy;
+ dc->user_creatable = false;
+}
+
+static const TypeInfo elroy_pcihost_info = {
+ .name = TYPE_ELROY_PCI_HOST_BRIDGE,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_init = elroy_pcihost_init,
+ .instance_size = sizeof(ElroyState),
+ .class_init = elroy_pcihost_class_init,
+};
+
+static void elroy_register_types(void)
+{
+ type_register_static(&elroy_pcihost_info);
+}
+
+type_init(elroy_register_types)
+
+
+static ElroyState *elroy_init(int num)
+{
+ DeviceState *dev;
+
+ dev = qdev_new(TYPE_ELROY_PCI_HOST_BRIDGE);
+ dev->id = g_strdup_printf("elroy%d", num);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ return ELROY_PCI_HOST_BRIDGE(dev);
+}
+
+/*
+ * Astro Runway chip.
+ */
+
+static MemTxResult astro_chip_read_with_attrs(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ AstroState *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+ uint64_t val = -1;
+ int index;
+
+ switch ((addr >> 3) << 3) {
+ /* R2I registers */
+ case 0x0000: /* ID */
+ val = (0x01 << 3) | 0x01ULL;
+ break;
+ case 0x0008: /* IOC_CTRL */
+ val = s->ioc_ctrl;
+ break;
+ case 0x0010: /* TOC_CLIENT_ID */
+ break;
+ case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */
+ val = -1;
+ break;
+ case 0x0300 ... 0x03d8: /* LMMIO_DIRECT0_BASE... */
+ index = (addr - 0x300) / 8;
+ val = s->ioc_ranges[index];
+ break;
+ case 0x10200:
+ val = 0;
+ break;
+ case 0x10220:
+ case 0x10230: /* HP-UX 11.11 reads it. No idea. */
+ val = -1;
+ break;
+ case 0x22108: /* IOC STATUS_CONTROL */
+ val = s->ioc_status_ctrl;
+ break;
+ case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */
+ index = (addr - 0x20200) / 8;
+ val = s->ioc_rope_control[index];
+ break;
+ case 0x20040: /* IOC Rope config */
+ val = s->ioc_rope_config;
+ break;
+ case 0x20050: /* IOC Rope debug */
+ val = 0;
+ break;
+ case 0x20108: /* IOC STATUS_CONTROL */
+ val = s->ioc_status_control;
+ break;
+ case 0x20310: /* IOC_PCOM */
+ val = s->tlb_pcom;
+ /* TODO: flush iommu */
+ break;
+ case 0x20400:
+ val = s->ioc_flush_control;
+ break;
+ /* empty placeholders for non-existent elroys */
+#define EMPTY_PORT(x) case x: case x+8: val = 0; break; \
+ case x+40: case x+48: val = UINT64_MAX; break;
+ EMPTY_PORT(0x30000)
+ EMPTY_PORT(0x32000)
+ EMPTY_PORT(0x34000)
+ EMPTY_PORT(0x36000)
+ EMPTY_PORT(0x38000)
+ EMPTY_PORT(0x3a000)
+ EMPTY_PORT(0x3c000)
+ EMPTY_PORT(0x3e000)
+#undef EMPTY_PORT
+
+ default:
+ trace_astro_chip_read(addr, size, val);
+ g_assert_not_reached();
+ }
+
+ /* for 32-bit accesses mask return value */
+ val = mask_32bit_val(addr, size, val);
+
+ trace_astro_chip_read(addr, size, val);
+ *data = val;
+ return ret;
+}
+
+static MemTxResult astro_chip_write_with_attrs(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ AstroState *s = opaque;
+
+ trace_astro_chip_write(addr, size, val);
+
+ switch ((addr >> 3) << 3) {
+ case 0x0000: /* ID */
+ break;
+ case 0x0008: /* IOC_CTRL */
+ val &= 0x0ffffff;
+ put_val_in_int64(&s->ioc_ctrl, addr, size, val);
+ break;
+ case 0x0010: /* TOC_CLIENT_ID */
+ break;
+ case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */
+ break;
+ case 0x0300 ... 0x03d8 - 1: /* LMMIO_DIRECT0_BASE... */
+ put_val_in_arrary(s->ioc_ranges, 0x300, addr, size, val);
+ break;
+ case 0x10200:
+ case 0x10220:
+ case 0x10230: /* HP-UX 11.11 reads it. No idea. */
+ break;
+ case 0x22108: /* IOC STATUS_CONTROL */
+ put_val_in_int64(&s->ioc_status_ctrl, addr, size, val);
+ break;
+ case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */
+ put_val_in_arrary(s->ioc_rope_control, 0x20200, addr, size, val);
+ break;
+ case 0x20040: /* IOC Rope config */
+ put_val_in_int64(&s->ioc_rope_config, addr, size, val);
+ break;
+ case 0x20300:
+ put_val_in_int64(&s->tlb_ibase, addr, size, val);
+ break;
+ case 0x20308:
+ put_val_in_int64(&s->tlb_imask, addr, size, val);
+ break;
+ case 0x20310:
+ put_val_in_int64(&s->tlb_pcom, addr, size, val);
+ /* TODO: flush iommu */
+ break;
+ case 0x20318:
+ put_val_in_int64(&s->tlb_tcnfg, addr, size, val);
+ break;
+ case 0x20320:
+ put_val_in_int64(&s->tlb_pdir_base, addr, size, val);
+ break;
+ /*
+ * empty placeholders for non-existent elroys, e.g.
+ * func_class, pci config & data
+ */
+#define EMPTY_PORT(x) case x: case x+8: case x+0x40: case x+0x48:
+ EMPTY_PORT(0x30000)
+ EMPTY_PORT(0x32000)
+ EMPTY_PORT(0x34000)
+ EMPTY_PORT(0x36000)
+ EMPTY_PORT(0x38000)
+ EMPTY_PORT(0x3a000)
+ EMPTY_PORT(0x3c000)
+ EMPTY_PORT(0x3e000)
+ break;
+#undef EMPTY_PORT
+
+ default:
+ /* Controlled by astro_chip_mem_valid above. */
+ trace_astro_chip_write(addr, size, val);
+ g_assert_not_reached();
+ }
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps astro_chip_ops = {
+ .read_with_attrs = astro_chip_read_with_attrs,
+ .write_with_attrs = astro_chip_write_with_attrs,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
+static const VMStateDescription vmstate_astro = {
+ .name = "Astro",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(ioc_ctrl, AstroState),
+ VMSTATE_UINT64(ioc_status_ctrl, AstroState),
+ VMSTATE_UINT64_ARRAY(ioc_ranges, AstroState, (0x03d8 - 0x300) / 8),
+ VMSTATE_UINT64(ioc_rope_config, AstroState),
+ VMSTATE_UINT64(ioc_status_control, AstroState),
+ VMSTATE_UINT64(ioc_flush_control, AstroState),
+ VMSTATE_UINT64_ARRAY(ioc_rope_control, AstroState, 8),
+ VMSTATE_UINT64(tlb_ibase, AstroState),
+ VMSTATE_UINT64(tlb_imask, AstroState),
+ VMSTATE_UINT64(tlb_pcom, AstroState),
+ VMSTATE_UINT64(tlb_tcnfg, AstroState),
+ VMSTATE_UINT64(tlb_pdir_base, AstroState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void astro_reset(DeviceState *dev)
+{
+ AstroState *s = ASTRO_CHIP(dev);
+ int i;
+
+ s->ioc_ctrl = 0x29cf;
+ s->ioc_rope_config = 0xc5f;
+ s->ioc_flush_control = 0xb03;
+ s->ioc_status_control = 0;
+ memset(&s->ioc_rope_control, 0, sizeof(s->ioc_rope_control));
+
+ /*
+ * The SBA BASE/MASK registers control CPU -> IO routing.
+ * The LBA BASE/MASK registers control IO -> System routing (in Elroy)
+ */
+ memset(&s->ioc_ranges, 0, sizeof(s->ioc_ranges));
+ s->ioc_ranges[(0x360 - 0x300) / 8] = LMMIO_DIST_BASE_ADDR | 0x01; /* LMMIO_DIST_BASE (SBA) */
+ s->ioc_ranges[(0x368 - 0x300) / 8] = 0xfc000000; /* LMMIO_DIST_MASK */
+ s->ioc_ranges[(0x370 - 0x300) / 8] = 0; /* LMMIO_DIST_ROUTE */
+ s->ioc_ranges[(0x390 - 0x300) / 8] = IOS_DIST_BASE_ADDR | 0x01; /* IOS_DIST_BASE */
+ s->ioc_ranges[(0x398 - 0x300) / 8] = 0xffffff0000; /* IOS_DIST_MASK */
+ s->ioc_ranges[(0x3a0 - 0x300) / 8] = 0x3400000000000000ULL; /* IOS_DIST_ROUTE */
+ s->ioc_ranges[(0x3c0 - 0x300) / 8] = 0xfffee00000; /* IOS_DIRECT_BASE */
+ s->ioc_ranges[(0x3c8 - 0x300) / 8] = 0xffffff0000; /* IOS_DIRECT_MASK */
+ s->ioc_ranges[(0x3d0 - 0x300) / 8] = 0x0; /* IOS_DIRECT_ROUTE */
+
+ s->tlb_ibase = 0;
+ s->tlb_imask = 0;
+ s->tlb_pcom = 0;
+ s->tlb_tcnfg = 0;
+ s->tlb_pdir_base = 0;
+
+ for (i = 0; i < ELROY_NUM; i++) {
+ elroy_reset(DEVICE(s->elroy[i]));
+ }
+}
+
+static void astro_init(Object *obj)
+{
+}
+
+static void astro_realize(DeviceState *obj, Error **errp)
+{
+ AstroState *s = ASTRO_CHIP(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ int i;
+
+ memory_region_init_io(&s->this_mem, OBJECT(s), &astro_chip_ops,
+ s, "astro", 0x40000);
+ sysbus_init_mmio(sbd, &s->this_mem);
+
+ /* Host memory as seen from Elroys PCI side, via the IOMMU. */
+ memory_region_init_iommu(&s->iommu, sizeof(s->iommu),
+ TYPE_ASTRO_IOMMU_MEMORY_REGION, OBJECT(s),
+ "iommu-astro", UINT64_MAX);
+ address_space_init(&s->iommu_as, MEMORY_REGION(&s->iommu),
+ "bm-pci");
+
+ /* Create Elroys (PCI host bus chips). */
+ for (i = 0; i < ELROY_NUM; i++) {
+ static const int elroy_hpa_offsets[ELROY_NUM] = {
+ 0x30000, 0x32000, 0x38000, 0x3c000 };
+ static const char elroy_rope_nr[ELROY_NUM] = {
+ 0, 1, 4, 6 }; /* busnum path, e.g. [10:6] */
+ int addr_offset;
+ ElroyState *elroy;
+ hwaddr map_addr;
+ uint64_t map_size;
+ int rope;
+
+ addr_offset = elroy_hpa_offsets[i];
+ rope = elroy_rope_nr[i];
+
+ elroy = elroy_init(i);
+ s->elroy[i] = elroy;
+ elroy->hpa = ASTRO_HPA + addr_offset;
+ elroy->pci_bus_num = i;
+ elroy->astro = s;
+
+ /*
+ * NOTE: we only allow PCI devices on first Elroy for now.
+ * SeaBIOS will not find devices on the other busses.
+ */
+ if (i > 0) {
+ qbus_mark_full(&PCI_HOST_BRIDGE(elroy)->bus->qbus);
+ }
+
+ /* map elroy config addresses into Astro space */
+ memory_region_add_subregion(&s->this_mem, addr_offset,
+ &elroy->this_mem);
+
+ /* LMMIO */
+ elroy->mmio_base[(0x0200 - 0x200) / 8] = 0xf0000001;
+ elroy->mmio_base[(0x0208 - 0x200) / 8] = 0xf8000000;
+ /* GMMIO */
+ elroy->mmio_base[(0x0210 - 0x200) / 8] = 0x000000f800000001;
+ elroy->mmio_base[(0x0218 - 0x200) / 8] = 0x000000ff80000000;
+ /* WLMMIO */
+ elroy->mmio_base[(0x0220 - 0x200) / 8] = 0xf0000001;
+ elroy->mmio_base[(0x0228 - 0x200) / 8] = 0xf0000000;
+ /* WGMMIO */
+ elroy->mmio_base[(0x0230 - 0x200) / 8] = 0x000000f800000001;
+ elroy->mmio_base[(0x0238 - 0x200) / 8] = 0x000000fc00000000;
+ /* IOS_BASE */
+ map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC;
+ elroy->mmio_base[(0x0240 - 0x200) / 8] = rope * map_size | 0x01;
+ elroy->mmio_base[(0x0248 - 0x200) / 8] = 0x0000e000;
+
+ /* map elroys mmio */
+ map_size = LMMIO_DIST_BASE_SIZE / ROPES_PER_IOC;
+ map_addr = (uint32_t) (LMMIO_DIST_BASE_ADDR + rope * map_size);
+ memory_region_init_alias(&elroy->pci_mmio_alias, OBJECT(elroy),
+ "pci-mmio-alias",
+ &elroy->pci_mmio, map_addr, map_size);
+ memory_region_add_subregion(get_system_memory(), map_addr,
+ &elroy->pci_mmio_alias);
+
+ map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC;
+ map_addr = (uint32_t) (IOS_DIST_BASE_ADDR + rope * map_size);
+ memory_region_add_subregion(get_system_memory(), map_addr,
+ &elroy->pci_io);
+
+ /* Host memory as seen from the PCI side, via the IOMMU. */
+ pci_setup_iommu(PCI_HOST_BRIDGE(elroy)->bus, elroy_pcihost_set_iommu,
+ elroy);
+ }
+}
+
+static void astro_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = astro_reset;
+ dc->vmsd = &vmstate_astro;
+ dc->realize = astro_realize;
+ /*
+ * astro with elroys are hard part of the newer PA2.0 machines and can not
+ * be created without that hardware
+ */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo astro_chip_info = {
+ .name = TYPE_ASTRO_CHIP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = astro_init,
+ .instance_size = sizeof(AstroState),
+ .class_init = astro_class_init,
+};
+
+static void astro_iommu_memory_region_class_init(ObjectClass *klass,
+ void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate = astro_translate_iommu;
+}
+
+static const TypeInfo astro_iommu_memory_region_info = {
+ .parent = TYPE_IOMMU_MEMORY_REGION,
+ .name = TYPE_ASTRO_IOMMU_MEMORY_REGION,
+ .class_init = astro_iommu_memory_region_class_init,
+};
+
+
+static void astro_register_types(void)
+{
+ type_register_static(&astro_chip_info);
+ type_register_static(&astro_iommu_memory_region_info);
+}
+
+type_init(astro_register_types)
diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build
index 64eada7..f891f02 100644
--- a/hw/pci-host/meson.build
+++ b/hw/pci-host/meson.build
@@ -27,6 +27,7 @@ pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c'))
pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c'))
# HPPA devices
+pci_ss.add(when: 'CONFIG_ASTRO', if_true: files('astro.c'))
pci_ss.add(when: 'CONFIG_DINO', if_true: files('dino.c'))
system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss)
diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
index 9d216bb..b2f47e6 100644
--- a/hw/pci-host/trace-events
+++ b/hw/pci-host/trace-events
@@ -46,3 +46,14 @@ pnv_phb4_xive_notify_abt(uint64_t notif_port, uint64_t data) "notif=@0x%"PRIx64"
dino_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d"
dino_chip_read(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x"
dino_chip_write(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x"
+
+# astro.c
+astro_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d"
+astro_chip_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+astro_chip_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_pci_config_data_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+elroy_pci_config_data_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64
+iosapic_reg_write(uint64_t reg_select, int size, uint64_t val) "reg_select 0x%"PRIx64" size %d val 0x%"PRIx64
+iosapic_reg_read(uint64_t reg_select, int size, uint64_t val) "reg_select 0x%"PRIx64" size %d val 0x%"PRIx64
diff --git a/include/hw/pci-host/astro.h b/include/hw/pci-host/astro.h
new file mode 100644
index 0000000..f63fd22
--- /dev/null
+++ b/include/hw/pci-host/astro.h
@@ -0,0 +1,92 @@
+/*
+ * HP-PARISC Astro Bus connector with Elroy PCI host bridges
+ */
+
+#ifndef ASTRO_H
+#define ASTRO_H
+
+#include "hw/pci/pci_host.h"
+
+#define ASTRO_HPA 0xfed00000
+
+#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */
+
+#define TYPE_ASTRO_CHIP "astro-chip"
+OBJECT_DECLARE_SIMPLE_TYPE(AstroState, ASTRO_CHIP)
+
+#define TYPE_ELROY_PCI_HOST_BRIDGE "elroy-pcihost"
+OBJECT_DECLARE_SIMPLE_TYPE(ElroyState, ELROY_PCI_HOST_BRIDGE)
+
+#define ELROY_NUM 4 /* # of Elroys */
+#define ELROY_IRQS 8 /* IOSAPIC IRQs */
+
+/* ASTRO Memory and I/O regions */
+#define LMMIO_DIST_BASE_ADDR 0xf4000000ULL
+#define LMMIO_DIST_BASE_SIZE 0x4000000ULL
+
+#define IOS_DIST_BASE_ADDR 0xfffee00000ULL
+#define IOS_DIST_BASE_SIZE 0x10000ULL
+
+struct AstroState;
+
+struct ElroyState {
+ PCIHostState parent_obj;
+
+ /* parent Astro device */
+ struct AstroState *astro;
+
+ /* HPA of this Elroy */
+ hwaddr hpa;
+
+ /* PCI bus number (Elroy number) */
+ unsigned int pci_bus_num;
+
+ uint64_t config_address;
+ uint64_t config_reg_elroy;
+
+ uint64_t status_control;
+ uint64_t arb_mask;
+ uint64_t mmio_base[(0x0250 - 0x200) / 8];
+ uint64_t error_config;
+
+ uint32_t iosapic_reg_select;
+ uint64_t iosapic_reg[0x20];
+
+ uint32_t ilr;
+
+ MemoryRegion this_mem;
+
+ MemoryRegion pci_mmio;
+ MemoryRegion pci_mmio_alias;
+ MemoryRegion pci_hole;
+ MemoryRegion pci_io;
+};
+
+struct AstroState {
+ PCIHostState parent_obj;
+
+ uint64_t ioc_ctrl;
+ uint64_t ioc_status_ctrl;
+ uint64_t ioc_ranges[(0x03d8 - 0x300) / 8];
+ uint64_t ioc_rope_config;
+ uint64_t ioc_status_control;
+ uint64_t ioc_flush_control;
+ uint64_t ioc_rope_control[8];
+ uint64_t tlb_ibase;
+ uint64_t tlb_imask;
+ uint64_t tlb_pcom;
+ uint64_t tlb_tcnfg;
+ uint64_t tlb_pdir_base;
+
+ struct ElroyState *elroy[ELROY_NUM];
+
+ MemoryRegion this_mem;
+
+ MemoryRegion pci_mmio;
+ MemoryRegion pci_io;
+
+ IOMMUMemoryRegion iommu;
+ AddressSpace iommu_as;
+};
+
+#endif
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 85469b9..f1a53fe 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -179,6 +179,8 @@
#define PCI_DEVICE_ID_AMD_LANCE 0x2000
#define PCI_DEVICE_ID_AMD_SCSI 0x2020
+#define PCI_VENDOR_ID_HP 0x103c
+
#define PCI_VENDOR_ID_TI 0x104c
#define PCI_VENDOR_ID_MOTOROLA 0x1057
diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img
index c719614..e976c0c 100644
--- a/pc-bios/hppa-firmware.img
+++ b/pc-bios/hppa-firmware.img
Binary files differ
diff --git a/roms/seabios-hppa b/roms/seabios-hppa
-Subproject 763e3b73499db5fef94087bd310bfc8ccbcf785
+Subproject fd5b6cf82369a1e53d68302fb6ede2b9e2afccd