PCIBase (Host Bridge config space BARs) and PCIBaseCfg registers in Bonito controls PCI DMA address translation.
For any incoming DMA requests, it will be matched against PCiBase{0, 1} together with PciBaseCfg.MASK{0,1}. If it hits any of both, higher bits of address will be replaced by PciBaseCfg.TRANSx. Emulating this behavior by PCI IOMMU DMA address space with dynamic remapping on register writes. Signed-off-by: Jiaxun Yang <jiaxun.y...@flygoat.com> --- hw/pci-host/bonito.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index f509f22df90ff7ed31ff5387a0acc239c22fd5f6..1c0d502a1e2dfa3c9803ca219cf505e08bf94a75 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -144,6 +144,17 @@ FIELD(PCIMAP, LO2, 12, 6) FIELD(PCIMAP, 2, 18, 1) #define BONITO_PCIMEMBASECFG (0x14 >> 2) /* 0x114 */ +REG32(PCIMEMBASECFG, 0x114) +FIELD(PCIMEMBASECFG, MASK0, 0, 5) +FIELD(PCIMEMBASECFG, TRANS0, 5, 5) +FIELD(PCIMEMBASECFG, CACHED0, 10, 1) +FIELD(PCIMEMBASECFG, IO0, 11, 1) +FIELD(PCIMEMBASECFG, MASK1, 12, 5) +FIELD(PCIMEMBASECFG, TRANS1, 17, 5) +FIELD(PCIMEMBASECFG, CACHED1, 22, 1) +FIELD(PCIMEMBASECFG, IO1, 23, 1) + + #define BONITO_PCIMAP_CFG (0x18 >> 2) /* 0x118 */ /* 5. ICU & GPIO regs */ @@ -258,9 +269,12 @@ struct BonitoState { PCIHostState parent_obj; qemu_irq *pic; PCIBonitoState *pci_dev; + MemoryRegion dma_mr; MemoryRegion pci_mem; + AddressSpace dma_as; MemoryRegion *pcimem_lo_alias; MemoryRegion *pcimem_hi_alias; + MemoryRegion *dma_alias; }; #define TYPE_PCI_BONITO "Bonito" @@ -314,6 +328,57 @@ static void bonito_update_pcimap(PCIBonitoState *s) FIELD_EX32(pcimap, PCIMAP, 2) << 31); } +static void pcibasecfg_decode(uint32_t mask, uint32_t trans, bool io, + uint32_t *base, uint32_t *size) +{ + uint32_t val; + + mask = (mask << 23 | 0xF0000000); + val = ctz32(mask); + *size = 1 << val; + *base = (trans & ~(*size - 1)) | io << 28; +} + +static void bonito_update_pcibase(PCIBonitoState *s) +{ + uint32_t pcibasecfg = s->regs[BONITO_PCIMEMBASECFG]; + uint32_t size, base; + uint32_t pcibase, wmask; + + pcibasecfg_decode(FIELD_EX32(pcibasecfg, PCIMEMBASECFG, MASK0), + FIELD_EX32(pcibasecfg, PCIMEMBASECFG, TRANS0), + FIELD_EX32(pcibasecfg, PCIMEMBASECFG, IO0), + &base, &size); + + wmask = ~(size - 1); + /* Mask will also influence PCIBase register writable range */ + pci_set_long(s->dev.wmask + PCI_BASE_ADDRESS_0, wmask); + /* Clear RO bits in PCIBase */ + pcibase = pci_get_long(s->dev.config + PCI_BASE_ADDRESS_0); + pcibase &= wmask; + pci_set_long(s->dev.config + PCI_BASE_ADDRESS_0, pcibase); + /* Adjust DMA spaces */ + memory_region_set_size(&s->pcihost->dma_alias[0], size); + memory_region_set_alias_offset(&s->pcihost->dma_alias[0], base); + memory_region_set_address(&s->pcihost->dma_alias[0], pcibase); + + /* Ditto for PCIMEMBASECFG1 */ + pcibasecfg_decode(FIELD_EX32(pcibasecfg, PCIMEMBASECFG, MASK1), + FIELD_EX32(pcibasecfg, PCIMEMBASECFG, TRANS1), + FIELD_EX32(pcibasecfg, PCIMEMBASECFG, IO1), + &base, &size); + + wmask = ~(size - 1); + pci_set_long(s->dev.wmask + PCI_BASE_ADDRESS_1, wmask); + pcibase = pci_get_long(s->dev.config + PCI_BASE_ADDRESS_1); + pcibase &= wmask; + pci_set_long(s->dev.config + PCI_BASE_ADDRESS_1, pcibase); + + memory_region_set_size(&s->pcihost->dma_alias[1], size); + memory_region_set_alias_offset(&s->pcihost->dma_alias[1], base); + memory_region_set_address(&s->pcihost->dma_alias[1], pcibase); +} + static void bonito_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -624,12 +689,35 @@ static const MemoryRegionOps bonito_spciconf_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void bonito_pci_write_config(PCIDevice *dev, uint32_t address, + uint32_t val, int len) +{ + pci_default_write_config(dev, address, val, len); + + if (ranges_overlap(address, len, PCI_BASE_ADDRESS_0, 12)) { + /* Bonito Host Bridge BARs are defined as DMA windows (pciBase) */ + bonito_update_pcibase(PCI_BONITO(dev)); + } +} + static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num) { /* Fuloong 2E PCI INTX are connected to Bonito GPIN[3:0] */ return ICU_PIN_GPINx(irq_num); } +static AddressSpace *bonito_pcihost_set_iommu(PCIBus *bus, void *opaque, + int devfn) +{ + BonitoState *bs = opaque; + + return &bs->dma_as; +} + +static const PCIIOMMUOps bonito_iommu_ops = { + .get_address_space = bonito_pcihost_set_iommu, +}; + static void bonito_reset_hold(Object *obj, ResetType type) { PCIBonitoState *s = PCI_BONITO(obj); @@ -653,6 +741,11 @@ static void bonito_reset_hold(Object *obj, ResetType type) s->regs[BONITO_DQCFG] = 0x8; s->regs[BONITO_MEMSIZE] = 0x10000000; s->regs[BONITO_PCIMAP] = 0x6140; + bonito_update_pcimap(s); + + pci_set_long(s->dev.config + PCI_BASE_ADDRESS_0, 0x80000000); + pci_set_long(s->dev.config + PCI_BASE_ADDRESS_1, 0x0); + bonito_update_pcibase(s); } static const VMStateDescription vmstate_bonito = { @@ -700,6 +793,7 @@ static void bonito_pci_realize(PCIDevice *dev, Error **errp) PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); BonitoState *bs = s->pcihost; MemoryRegion *pcimem_hi_alias = g_new(MemoryRegion, 1); + MemoryRegion *dma_alias = g_new(MemoryRegion, 2); /* * Bonito North Bridge, built on FPGA, @@ -764,6 +858,24 @@ static void bonito_pci_realize(PCIDevice *dev, Error **errp) (hwaddr)BONITO_PCIHI_BASE + BONITO_PCIHI_SIZE, 2 * GiB); + /* 32bit DMA */ + memory_region_init(&bs->dma_mr, OBJECT(s), "dma.pciBase", 4 * GiB); + + /* pciBase0, mapped to system RAM */ + memory_region_init_alias(&dma_alias[0], NULL, "pciBase0.mem.alias", + host_mem, 0x80000000, 256 * MiB); + memory_region_add_subregion_overlap(&bs->dma_mr, 0, &dma_alias[0], 2); + + /* pciBase1, mapped to system RAM */ + memory_region_init_alias(&dma_alias[1], NULL, "pciBase1.mem.alias", + host_mem, 0, 256 * MiB); + memory_region_add_subregion_overlap(&bs->dma_mr, 0, &dma_alias[1], 1); + + bs->dma_alias = dma_alias; + + address_space_init(&bs->dma_as, &bs->dma_mr, "pciBase.dma"); + pci_setup_iommu(phb->bus, &bonito_iommu_ops, bs); + /* set the default value of north bridge pci config */ pci_set_word(dev->config + PCI_COMMAND, 0x0000); pci_set_word(dev->config + PCI_STATUS, 0x0000); @@ -806,6 +918,7 @@ static void bonito_pci_class_init(ObjectClass *klass, const void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); + k->config_write = bonito_pci_write_config; rc->phases.hold = bonito_reset_hold; k->realize = bonito_pci_realize; k->vendor_id = 0xdf53; -- Git-154)