target/hppa: Add emulation of a C3700 HP-PARISC workstation

This series adds a new PA-RISC machine emulation for the HP-PARISC
 C3700 workstation.
 
 The physical HP C3700 machine has a PA2.0 (64-bit) CPU, in contrast to
 the existing emulation of a B160L workstation which is a 32-bit only
 machine and where it's Dino PCI controller isn't 64-bit capable.
 
 With the HP C3700 machine emulation (together with the emulated Astro
 Memory controller and the Elroy PCI bridge) it's now possible to
 enhance the hppa CPU emulation to support the 64-bit instruction set
 in upcoming patches.
 
 Helge
 
 v4 changes:
 - Fix testsuite error in astro by adding a realize() implementation
 
 v3 changes:
 based on feedback from BALATON Zoltan <balaton@eik.bme.hu>:
 - apply paches in different order to bring them logically closer to each other
 - update comments in lasips2
 - rephrased title and commit message of MAINTAINERS patch
 
 v2 changes:
 suggestions by BALATON Zoltan <balaton@eik.bme.hu>:
 - merged pci_ids and tulip patch
 - dropped comments in lasips2
 - mention additional cleanups in patch "Require at least SeaBIOS-hppa version 10"
 suggestions by Philippe Mathieu-Daudé <philmd@linaro.org>:
 - dropped static pci_bus variable
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQS86RI+GtKfB8BJu973ErUQojoPXwUCZTGzDQAKCRD3ErUQojoP
 X9psAP0cHfTuJuXMiBWhrJhfp5VV0TURvaNXjCGyK8qvfbK+zgEArg3nvKhZPvnu
 jVSq6b/Ppf3eCAZIYSVIsfLITbElTQ4=
 =Esj+
 -----END PGP SIGNATURE-----

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

target/hppa: Add emulation of a C3700 HP-PARISC workstation

This series adds a new PA-RISC machine emulation for the HP-PARISC
C3700 workstation.

The physical HP C3700 machine has a PA2.0 (64-bit) CPU, in contrast to
the existing emulation of a B160L workstation which is a 32-bit only
machine and where it's Dino PCI controller isn't 64-bit capable.

With the HP C3700 machine emulation (together with the emulated Astro
Memory controller and the Elroy PCI bridge) it's now possible to
enhance the hppa CPU emulation to support the 64-bit instruction set
in upcoming patches.

Helge

v4 changes:
- Fix testsuite error in astro by adding a realize() implementation

v3 changes:
based on feedback from BALATON Zoltan <balaton@eik.bme.hu>:
- apply paches in different order to bring them logically closer to each other
- update comments in lasips2
- rephrased title and commit message of MAINTAINERS patch

v2 changes:
suggestions by BALATON Zoltan <balaton@eik.bme.hu>:
- merged pci_ids and tulip patch
- dropped comments in lasips2
- mention additional cleanups in patch "Require at least SeaBIOS-hppa version 10"
suggestions by Philippe Mathieu-Daudé <philmd@linaro.org>:
- dropped static pci_bus variable

# -----BEGIN PGP SIGNATURE-----
#
# iHUEABYKAB0WIQS86RI+GtKfB8BJu973ErUQojoPXwUCZTGzDQAKCRD3ErUQojoP
# X9psAP0cHfTuJuXMiBWhrJhfp5VV0TURvaNXjCGyK8qvfbK+zgEArg3nvKhZPvnu
# jVSq6b/Ppf3eCAZIYSVIsfLITbElTQ4=
# =Esj+
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 19 Oct 2023 15:51:57 PDT
# 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 'C3700-pull-request' of https://github.com/hdeller/qemu-hppa:
  hw/hppa: Add new HP C3700 machine
  hw/hppa: Split out machine creation
  hw/hppa: Provide RTC and DebugOutputPort on CPU #0
  hw/hppa: Export machine name, BTLBs, power-button address via fw_cfg
  MAINTAINERS: Update HP-PARISC entries
  pci-host: Wire up new Astro/Elroy PCI bridge
  hw/pci-host: Add Astro system bus adapter found on PA-RISC machines
  lasips2: LASI PS/2 devices are not user-createable
  pci_ids/tulip: Add PCI vendor ID for HP and use it in tulip
  hw/hppa: Require at least SeaBIOS-hppa version 10
  target/hppa: Update to SeaBIOS-hppa version 10

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2023-10-20 06:46:26 -07:00
commit 749d14f782
14 changed files with 1301 additions and 82 deletions

View File

@ -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
------------------

View File

@ -3,6 +3,7 @@ config HPPA_B160L
imply PCI_DEVICES
imply E1000_PCI
imply VIRTIO_VGA
select ASTRO
select DINO
select LASI
select SERIAL

View File

@ -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

View File

@ -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 = hppa_machine_init_class_init,
.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 = 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)

View File

@ -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);
}

View File

@ -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;

View File

@ -82,6 +82,10 @@ config DINO
bool
select PCI
config ASTRO
bool
select PCI
config GT64120
bool
select PCI

885
hw/pci-host/astro.c Normal file
View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

@ -1 +1 @@
Subproject commit 763e3b73499db5fef94087bd310bfc8ccbcf7858
Subproject commit fd5b6cf82369a1e53d68302fb6ede2b9e2afccd1