From nobody Wed Nov 27 18:48:20 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=kernel.org ARC-Seal: i=1; a=rsa-sha256; t=1697756069; cv=none; d=zohomail.com; s=zohoarc; b=RtxdbnIKOuYK+XHF8dPY5NYSiYbd7R1Kdva8cpMHkTVkFyblaxZxYwHhCUb2mhL/klMlrASilHRO/OSPvxeUxA6fZIU8URLCaZuptult5H4LxToNGihUFH40hBdPLYJzfzWCm+C0a4yPIpg3oycJQpwNKNpA0VpKGZW75tTHB00= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1697756069; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=ZXGeluVHnXa9tJpOM7or5e5g8z0pnvlVXJ+PglX7uf0=; b=DChucv+PGuL7PO2yzd4D7+uAAHbBzkQTpWC6IOJbgBbgxDbnH9WosW0ST+w8BFOVQBpa5IR8eNkeZ7eiUigT/kkaiNMJ9I/Y3x8IWjjqzAC2tOUl3mr2/XcTzJyswrL8X2/BCA4MwEn2IpU7XCnTGb7lLxfXPzX32QHvifSaMW4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1697756069344911.8761457659493; Thu, 19 Oct 2023 15:54:29 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qtbtK-0008Ao-Lr; Thu, 19 Oct 2023 18:53:22 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qtbtH-000871-3E for qemu-devel@nongnu.org; Thu, 19 Oct 2023 18:53:19 -0400 Received: from sin.source.kernel.org ([2604:1380:40e1:4800::1]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qtbtC-0002ev-KE for qemu-devel@nongnu.org; Thu, 19 Oct 2023 18:53:18 -0400 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id 9307CCE32A1; Thu, 19 Oct 2023 22:53:11 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 380BAC433CB; Thu, 19 Oct 2023 22:53:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1697755990; bh=uijV04DfwINzhbyg3z2qCHOLi1MMbWPlGrr1cassqVA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XGDIx9mXaEkYV7q+JnqV3Eddb1PqOgTiDjPqXwqLgnAe4iOSI31RtTM3iAiwdqUMk sDZ1qbZq1REy1U1CEBvlZikzKYbfHpvL7JXxlOmogYMbk0rZTA9yJIilCV8+1yAevH 4G80IjQG7l3sR1YjCE/ShvTPBj+gdF5k601i3n/O/SLxaHoEzM2gbwtFafcUsW15Jv 0PZ/3aUIyfxuWhwNCgiMlYPPEw75YiI+vSpSXSR/1UfPX0oNVSIC91oUiyp/2sLZhx KtaSaEI4+HKS5xTCxZENQSDSOIsAVhlp3Lve9l78f0fWsagJkTVTQAd4cEgU2XxXMo Pc1J2N2DQC8qQ== From: deller@kernel.org To: Stefan Hajnoczi , qemu-devel@nongnu.org Cc: Helge Deller , Richard Henderson Subject: [PULL 05/11] hw/pci-host: Add Astro system bus adapter found on PA-RISC machines Date: Fri, 20 Oct 2023 00:52:51 +0200 Message-ID: <20231019225257.383071-6-deller@kernel.org> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231019225257.383071-1-deller@kernel.org> References: <20231019225257.383071-1-deller@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2604:1380:40e1:4800::1; envelope-from=deller@kernel.org; helo=sin.source.kernel.org X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @kernel.org) X-ZM-MESSAGEID: 1697756071100100003 Content-Type: text/plain; charset="utf-8" From: Helge Deller The 64-bit PA-RISC machines use a Astro system bus adapter (SBA) with Elroy PCI host chips. Later generation Astro chips were named Pluto, Ike and REO. Signed-off-by: Helge Deller --- hw/pci-host/astro.c | 885 ++++++++++++++++++++++++++++++++++++ hw/pci-host/trace-events | 11 + include/hw/pci-host/astro.h | 92 ++++ 3 files changed, 988 insertions(+) create mode 100644 hw/pci-host/astro.c create mode 100644 include/hw/pci-host/astro.h diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c new file mode 100644 index 0000000000..4b2d7caf2d --- /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 + * + * 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 =3D=3D 8) { + return val; + } + if (addr & 4) { + val >>=3D 32; + } else { + val =3D (uint32_t) val; + } + return val; +} + +static void put_val_in_int64(uint64_t *p, hwaddr addr, unsigned size, + uint64_t val) +{ + if (size =3D=3D 8) { + *p =3D val; + } else if (size =3D=3D 4) { + if (addr & 4) { + *p =3D ((*p << 32) >> 32) | (val << 32); + } else { + *p =3D ((*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 =3D (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 =3D MEMTX_OK; + ElroyState *s =3D opaque; + uint64_t val =3D -1; + int index; + + switch ((addr >> 3) << 3) { + case 0x0008: + val =3D 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 =3D s->pci_bus_num; + /* Upper byte holds the end of this bus number */ + val |=3D s->pci_bus_num << 8; + break; + case 0x0080: + val =3D s->arb_mask; /* set ARB mask */ + break; + case 0x0108: + val =3D s->status_control; + break; + case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */ + index =3D (addr - 0x200) / 8; + val =3D s->mmio_base[index]; + break; + case 0x0680: + val =3D s->error_config; + break; + case 0x0688: + val =3D 0; /* ERROR_STATUS */ + break; + case 0x0800: /* IOSAPIC_REG_SELECT */ + val =3D s->iosapic_reg_select; + break; + case 0x0808: + val =3D 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 =3D (32 << 16) | 1; /* upper 16bit holds max entries */ + break; + default: + if (s->iosapic_reg_select < ARRAY_SIZE(s->iosapic_reg)) { + val =3D 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 =3D mask_32bit_val(addr, size, val); + + trace_astro_chip_read(addr, size, val); + *data =3D val; + return ret; +} + + +static MemTxResult elroy_chip_write_with_attrs(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + ElroyState *s =3D 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 =3D 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] =3D val; + } else { + g_assert_not_reached(); + } + break; + case 0x0840: /* IOSAPIC_REG_EOI */ + val =3D le64_to_cpu(val); + val &=3D 63; + for (i =3D 0; i < ELROY_IRQS; i++) { + if ((s->iosapic_reg[0x10 + 2 * i] & 63) =3D=3D val) { + s->ilr &=3D ~(1ull << i); + } + } + break; + default: + g_assert_not_reached(); + } + return MEMTX_OK; +} + +static const MemoryRegionOps elroy_chip_ops =3D { + .read_with_attrs =3D elroy_chip_read_with_attrs, + .write_with_attrs =3D elroy_chip_write_with_attrs, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 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 =3D opaque; + val =3D 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 =3D 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 =3D { + .read =3D elroy_config_data_read, + .write =3D elroy_config_data_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t elroy_config_addr_read(void *opaque, hwaddr addr, unsigned= len) +{ + ElroyState *s =3D opaque; + return s->config_reg_elroy; +} + +static void elroy_config_addr_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s =3D opaque; + ElroyState *es =3D opaque; + es->config_reg_elroy =3D val; /* keep a copy of original value */ + s->config_reg =3D val; +} + +static const MemoryRegionOps elroy_config_addr_ops =3D { + .read =3D elroy_config_addr_read, + .write =3D elroy_config_addr_write, + .valid.min_access_size =3D 4, + .valid.max_access_size =3D 8, + .endianness =3D DEVICE_LITTLE_ENDIAN, +}; + + +/* + * A subroutine of astro_translate_iommu that builds an IOMMUTLBEntry usin= g the + * given translated address and mask. + */ +static bool make_iommu_tlbe(hwaddr addr, hwaddr taddr, hwaddr mask, + IOMMUTLBEntry *ret) +{ + hwaddr tce_mask =3D ~((1ull << 12) - 1); + ret->target_as =3D &address_space_memory; + ret->iova =3D addr & tce_mask; + ret->translated_addr =3D taddr & tce_mask; + ret->addr_mask =3D ~tce_mask; + ret->perm =3D 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 =3D container_of(iommu, AstroState, iommu); + IOMMUTLBEntry ret =3D { + .target_as =3D &address_space_memory, + .iova =3D addr, + .translated_addr =3D 0, + .addr_mask =3D ~(hwaddr)0, + .perm =3D IOMMU_NONE, + }; + hwaddr pdir_ptr, index, a, ibase; + hwaddr addr_mask =3D 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) =3D=3D 0) { + make_iommu_tlbe(addr, addr, addr_mask, &ret); + return ret; + } + + a =3D addr; + ibase =3D s->tlb_ibase & ~1ULL; + if ((a & s->tlb_imask) !=3D ibase) { + /* do not translate this one! */ + make_iommu_tlbe(addr, addr, addr_mask, &ret); + return ret; + } + index =3D PDIR_INDEX(a); + pdir_ptr =3D s->tlb_pdir_base + index * sizeof(entry); + entry =3D 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 &=3D ~SBA_PDIR_VALID_BIT; + entry >>=3D IOVP_SHIFT; + entry <<=3D 12; + entry |=3D addr & 0xfff; + make_iommu_tlbe(addr, entry, addr_mask, &ret); + goto success; + + failure: + ret =3D (IOMMUTLBEntry) { .perm =3D IOMMU_NONE }; + success: + return ret; +} + +static AddressSpace *elroy_pcihost_set_iommu(PCIBus *bus, void *opaque, + int devfn) +{ + ElroyState *s =3D opaque; + return &s->astro->iommu_as; +} + +/* + * Encoding in IOSAPIC: + * base_addr =3D=3D 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) | 0xf0000= 000)) + +/* 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 =3D opaque; + uint32_t bit; + uint32_t old_ilr =3D s->ilr; + hwaddr cpu_hpa; + uint32_t val; + + val =3D s->iosapic_reg[0x10 + 2 * irq]; + cpu_hpa =3D s->iosapic_reg[0x11 + 2 * irq]; + /* low nibble of val has value to write into CPU irq reg */ + bit =3D 1u << (val & (ELROY_IRQS - 1)); + cpu_hpa =3D UNSWIZZLE_HPA(cpu_hpa); + + if (level && (!(val & IOSAPIC_IRDT_DISABLE)) && cpu_hpa) { + uint32_t ena =3D bit & ~old_ilr; + s->ilr =3D old_ilr | bit; + if (ena !=3D 0) { + stl_be_phys(&address_space_memory, cpu_hpa, val & 63); + } + } else { + s->ilr =3D old_ilr & ~bit; + } +} + +static int elroy_pci_map_irq(PCIDevice *d, int irq_num) +{ + int slot =3D PCI_SLOT(d->devfn); + + assert(irq_num >=3D 0 && irq_num < ELROY_IRQS); + return slot & (ELROY_IRQS - 1); +} + +static void elroy_reset(DeviceState *dev) +{ + ElroyState *s =3D ELROY_PCI_HOST_BRIDGE(dev); + int irq; + + /* + * Make sure to disable interrupts at reboot, otherwise the Linux kern= el + * serial8250_config_port() in drivers/tty/serial/8250/8250_port.c + * will hang during autoconfig(). + */ + s->ilr =3D 0; + for (irq =3D 0; irq < ELROY_IRQS; irq++) { + s->iosapic_reg[0x10 + 2 * irq] =3D IOSAPIC_IRDT_PO_LOW | + IOSAPIC_IRDT_LEVEL_TRIG | (irq + CPU_IRQ_OFFSET) | + IOSAPIC_IRDT_DISABLE; + s->iosapic_reg[0x11 + 2 * irq] =3D SWIZZLE_HPA(CPU_HPA); + } +} + +static void elroy_pcihost_init(Object *obj) +{ + ElroyState *s =3D ELROY_PCI_HOST_BRIDGE(obj); + PCIHostState *phb =3D PCI_HOST_BRIDGE(obj); + SysBusDevice *sbd =3D 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_IO= C); + + phb->bus =3D 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[] =3D { + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_elroy =3D { + .name =3D "Elroy", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (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 =3D DEVICE_CLASS(klass); + + dc->reset =3D elroy_reset; + device_class_set_props(dc, elroy_pcihost_properties); + dc->vmsd =3D &vmstate_elroy; + dc->user_creatable =3D false; +} + +static const TypeInfo elroy_pcihost_info =3D { + .name =3D TYPE_ELROY_PCI_HOST_BRIDGE, + .parent =3D TYPE_PCI_HOST_BRIDGE, + .instance_init =3D elroy_pcihost_init, + .instance_size =3D sizeof(ElroyState), + .class_init =3D 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 =3D qdev_new(TYPE_ELROY_PCI_HOST_BRIDGE); + dev->id =3D 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 =3D opaque; + MemTxResult ret =3D MEMTX_OK; + uint64_t val =3D -1; + int index; + + switch ((addr >> 3) << 3) { + /* R2I registers */ + case 0x0000: /* ID */ + val =3D (0x01 << 3) | 0x01ULL; + break; + case 0x0008: /* IOC_CTRL */ + val =3D s->ioc_ctrl; + break; + case 0x0010: /* TOC_CLIENT_ID */ + break; + case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */ + val =3D -1; + break; + case 0x0300 ... 0x03d8: /* LMMIO_DIRECT0_BASE... */ + index =3D (addr - 0x300) / 8; + val =3D s->ioc_ranges[index]; + break; + case 0x10200: + val =3D 0; + break; + case 0x10220: + case 0x10230: /* HP-UX 11.11 reads it. No idea. */ + val =3D -1; + break; + case 0x22108: /* IOC STATUS_CONTROL */ + val =3D s->ioc_status_ctrl; + break; + case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */ + index =3D (addr - 0x20200) / 8; + val =3D s->ioc_rope_control[index]; + break; + case 0x20040: /* IOC Rope config */ + val =3D s->ioc_rope_config; + break; + case 0x20050: /* IOC Rope debug */ + val =3D 0; + break; + case 0x20108: /* IOC STATUS_CONTROL */ + val =3D s->ioc_status_control; + break; + case 0x20310: /* IOC_PCOM */ + val =3D s->tlb_pcom; + /* TODO: flush iommu */ + break; + case 0x20400: + val =3D s->ioc_flush_control; + break; + /* empty placeholders for non-existent elroys */ +#define EMPTY_PORT(x) case x: case x+8: val =3D 0; break; \ + case x+40: case x+48: val =3D 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 =3D mask_32bit_val(addr, size, val); + + trace_astro_chip_read(addr, size, val); + *data =3D val; + return ret; +} + +static MemTxResult astro_chip_write_with_attrs(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + AstroState *s =3D opaque; + + trace_astro_chip_write(addr, size, val); + + switch ((addr >> 3) << 3) { + case 0x0000: /* ID */ + break; + case 0x0008: /* IOC_CTRL */ + val &=3D 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 =3D { + .read_with_attrs =3D astro_chip_read_with_attrs, + .write_with_attrs =3D astro_chip_write_with_attrs, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static const VMStateDescription vmstate_astro =3D { + .name =3D "Astro", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (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 =3D ASTRO_CHIP(dev); + int i; + + s->ioc_ctrl =3D 0x29cf; + s->ioc_rope_config =3D 0xc5f; + s->ioc_flush_control =3D 0xb03; + s->ioc_status_control =3D 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] =3D LMMIO_DIST_BASE_ADDR | 0x01; /*= LMMIO_DIST_BASE (SBA) */ + s->ioc_ranges[(0x368 - 0x300) / 8] =3D 0xfc000000; /* LMMIO_D= IST_MASK */ + s->ioc_ranges[(0x370 - 0x300) / 8] =3D 0; /* LMMIO_D= IST_ROUTE */ + s->ioc_ranges[(0x390 - 0x300) / 8] =3D IOS_DIST_BASE_ADDR | 0x01; /* I= OS_DIST_BASE */ + s->ioc_ranges[(0x398 - 0x300) / 8] =3D 0xffffff0000; /* IOS_DIS= T_MASK */ + s->ioc_ranges[(0x3a0 - 0x300) / 8] =3D 0x3400000000000000ULL; /* IOS_D= IST_ROUTE */ + s->ioc_ranges[(0x3c0 - 0x300) / 8] =3D 0xfffee00000; /* IOS_DIR= ECT_BASE */ + s->ioc_ranges[(0x3c8 - 0x300) / 8] =3D 0xffffff0000; /* IOS_DIR= ECT_MASK */ + s->ioc_ranges[(0x3d0 - 0x300) / 8] =3D 0x0; /* IOS_DIR= ECT_ROUTE */ + + s->tlb_ibase =3D 0; + s->tlb_imask =3D 0; + s->tlb_pcom =3D 0; + s->tlb_tcnfg =3D 0; + s->tlb_pdir_base =3D 0; + + for (i =3D 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 =3D ASTRO_CHIP(obj); + SysBusDevice *sbd =3D 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 =3D 0; i < ELROY_NUM; i++) { + static const int elroy_hpa_offsets[ELROY_NUM] =3D { + 0x30000, 0x32000, 0x38000, 0x3c000 }; + static const char elroy_rope_nr[ELROY_NUM] =3D { + 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 =3D elroy_hpa_offsets[i]; + rope =3D elroy_rope_nr[i]; + + elroy =3D elroy_init(i); + s->elroy[i] =3D elroy; + elroy->hpa =3D ASTRO_HPA + addr_offset; + elroy->pci_bus_num =3D i; + elroy->astro =3D 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] =3D 0xf0000001; + elroy->mmio_base[(0x0208 - 0x200) / 8] =3D 0xf8000000; + /* GMMIO */ + elroy->mmio_base[(0x0210 - 0x200) / 8] =3D 0x000000f800000001; + elroy->mmio_base[(0x0218 - 0x200) / 8] =3D 0x000000ff80000000; + /* WLMMIO */ + elroy->mmio_base[(0x0220 - 0x200) / 8] =3D 0xf0000001; + elroy->mmio_base[(0x0228 - 0x200) / 8] =3D 0xf0000000; + /* WGMMIO */ + elroy->mmio_base[(0x0230 - 0x200) / 8] =3D 0x000000f800000001; + elroy->mmio_base[(0x0238 - 0x200) / 8] =3D 0x000000fc00000000; + /* IOS_BASE */ + map_size =3D IOS_DIST_BASE_SIZE / ROPES_PER_IOC; + elroy->mmio_base[(0x0240 - 0x200) / 8] =3D rope * map_size | 0x01; + elroy->mmio_base[(0x0248 - 0x200) / 8] =3D 0x0000e000; + + /* map elroys mmio */ + map_size =3D LMMIO_DIST_BASE_SIZE / ROPES_PER_IOC; + map_addr =3D (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 =3D IOS_DIST_BASE_SIZE / ROPES_PER_IOC; + map_addr =3D (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_iom= mu, + elroy); + } +} + +static void astro_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->reset =3D astro_reset; + dc->vmsd =3D &vmstate_astro; + dc->realize =3D 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 =3D false; +} + +static const TypeInfo astro_chip_info =3D { + .name =3D TYPE_ASTRO_CHIP, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_init =3D astro_init, + .instance_size =3D sizeof(AstroState), + .class_init =3D astro_class_init, +}; + +static void astro_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc =3D IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate =3D astro_translate_iommu; +} + +static const TypeInfo astro_iommu_memory_region_info =3D { + .parent =3D TYPE_IOMMU_MEMORY_REGION, + .name =3D TYPE_ASTRO_IOMMU_MEMORY_REGION, + .class_init =3D 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/trace-events b/hw/pci-host/trace-events index 9d216bb89f..b2f47e6335 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 d= ata) "notif=3D@0x%"PRIx64" dino_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx6= 4" 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%"PRIx= 64" is %d" +astro_chip_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" s= ize %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 0= x%"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 0000000000..f63fd220f3 --- /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 --=20 2.41.0