18cba80c3SFrank Blaschka /*
28cba80c3SFrank Blaschka * s390 PCI BUS
38cba80c3SFrank Blaschka *
48cba80c3SFrank Blaschka * Copyright 2014 IBM Corp.
58cba80c3SFrank Blaschka * Author(s): Frank Blaschka <frank.blaschka@de.ibm.com>
68cba80c3SFrank Blaschka * Hong Bo Li <lihbbj@cn.ibm.com>
78cba80c3SFrank Blaschka * Yi Min Zhao <zyimin@cn.ibm.com>
88cba80c3SFrank Blaschka *
98cba80c3SFrank Blaschka * This work is licensed under the terms of the GNU GPL, version 2 or (at
108cba80c3SFrank Blaschka * your option) any later version. See the COPYING file in the top-level
118cba80c3SFrank Blaschka * directory.
128cba80c3SFrank Blaschka */
138cba80c3SFrank Blaschka
149615495aSPeter Maydell #include "qemu/osdep.h"
153e5cfba3SYi Min Zhao #include "qapi/error.h"
163e5cfba3SYi Min Zhao #include "qapi/visitor.h"
17408b55dbSMatthew Rosato #include "hw/s390x/s390-pci-bus.h"
18408b55dbSMatthew Rosato #include "hw/s390x/s390-pci-inst.h"
19dd1d5fd9SMatthew Rosato #include "hw/s390x/s390-pci-kvm.h"
2037fa32deSMatthew Rosato #include "hw/s390x/s390-pci-vfio.h"
21a9c94277SMarkus Armbruster #include "hw/pci/pci_bus.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
233fc92a24SPierre Morel #include "hw/pci/pci_bridge.h"
24a9c94277SMarkus Armbruster #include "hw/pci/msi.h"
25a9c94277SMarkus Armbruster #include "qemu/error-report.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
2703451953SMatthew Rosato #include "sysemu/reset.h"
2803451953SMatthew Rosato #include "sysemu/runstate.h"
298cba80c3SFrank Blaschka
30f4a69168SCédric Le Goater #include "trace.h"
31229913f0SDanil Antonov
s390_get_phb(void)32a975a24aSYi Min Zhao S390pciState *s390_get_phb(void)
33e7d33695SYi Min Zhao {
34e7d33695SYi Min Zhao static S390pciState *phb;
35e7d33695SYi Min Zhao
36e7d33695SYi Min Zhao if (!phb) {
37e7d33695SYi Min Zhao phb = S390_PCI_HOST_BRIDGE(
38e7d33695SYi Min Zhao object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
39e7d33695SYi Min Zhao assert(phb != NULL);
40e7d33695SYi Min Zhao }
41e7d33695SYi Min Zhao
42e7d33695SYi Min Zhao return phb;
43e7d33695SYi Min Zhao }
44e7d33695SYi Min Zhao
pci_chsc_sei_nt2_get_event(void * res)451c5deaecSCornelia Huck int pci_chsc_sei_nt2_get_event(void *res)
468cba80c3SFrank Blaschka {
478cba80c3SFrank Blaschka ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res;
488cba80c3SFrank Blaschka PciCcdfAvail *accdf;
498cba80c3SFrank Blaschka PciCcdfErr *eccdf;
508cba80c3SFrank Blaschka int rc = 1;
518cba80c3SFrank Blaschka SeiContainer *sei_cont;
52e7d33695SYi Min Zhao S390pciState *s = s390_get_phb();
538cba80c3SFrank Blaschka
548cba80c3SFrank Blaschka sei_cont = QTAILQ_FIRST(&s->pending_sei);
558cba80c3SFrank Blaschka if (sei_cont) {
568cba80c3SFrank Blaschka QTAILQ_REMOVE(&s->pending_sei, sei_cont, link);
578cba80c3SFrank Blaschka nt2_res->nt = 2;
588cba80c3SFrank Blaschka nt2_res->cc = sei_cont->cc;
59d3321fc7SFrank Blaschka nt2_res->length = cpu_to_be16(sizeof(ChscSeiNt2Res));
608cba80c3SFrank Blaschka switch (sei_cont->cc) {
618cba80c3SFrank Blaschka case 1: /* error event */
628cba80c3SFrank Blaschka eccdf = (PciCcdfErr *)nt2_res->ccdf;
638cba80c3SFrank Blaschka eccdf->fid = cpu_to_be32(sei_cont->fid);
648cba80c3SFrank Blaschka eccdf->fh = cpu_to_be32(sei_cont->fh);
658cba80c3SFrank Blaschka eccdf->e = cpu_to_be32(sei_cont->e);
668cba80c3SFrank Blaschka eccdf->faddr = cpu_to_be64(sei_cont->faddr);
678cba80c3SFrank Blaschka eccdf->pec = cpu_to_be16(sei_cont->pec);
688cba80c3SFrank Blaschka break;
698cba80c3SFrank Blaschka case 2: /* availability event */
708cba80c3SFrank Blaschka accdf = (PciCcdfAvail *)nt2_res->ccdf;
718cba80c3SFrank Blaschka accdf->fid = cpu_to_be32(sei_cont->fid);
728cba80c3SFrank Blaschka accdf->fh = cpu_to_be32(sei_cont->fh);
738cba80c3SFrank Blaschka accdf->pec = cpu_to_be16(sei_cont->pec);
748cba80c3SFrank Blaschka break;
758cba80c3SFrank Blaschka default:
768cba80c3SFrank Blaschka abort();
778cba80c3SFrank Blaschka }
788cba80c3SFrank Blaschka g_free(sei_cont);
798cba80c3SFrank Blaschka rc = 0;
808cba80c3SFrank Blaschka }
818cba80c3SFrank Blaschka
828cba80c3SFrank Blaschka return rc;
838cba80c3SFrank Blaschka }
848cba80c3SFrank Blaschka
pci_chsc_sei_nt2_have_event(void)851c5deaecSCornelia Huck int pci_chsc_sei_nt2_have_event(void)
868cba80c3SFrank Blaschka {
87e7d33695SYi Min Zhao S390pciState *s = s390_get_phb();
888cba80c3SFrank Blaschka
898cba80c3SFrank Blaschka return !QTAILQ_EMPTY(&s->pending_sei);
908cba80c3SFrank Blaschka }
918cba80c3SFrank Blaschka
s390_pci_find_next_avail_dev(S390pciState * s,S390PCIBusDevice * pbdev)92a975a24aSYi Min Zhao S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s,
93a975a24aSYi Min Zhao S390PCIBusDevice *pbdev)
944e3bfc16SYi Min Zhao {
95e70377dfSPierre Morel S390PCIBusDevice *ret = pbdev ? QTAILQ_NEXT(pbdev, link) :
96e70377dfSPierre Morel QTAILQ_FIRST(&s->zpci_devs);
974e3bfc16SYi Min Zhao
98e70377dfSPierre Morel while (ret && ret->state == ZPCI_FS_RESERVED) {
99e70377dfSPierre Morel ret = QTAILQ_NEXT(ret, link);
1004e3bfc16SYi Min Zhao }
1014e3bfc16SYi Min Zhao
102e70377dfSPierre Morel return ret;
1034e3bfc16SYi Min Zhao }
1044e3bfc16SYi Min Zhao
s390_pci_find_dev_by_fid(S390pciState * s,uint32_t fid)105a975a24aSYi Min Zhao S390PCIBusDevice *s390_pci_find_dev_by_fid(S390pciState *s, uint32_t fid)
1068cba80c3SFrank Blaschka {
1078cba80c3SFrank Blaschka S390PCIBusDevice *pbdev;
1088cba80c3SFrank Blaschka
109e70377dfSPierre Morel QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
110e70377dfSPierre Morel if (pbdev->fid == fid) {
1118cba80c3SFrank Blaschka return pbdev;
1128cba80c3SFrank Blaschka }
1138cba80c3SFrank Blaschka }
1148cba80c3SFrank Blaschka
1158cba80c3SFrank Blaschka return NULL;
1168cba80c3SFrank Blaschka }
1178cba80c3SFrank Blaschka
s390_pci_sclp_configure(SCCB * sccb)1188f5cb693SYi Min Zhao void s390_pci_sclp_configure(SCCB *sccb)
1198cba80c3SFrank Blaschka {
12080b7a265SCornelia Huck IoaCfgSccb *psccb = (IoaCfgSccb *)sccb;
121a975a24aSYi Min Zhao S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(s390_get_phb(),
122a975a24aSYi Min Zhao be32_to_cpu(psccb->aid));
1238cba80c3SFrank Blaschka uint16_t rc;
1248cba80c3SFrank Blaschka
1255d1abf23SYi Min Zhao if (!pbdev) {
126f4a69168SCédric Le Goater trace_s390_pci_sclp_nodev("configure", be32_to_cpu(psccb->aid));
1278f5cb693SYi Min Zhao rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
1285d1abf23SYi Min Zhao goto out;
1295d1abf23SYi Min Zhao }
1305d1abf23SYi Min Zhao
1315d1abf23SYi Min Zhao switch (pbdev->state) {
1325d1abf23SYi Min Zhao case ZPCI_FS_RESERVED:
1335d1abf23SYi Min Zhao rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE;
1345d1abf23SYi Min Zhao break;
1355d1abf23SYi Min Zhao case ZPCI_FS_STANDBY:
1365d1abf23SYi Min Zhao pbdev->state = ZPCI_FS_DISABLED;
1375d1abf23SYi Min Zhao rc = SCLP_RC_NORMAL_COMPLETION;
1385d1abf23SYi Min Zhao break;
1395d1abf23SYi Min Zhao default:
1405d1abf23SYi Min Zhao rc = SCLP_RC_NO_ACTION_REQUIRED;
1418f5cb693SYi Min Zhao }
1423b40ea29SYi Min Zhao out:
1438f5cb693SYi Min Zhao psccb->header.response_code = cpu_to_be16(rc);
1448f5cb693SYi Min Zhao }
1458f5cb693SYi Min Zhao
s390_pci_shutdown_notifier(Notifier * n,void * opaque)14603451953SMatthew Rosato static void s390_pci_shutdown_notifier(Notifier *n, void *opaque)
14703451953SMatthew Rosato {
14803451953SMatthew Rosato S390PCIBusDevice *pbdev = container_of(n, S390PCIBusDevice,
14903451953SMatthew Rosato shutdown_notifier);
15003451953SMatthew Rosato
15103451953SMatthew Rosato pci_device_reset(pbdev->pdev);
15203451953SMatthew Rosato }
15303451953SMatthew Rosato
s390_pci_perform_unplug(S390PCIBusDevice * pbdev)154e0998fe8SDavid Hildenbrand static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev)
155e0998fe8SDavid Hildenbrand {
156e0998fe8SDavid Hildenbrand HotplugHandler *hotplug_ctrl;
157e0998fe8SDavid Hildenbrand
15803451953SMatthew Rosato if (pbdev->pft == ZPCI_PFT_ISM) {
15903451953SMatthew Rosato notifier_remove(&pbdev->shutdown_notifier);
16003451953SMatthew Rosato }
16103451953SMatthew Rosato
162e0998fe8SDavid Hildenbrand /* Unplug the PCI device */
163e0998fe8SDavid Hildenbrand if (pbdev->pdev) {
16407578b0aSDavid Hildenbrand DeviceState *pdev = DEVICE(pbdev->pdev);
16507578b0aSDavid Hildenbrand
16607578b0aSDavid Hildenbrand hotplug_ctrl = qdev_get_hotplug_handler(pdev);
16707578b0aSDavid Hildenbrand hotplug_handler_unplug(hotplug_ctrl, pdev, &error_abort);
16807578b0aSDavid Hildenbrand object_unparent(OBJECT(pdev));
169e0998fe8SDavid Hildenbrand }
170e0998fe8SDavid Hildenbrand
171e0998fe8SDavid Hildenbrand /* Unplug the zPCI device */
172e0998fe8SDavid Hildenbrand hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev));
173e0998fe8SDavid Hildenbrand hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev), &error_abort);
17407578b0aSDavid Hildenbrand object_unparent(OBJECT(pbdev));
175e0998fe8SDavid Hildenbrand }
176e0998fe8SDavid Hildenbrand
s390_pci_sclp_deconfigure(SCCB * sccb)1778f5cb693SYi Min Zhao void s390_pci_sclp_deconfigure(SCCB *sccb)
1788f5cb693SYi Min Zhao {
17980b7a265SCornelia Huck IoaCfgSccb *psccb = (IoaCfgSccb *)sccb;
180a975a24aSYi Min Zhao S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(s390_get_phb(),
181a975a24aSYi Min Zhao be32_to_cpu(psccb->aid));
1828f5cb693SYi Min Zhao uint16_t rc;
1838f5cb693SYi Min Zhao
1845d1abf23SYi Min Zhao if (!pbdev) {
185f4a69168SCédric Le Goater trace_s390_pci_sclp_nodev("deconfigure", be32_to_cpu(psccb->aid));
1865d1abf23SYi Min Zhao rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
1875d1abf23SYi Min Zhao goto out;
1885d1abf23SYi Min Zhao }
1895d1abf23SYi Min Zhao
1905d1abf23SYi Min Zhao switch (pbdev->state) {
1915d1abf23SYi Min Zhao case ZPCI_FS_RESERVED:
1925d1abf23SYi Min Zhao rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE;
1935d1abf23SYi Min Zhao break;
1945d1abf23SYi Min Zhao case ZPCI_FS_STANDBY:
1958f5cb693SYi Min Zhao rc = SCLP_RC_NO_ACTION_REQUIRED;
1965d1abf23SYi Min Zhao break;
1975d1abf23SYi Min Zhao default:
198d0bc7091SMatthew Rosato if (pbdev->interp && (pbdev->fh & FH_MASK_ENABLE)) {
199d0bc7091SMatthew Rosato /* Interpreted devices were using interrupt forwarding */
200d0bc7091SMatthew Rosato s390_pci_kvm_aif_disable(pbdev);
201d0bc7091SMatthew Rosato } else if (pbdev->summary_ind) {
202259a4f0aSYi Min Zhao pci_dereg_irqs(pbdev);
203259a4f0aSYi Min Zhao }
204de91ea92SYi Min Zhao if (pbdev->iommu->enabled) {
205de91ea92SYi Min Zhao pci_dereg_ioat(pbdev->iommu);
206259a4f0aSYi Min Zhao }
2075d1abf23SYi Min Zhao pbdev->state = ZPCI_FS_STANDBY;
2088f5cb693SYi Min Zhao rc = SCLP_RC_NORMAL_COMPLETION;
20993d16d81SYi Min Zhao
2109f2a46b1SDavid Hildenbrand if (pbdev->unplug_requested) {
211e0998fe8SDavid Hildenbrand s390_pci_perform_unplug(pbdev);
21293d16d81SYi Min Zhao }
2138f5cb693SYi Min Zhao }
2143b40ea29SYi Min Zhao out:
2158cba80c3SFrank Blaschka psccb->header.response_code = cpu_to_be16(rc);
2168cba80c3SFrank Blaschka }
2178cba80c3SFrank Blaschka
s390_pci_find_dev_by_uid(S390pciState * s,uint16_t uid)218a975a24aSYi Min Zhao static S390PCIBusDevice *s390_pci_find_dev_by_uid(S390pciState *s, uint16_t uid)
2193e5cfba3SYi Min Zhao {
2203e5cfba3SYi Min Zhao S390PCIBusDevice *pbdev;
2213e5cfba3SYi Min Zhao
222e70377dfSPierre Morel QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
2233e5cfba3SYi Min Zhao if (pbdev->uid == uid) {
2243e5cfba3SYi Min Zhao return pbdev;
2253e5cfba3SYi Min Zhao }
2263e5cfba3SYi Min Zhao }
2273e5cfba3SYi Min Zhao
2283e5cfba3SYi Min Zhao return NULL;
2293e5cfba3SYi Min Zhao }
2303e5cfba3SYi Min Zhao
s390_pci_find_dev_by_target(S390pciState * s,const char * target)231ceb7054fSYi Min Zhao S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s,
232a975a24aSYi Min Zhao const char *target)
2333e5cfba3SYi Min Zhao {
2343e5cfba3SYi Min Zhao S390PCIBusDevice *pbdev;
2353e5cfba3SYi Min Zhao
2363e5cfba3SYi Min Zhao if (!target) {
2373e5cfba3SYi Min Zhao return NULL;
2383e5cfba3SYi Min Zhao }
2393e5cfba3SYi Min Zhao
240e70377dfSPierre Morel QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
2413e5cfba3SYi Min Zhao if (!strcmp(pbdev->target, target)) {
2423e5cfba3SYi Min Zhao return pbdev;
2433e5cfba3SYi Min Zhao }
2443e5cfba3SYi Min Zhao }
2453e5cfba3SYi Min Zhao
2463e5cfba3SYi Min Zhao return NULL;
2473e5cfba3SYi Min Zhao }
2483e5cfba3SYi Min Zhao
s390_pci_find_dev_by_pci(S390pciState * s,PCIDevice * pci_dev)249e0998fe8SDavid Hildenbrand static S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s,
250e0998fe8SDavid Hildenbrand PCIDevice *pci_dev)
251e0998fe8SDavid Hildenbrand {
252e0998fe8SDavid Hildenbrand S390PCIBusDevice *pbdev;
253e0998fe8SDavid Hildenbrand
254e0998fe8SDavid Hildenbrand if (!pci_dev) {
255e0998fe8SDavid Hildenbrand return NULL;
256e0998fe8SDavid Hildenbrand }
257e0998fe8SDavid Hildenbrand
258e0998fe8SDavid Hildenbrand QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
259e0998fe8SDavid Hildenbrand if (pbdev->pdev == pci_dev) {
260e0998fe8SDavid Hildenbrand return pbdev;
261e0998fe8SDavid Hildenbrand }
262e0998fe8SDavid Hildenbrand }
263e0998fe8SDavid Hildenbrand
264e0998fe8SDavid Hildenbrand return NULL;
265e0998fe8SDavid Hildenbrand }
266e0998fe8SDavid Hildenbrand
s390_pci_find_dev_by_idx(S390pciState * s,uint32_t idx)267a975a24aSYi Min Zhao S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx)
2688cba80c3SFrank Blaschka {
269df8dd91bSYi Min Zhao return g_hash_table_lookup(s->zpci_table, &idx);
2708cba80c3SFrank Blaschka }
2718cba80c3SFrank Blaschka
s390_pci_find_dev_by_fh(S390pciState * s,uint32_t fh)272a975a24aSYi Min Zhao S390PCIBusDevice *s390_pci_find_dev_by_fh(S390pciState *s, uint32_t fh)
2738cba80c3SFrank Blaschka {
274df8dd91bSYi Min Zhao uint32_t idx = FH_MASK_INDEX & fh;
275df8dd91bSYi Min Zhao S390PCIBusDevice *pbdev = s390_pci_find_dev_by_idx(s, idx);
2768cba80c3SFrank Blaschka
277df8dd91bSYi Min Zhao if (pbdev && pbdev->fh == fh) {
2788cba80c3SFrank Blaschka return pbdev;
2798cba80c3SFrank Blaschka }
2808cba80c3SFrank Blaschka
2818cba80c3SFrank Blaschka return NULL;
2828cba80c3SFrank Blaschka }
2838cba80c3SFrank Blaschka
s390_pci_generate_event(uint8_t cc,uint16_t pec,uint32_t fh,uint32_t fid,uint64_t faddr,uint32_t e)2848cba80c3SFrank Blaschka static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh,
2858cba80c3SFrank Blaschka uint32_t fid, uint64_t faddr, uint32_t e)
2868cba80c3SFrank Blaschka {
287b7022d9aSMarkus Armbruster SeiContainer *sei_cont;
288e7d33695SYi Min Zhao S390pciState *s = s390_get_phb();
2898cba80c3SFrank Blaschka
29096f64aa8SMarc-André Lureau sei_cont = g_new0(SeiContainer, 1);
2918cba80c3SFrank Blaschka sei_cont->fh = fh;
2928cba80c3SFrank Blaschka sei_cont->fid = fid;
2938cba80c3SFrank Blaschka sei_cont->cc = cc;
2948cba80c3SFrank Blaschka sei_cont->pec = pec;
2958cba80c3SFrank Blaschka sei_cont->faddr = faddr;
2968cba80c3SFrank Blaschka sei_cont->e = e;
2978cba80c3SFrank Blaschka
2988cba80c3SFrank Blaschka QTAILQ_INSERT_TAIL(&s->pending_sei, sei_cont, link);
2998cba80c3SFrank Blaschka css_generate_css_crws(0);
3008cba80c3SFrank Blaschka }
3018cba80c3SFrank Blaschka
s390_pci_generate_plug_event(uint16_t pec,uint32_t fh,uint32_t fid)3028cba80c3SFrank Blaschka static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
3038cba80c3SFrank Blaschka uint32_t fid)
3048cba80c3SFrank Blaschka {
3058cba80c3SFrank Blaschka s390_pci_generate_event(2, pec, fh, fid, 0, 0);
3068cba80c3SFrank Blaschka }
3078cba80c3SFrank Blaschka
s390_pci_generate_error_event(uint16_t pec,uint32_t fh,uint32_t fid,uint64_t faddr,uint32_t e)3085d1abf23SYi Min Zhao void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
3095d1abf23SYi Min Zhao uint64_t faddr, uint32_t e)
3108cba80c3SFrank Blaschka {
3118cba80c3SFrank Blaschka s390_pci_generate_event(1, pec, fh, fid, faddr, e);
3128cba80c3SFrank Blaschka }
3138cba80c3SFrank Blaschka
s390_pci_set_irq(void * opaque,int irq,int level)3148cba80c3SFrank Blaschka static void s390_pci_set_irq(void *opaque, int irq, int level)
3158cba80c3SFrank Blaschka {
3168cba80c3SFrank Blaschka /* nothing to do */
3178cba80c3SFrank Blaschka }
3188cba80c3SFrank Blaschka
s390_pci_map_irq(PCIDevice * pci_dev,int irq_num)3198cba80c3SFrank Blaschka static int s390_pci_map_irq(PCIDevice *pci_dev, int irq_num)
3208cba80c3SFrank Blaschka {
3218cba80c3SFrank Blaschka /* nothing to do */
3228cba80c3SFrank Blaschka return 0;
3238cba80c3SFrank Blaschka }
3248cba80c3SFrank Blaschka
s390_pci_get_table_origin(uint64_t iota)3258cba80c3SFrank Blaschka static uint64_t s390_pci_get_table_origin(uint64_t iota)
3268cba80c3SFrank Blaschka {
3278cba80c3SFrank Blaschka return iota & ~ZPCI_IOTA_RTTO_FLAG;
3288cba80c3SFrank Blaschka }
3298cba80c3SFrank Blaschka
calc_rtx(dma_addr_t ptr)3308cba80c3SFrank Blaschka static unsigned int calc_rtx(dma_addr_t ptr)
3318cba80c3SFrank Blaschka {
3328cba80c3SFrank Blaschka return ((unsigned long) ptr >> ZPCI_RT_SHIFT) & ZPCI_INDEX_MASK;
3338cba80c3SFrank Blaschka }
3348cba80c3SFrank Blaschka
calc_sx(dma_addr_t ptr)3358cba80c3SFrank Blaschka static unsigned int calc_sx(dma_addr_t ptr)
3368cba80c3SFrank Blaschka {
3378cba80c3SFrank Blaschka return ((unsigned long) ptr >> ZPCI_ST_SHIFT) & ZPCI_INDEX_MASK;
3388cba80c3SFrank Blaschka }
3398cba80c3SFrank Blaschka
calc_px(dma_addr_t ptr)3408cba80c3SFrank Blaschka static unsigned int calc_px(dma_addr_t ptr)
3418cba80c3SFrank Blaschka {
342ed3288ffSThomas Huth return ((unsigned long) ptr >> TARGET_PAGE_BITS) & ZPCI_PT_MASK;
3438cba80c3SFrank Blaschka }
3448cba80c3SFrank Blaschka
get_rt_sto(uint64_t entry)3458cba80c3SFrank Blaschka static uint64_t get_rt_sto(uint64_t entry)
3468cba80c3SFrank Blaschka {
3478cba80c3SFrank Blaschka return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_RTX)
3488cba80c3SFrank Blaschka ? (entry & ZPCI_RTE_ADDR_MASK)
3498cba80c3SFrank Blaschka : 0;
3508cba80c3SFrank Blaschka }
3518cba80c3SFrank Blaschka
get_st_pto(uint64_t entry)3528cba80c3SFrank Blaschka static uint64_t get_st_pto(uint64_t entry)
3538cba80c3SFrank Blaschka {
3548cba80c3SFrank Blaschka return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_SX)
3558cba80c3SFrank Blaschka ? (entry & ZPCI_STE_ADDR_MASK)
3568cba80c3SFrank Blaschka : 0;
3578cba80c3SFrank Blaschka }
3588cba80c3SFrank Blaschka
rt_entry_isvalid(uint64_t entry)3590125861eSYi Min Zhao static bool rt_entry_isvalid(uint64_t entry)
3608cba80c3SFrank Blaschka {
3610125861eSYi Min Zhao return (entry & ZPCI_TABLE_VALID_MASK) == ZPCI_TABLE_VALID;
3620125861eSYi Min Zhao }
3638cba80c3SFrank Blaschka
pt_entry_isvalid(uint64_t entry)3640125861eSYi Min Zhao static bool pt_entry_isvalid(uint64_t entry)
3650125861eSYi Min Zhao {
3660125861eSYi Min Zhao return (entry & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_VALID;
3670125861eSYi Min Zhao }
3688cba80c3SFrank Blaschka
entry_isprotected(uint64_t entry)3690125861eSYi Min Zhao static bool entry_isprotected(uint64_t entry)
3700125861eSYi Min Zhao {
3710125861eSYi Min Zhao return (entry & ZPCI_TABLE_PROT_MASK) == ZPCI_TABLE_PROTECTED;
3720125861eSYi Min Zhao }
3730125861eSYi Min Zhao
3740125861eSYi Min Zhao /* ett is expected table type, -1 page table, 0 segment table, 1 region table */
get_table_index(uint64_t iova,int8_t ett)3750125861eSYi Min Zhao static uint64_t get_table_index(uint64_t iova, int8_t ett)
3760125861eSYi Min Zhao {
3770125861eSYi Min Zhao switch (ett) {
3780125861eSYi Min Zhao case ZPCI_ETT_PT:
3790125861eSYi Min Zhao return calc_px(iova);
3800125861eSYi Min Zhao case ZPCI_ETT_ST:
3810125861eSYi Min Zhao return calc_sx(iova);
3820125861eSYi Min Zhao case ZPCI_ETT_RT:
3830125861eSYi Min Zhao return calc_rtx(iova);
3840125861eSYi Min Zhao }
3850125861eSYi Min Zhao
3860125861eSYi Min Zhao return -1;
3870125861eSYi Min Zhao }
3880125861eSYi Min Zhao
entry_isvalid(uint64_t entry,int8_t ett)3890125861eSYi Min Zhao static bool entry_isvalid(uint64_t entry, int8_t ett)
3900125861eSYi Min Zhao {
3910125861eSYi Min Zhao switch (ett) {
3920125861eSYi Min Zhao case ZPCI_ETT_PT:
3930125861eSYi Min Zhao return pt_entry_isvalid(entry);
3940125861eSYi Min Zhao case ZPCI_ETT_ST:
3950125861eSYi Min Zhao case ZPCI_ETT_RT:
3960125861eSYi Min Zhao return rt_entry_isvalid(entry);
3970125861eSYi Min Zhao }
3980125861eSYi Min Zhao
3990125861eSYi Min Zhao return false;
4000125861eSYi Min Zhao }
4010125861eSYi Min Zhao
4020125861eSYi Min Zhao /* Return true if address translation is done */
translate_iscomplete(uint64_t entry,int8_t ett)4030125861eSYi Min Zhao static bool translate_iscomplete(uint64_t entry, int8_t ett)
4040125861eSYi Min Zhao {
4050125861eSYi Min Zhao switch (ett) {
4060125861eSYi Min Zhao case 0:
4070125861eSYi Min Zhao return (entry & ZPCI_TABLE_FC) ? true : false;
4080125861eSYi Min Zhao case 1:
4090125861eSYi Min Zhao return false;
4100125861eSYi Min Zhao }
4110125861eSYi Min Zhao
4120125861eSYi Min Zhao return true;
4130125861eSYi Min Zhao }
4140125861eSYi Min Zhao
get_frame_size(int8_t ett)4150125861eSYi Min Zhao static uint64_t get_frame_size(int8_t ett)
4160125861eSYi Min Zhao {
4170125861eSYi Min Zhao switch (ett) {
4180125861eSYi Min Zhao case ZPCI_ETT_PT:
4190125861eSYi Min Zhao return 1ULL << 12;
4200125861eSYi Min Zhao case ZPCI_ETT_ST:
4210125861eSYi Min Zhao return 1ULL << 20;
4220125861eSYi Min Zhao case ZPCI_ETT_RT:
4230125861eSYi Min Zhao return 1ULL << 31;
4240125861eSYi Min Zhao }
4250125861eSYi Min Zhao
4260125861eSYi Min Zhao return 0;
4270125861eSYi Min Zhao }
4280125861eSYi Min Zhao
get_next_table_origin(uint64_t entry,int8_t ett)4290125861eSYi Min Zhao static uint64_t get_next_table_origin(uint64_t entry, int8_t ett)
4300125861eSYi Min Zhao {
4310125861eSYi Min Zhao switch (ett) {
4320125861eSYi Min Zhao case ZPCI_ETT_PT:
4330125861eSYi Min Zhao return entry & ZPCI_PTE_ADDR_MASK;
4340125861eSYi Min Zhao case ZPCI_ETT_ST:
4350125861eSYi Min Zhao return get_st_pto(entry);
4360125861eSYi Min Zhao case ZPCI_ETT_RT:
4370125861eSYi Min Zhao return get_rt_sto(entry);
4380125861eSYi Min Zhao }
4390125861eSYi Min Zhao
4400125861eSYi Min Zhao return 0;
4410125861eSYi Min Zhao }
4420125861eSYi Min Zhao
4430125861eSYi Min Zhao /**
4440125861eSYi Min Zhao * table_translate: do translation within one table and return the following
4450125861eSYi Min Zhao * table origin
4460125861eSYi Min Zhao *
4470125861eSYi Min Zhao * @entry: the entry being translated, the result is stored in this.
4480125861eSYi Min Zhao * @to: the address of table origin.
4490125861eSYi Min Zhao * @ett: expected table type, 1 region table, 0 segment table and -1 page table.
4500125861eSYi Min Zhao * @error: error code
4510125861eSYi Min Zhao */
table_translate(S390IOTLBEntry * entry,uint64_t to,int8_t ett,uint16_t * error)4520125861eSYi Min Zhao static uint64_t table_translate(S390IOTLBEntry *entry, uint64_t to, int8_t ett,
4530125861eSYi Min Zhao uint16_t *error)
4540125861eSYi Min Zhao {
4550125861eSYi Min Zhao uint64_t tx, te, nto = 0;
4560125861eSYi Min Zhao uint16_t err = 0;
4570125861eSYi Min Zhao
4580125861eSYi Min Zhao tx = get_table_index(entry->iova, ett);
4590125861eSYi Min Zhao te = address_space_ldq(&address_space_memory, to + tx * sizeof(uint64_t),
46042874d3aSPeter Maydell MEMTXATTRS_UNSPECIFIED, NULL);
4610125861eSYi Min Zhao
4620125861eSYi Min Zhao if (!te) {
4630125861eSYi Min Zhao err = ERR_EVENT_INVALTE;
4648cba80c3SFrank Blaschka goto out;
4658cba80c3SFrank Blaschka }
4668cba80c3SFrank Blaschka
4670125861eSYi Min Zhao if (!entry_isvalid(te, ett)) {
4680125861eSYi Min Zhao entry->perm &= IOMMU_NONE;
4698cba80c3SFrank Blaschka goto out;
4708cba80c3SFrank Blaschka }
4718cba80c3SFrank Blaschka
4720125861eSYi Min Zhao if (ett == ZPCI_ETT_RT && ((te & ZPCI_TABLE_LEN_RTX) != ZPCI_TABLE_LEN_RTX
4730125861eSYi Min Zhao || te & ZPCI_TABLE_OFFSET_MASK)) {
4740125861eSYi Min Zhao err = ERR_EVENT_INVALTL;
4750125861eSYi Min Zhao goto out;
4760125861eSYi Min Zhao }
4778cba80c3SFrank Blaschka
4780125861eSYi Min Zhao nto = get_next_table_origin(te, ett);
4790125861eSYi Min Zhao if (!nto) {
4800125861eSYi Min Zhao err = ERR_EVENT_TT;
4810125861eSYi Min Zhao goto out;
4820125861eSYi Min Zhao }
4830125861eSYi Min Zhao
4840125861eSYi Min Zhao if (entry_isprotected(te)) {
4850125861eSYi Min Zhao entry->perm &= IOMMU_RO;
4860125861eSYi Min Zhao } else {
4870125861eSYi Min Zhao entry->perm &= IOMMU_RW;
4880125861eSYi Min Zhao }
4890125861eSYi Min Zhao
4900125861eSYi Min Zhao if (translate_iscomplete(te, ett)) {
4910125861eSYi Min Zhao switch (ett) {
4920125861eSYi Min Zhao case ZPCI_ETT_PT:
4930125861eSYi Min Zhao entry->translated_addr = te & ZPCI_PTE_ADDR_MASK;
4940125861eSYi Min Zhao break;
4950125861eSYi Min Zhao case ZPCI_ETT_ST:
4960125861eSYi Min Zhao entry->translated_addr = (te & ZPCI_SFAA_MASK) |
4970125861eSYi Min Zhao (entry->iova & ~ZPCI_SFAA_MASK);
4980125861eSYi Min Zhao break;
4990125861eSYi Min Zhao }
5000125861eSYi Min Zhao nto = 0;
5010125861eSYi Min Zhao }
5028cba80c3SFrank Blaschka out:
5030125861eSYi Min Zhao if (err) {
5040125861eSYi Min Zhao entry->perm = IOMMU_NONE;
5050125861eSYi Min Zhao *error = err;
5060125861eSYi Min Zhao }
5070125861eSYi Min Zhao entry->len = get_frame_size(ett);
5080125861eSYi Min Zhao return nto;
5090125861eSYi Min Zhao }
5100125861eSYi Min Zhao
s390_guest_io_table_walk(uint64_t g_iota,hwaddr addr,S390IOTLBEntry * entry)5110125861eSYi Min Zhao uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr,
5120125861eSYi Min Zhao S390IOTLBEntry *entry)
5130125861eSYi Min Zhao {
5140125861eSYi Min Zhao uint64_t to = s390_pci_get_table_origin(g_iota);
5150125861eSYi Min Zhao int8_t ett = 1;
5160125861eSYi Min Zhao uint16_t error = 0;
5170125861eSYi Min Zhao
518ed3288ffSThomas Huth entry->iova = addr & TARGET_PAGE_MASK;
5190125861eSYi Min Zhao entry->translated_addr = 0;
5200125861eSYi Min Zhao entry->perm = IOMMU_RW;
5210125861eSYi Min Zhao
5220125861eSYi Min Zhao if (entry_isprotected(g_iota)) {
5230125861eSYi Min Zhao entry->perm &= IOMMU_RO;
5240125861eSYi Min Zhao }
5250125861eSYi Min Zhao
5260125861eSYi Min Zhao while (to) {
5270125861eSYi Min Zhao to = table_translate(entry, to, ett--, &error);
5280125861eSYi Min Zhao }
5290125861eSYi Min Zhao
5300125861eSYi Min Zhao return error;
5318cba80c3SFrank Blaschka }
5328cba80c3SFrank Blaschka
s390_translate_iommu(IOMMUMemoryRegion * mr,hwaddr addr,IOMMUAccessFlags flag,int iommu_idx)5333df9d748SAlexey Kardashevskiy static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
5342c91bcf2SPeter Maydell IOMMUAccessFlags flag, int iommu_idx)
5358cba80c3SFrank Blaschka {
536de91ea92SYi Min Zhao S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr);
537b3f05d8cSYi Min Zhao S390IOTLBEntry *entry;
538ed3288ffSThomas Huth uint64_t iova = addr & TARGET_PAGE_MASK;
5390125861eSYi Min Zhao uint16_t error = 0;
5408cba80c3SFrank Blaschka IOMMUTLBEntry ret = {
5418cba80c3SFrank Blaschka .target_as = &address_space_memory,
5428cba80c3SFrank Blaschka .iova = 0,
5438cba80c3SFrank Blaschka .translated_addr = 0,
5448cba80c3SFrank Blaschka .addr_mask = ~(hwaddr)0,
5458cba80c3SFrank Blaschka .perm = IOMMU_NONE,
5468cba80c3SFrank Blaschka };
5478cba80c3SFrank Blaschka
548de91ea92SYi Min Zhao switch (iommu->pbdev->state) {
5495d1abf23SYi Min Zhao case ZPCI_FS_ENABLED:
5505d1abf23SYi Min Zhao case ZPCI_FS_BLOCKED:
551de91ea92SYi Min Zhao if (!iommu->enabled) {
5525d1abf23SYi Min Zhao return ret;
5535d1abf23SYi Min Zhao }
5545d1abf23SYi Min Zhao break;
5555d1abf23SYi Min Zhao default:
556dce1b089SYi Min Zhao return ret;
557dce1b089SYi Min Zhao }
558dce1b089SYi Min Zhao
559f4a69168SCédric Le Goater trace_s390_pci_iommu_xlate(addr);
5608cba80c3SFrank Blaschka
561de91ea92SYi Min Zhao if (addr < iommu->pba || addr > iommu->pal) {
5620125861eSYi Min Zhao error = ERR_EVENT_OORANGE;
5630125861eSYi Min Zhao goto err;
5648cba80c3SFrank Blaschka }
5658cba80c3SFrank Blaschka
566b3f05d8cSYi Min Zhao entry = g_hash_table_lookup(iommu->iotlb, &iova);
567b3f05d8cSYi Min Zhao if (entry) {
568b3f05d8cSYi Min Zhao ret.iova = entry->iova;
569b3f05d8cSYi Min Zhao ret.translated_addr = entry->translated_addr;
570b3f05d8cSYi Min Zhao ret.addr_mask = entry->len - 1;
571b3f05d8cSYi Min Zhao ret.perm = entry->perm;
572b3f05d8cSYi Min Zhao } else {
573b3f05d8cSYi Min Zhao ret.iova = iova;
574ed3288ffSThomas Huth ret.addr_mask = ~TARGET_PAGE_MASK;
575b3f05d8cSYi Min Zhao ret.perm = IOMMU_NONE;
576b3f05d8cSYi Min Zhao }
5770125861eSYi Min Zhao
5780125861eSYi Min Zhao if (flag != IOMMU_NONE && !(flag & ret.perm)) {
5790125861eSYi Min Zhao error = ERR_EVENT_TPROTE;
5808cba80c3SFrank Blaschka }
5810125861eSYi Min Zhao err:
5820125861eSYi Min Zhao if (error) {
5830125861eSYi Min Zhao iommu->pbdev->state = ZPCI_FS_ERROR;
5840125861eSYi Min Zhao s390_pci_generate_error_event(error, iommu->pbdev->fh,
5850125861eSYi Min Zhao iommu->pbdev->fid, addr, 0);
5868cba80c3SFrank Blaschka }
5878cba80c3SFrank Blaschka return ret;
5888cba80c3SFrank Blaschka }
5898cba80c3SFrank Blaschka
s390_pci_iommu_replay(IOMMUMemoryRegion * iommu,IOMMUNotifier * notifier)5906c5e7402SYi Min Zhao static void s390_pci_iommu_replay(IOMMUMemoryRegion *iommu,
5916c5e7402SYi Min Zhao IOMMUNotifier *notifier)
5926c5e7402SYi Min Zhao {
5936c5e7402SYi Min Zhao /* It's impossible to plug a pci device on s390x that already has iommu
5946c5e7402SYi Min Zhao * mappings which need to be replayed, that is due to the "one iommu per
5956c5e7402SYi Min Zhao * zpci device" construct. But when we support migration of vfio-pci
5966c5e7402SYi Min Zhao * devices in future, we need to revisit this.
5976c5e7402SYi Min Zhao */
5986c5e7402SYi Min Zhao return;
5996c5e7402SYi Min Zhao }
6006c5e7402SYi Min Zhao
s390_pci_get_iommu(S390pciState * s,PCIBus * bus,int devfn)60103805be0SYi Min Zhao static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus,
60203805be0SYi Min Zhao int devfn)
60303805be0SYi Min Zhao {
60403805be0SYi Min Zhao uint64_t key = (uintptr_t)bus;
60503805be0SYi Min Zhao S390PCIIOMMUTable *table = g_hash_table_lookup(s->iommu_table, &key);
60603805be0SYi Min Zhao S390PCIIOMMU *iommu;
60703805be0SYi Min Zhao
60803805be0SYi Min Zhao if (!table) {
60996f64aa8SMarc-André Lureau table = g_new0(S390PCIIOMMUTable, 1);
61003805be0SYi Min Zhao table->key = key;
61103805be0SYi Min Zhao g_hash_table_insert(s->iommu_table, &table->key, table);
61203805be0SYi Min Zhao }
61303805be0SYi Min Zhao
61403805be0SYi Min Zhao iommu = table->iommu[PCI_SLOT(devfn)];
61503805be0SYi Min Zhao if (!iommu) {
61603805be0SYi Min Zhao iommu = S390_PCI_IOMMU(object_new(TYPE_S390_PCI_IOMMU));
61703805be0SYi Min Zhao
61803805be0SYi Min Zhao char *mr_name = g_strdup_printf("iommu-root-%02x:%02x.%01x",
61903805be0SYi Min Zhao pci_bus_num(bus),
62003805be0SYi Min Zhao PCI_SLOT(devfn),
62103805be0SYi Min Zhao PCI_FUNC(devfn));
62203805be0SYi Min Zhao char *as_name = g_strdup_printf("iommu-pci-%02x:%02x.%01x",
62303805be0SYi Min Zhao pci_bus_num(bus),
62403805be0SYi Min Zhao PCI_SLOT(devfn),
62503805be0SYi Min Zhao PCI_FUNC(devfn));
62603805be0SYi Min Zhao memory_region_init(&iommu->mr, OBJECT(iommu), mr_name, UINT64_MAX);
62703805be0SYi Min Zhao address_space_init(&iommu->as, &iommu->mr, as_name);
628b3f05d8cSYi Min Zhao iommu->iotlb = g_hash_table_new_full(g_int64_hash, g_int64_equal,
629b3f05d8cSYi Min Zhao NULL, g_free);
63003805be0SYi Min Zhao table->iommu[PCI_SLOT(devfn)] = iommu;
63103805be0SYi Min Zhao
63203805be0SYi Min Zhao g_free(mr_name);
63303805be0SYi Min Zhao g_free(as_name);
63403805be0SYi Min Zhao }
63503805be0SYi Min Zhao
63603805be0SYi Min Zhao return iommu;
63703805be0SYi Min Zhao }
63803805be0SYi Min Zhao
s390_pci_dma_iommu(PCIBus * bus,void * opaque,int devfn)6398cba80c3SFrank Blaschka static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
6408cba80c3SFrank Blaschka {
6418cba80c3SFrank Blaschka S390pciState *s = opaque;
64203805be0SYi Min Zhao S390PCIIOMMU *iommu = s390_pci_get_iommu(s, bus, devfn);
6438cba80c3SFrank Blaschka
64403805be0SYi Min Zhao return &iommu->as;
6458cba80c3SFrank Blaschka }
6468cba80c3SFrank Blaschka
647ba7d12ebSYi Liu static const PCIIOMMUOps s390_iommu_ops = {
648ba7d12ebSYi Liu .get_address_space = s390_pci_dma_iommu,
649ba7d12ebSYi Liu };
650ba7d12ebSYi Liu
set_ind_atomic(uint64_t ind_loc,uint8_t to_be_set)6518cba80c3SFrank Blaschka static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set)
6528cba80c3SFrank Blaschka {
65345175361SHalil Pasic uint8_t expected, actual;
6548cba80c3SFrank Blaschka hwaddr len = 1;
65545175361SHalil Pasic /* avoid multiple fetches */
65645175361SHalil Pasic uint8_t volatile *ind_addr;
6578cba80c3SFrank Blaschka
65885eb7c18SPhilippe Mathieu-Daudé ind_addr = cpu_physical_memory_map(ind_loc, &len, true);
6598cba80c3SFrank Blaschka if (!ind_addr) {
6608cba80c3SFrank Blaschka s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0);
6618cba80c3SFrank Blaschka return -1;
6628cba80c3SFrank Blaschka }
66345175361SHalil Pasic actual = *ind_addr;
6648cba80c3SFrank Blaschka do {
66545175361SHalil Pasic expected = actual;
666d73415a3SStefan Hajnoczi actual = qatomic_cmpxchg(ind_addr, expected, expected | to_be_set);
66745175361SHalil Pasic } while (actual != expected);
66845175361SHalil Pasic cpu_physical_memory_unmap((void *)ind_addr, len, 1, len);
6698cba80c3SFrank Blaschka
67045175361SHalil Pasic return actual;
6718cba80c3SFrank Blaschka }
6728cba80c3SFrank Blaschka
s390_msi_ctrl_write(void * opaque,hwaddr addr,uint64_t data,unsigned int size)6738cba80c3SFrank Blaschka static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
6748cba80c3SFrank Blaschka unsigned int size)
6758cba80c3SFrank Blaschka {
6768f955950SYi Min Zhao S390PCIBusDevice *pbdev = opaque;
6778cba80c3SFrank Blaschka uint32_t vec = data & ZPCI_MSI_VEC_MASK;
6788cba80c3SFrank Blaschka uint64_t ind_bit;
6798cba80c3SFrank Blaschka uint32_t sum_bit;
6808cba80c3SFrank Blaschka
681ceb7054fSYi Min Zhao assert(pbdev);
682f4a69168SCédric Le Goater
683f4a69168SCédric Le Goater trace_s390_pci_msi_ctrl_write(data, pbdev->idx, vec);
6848cba80c3SFrank Blaschka
6855d1abf23SYi Min Zhao if (pbdev->state != ZPCI_FS_ENABLED) {
6863be5c207SYi Min Zhao return;
6873be5c207SYi Min Zhao }
6883be5c207SYi Min Zhao
6898cba80c3SFrank Blaschka ind_bit = pbdev->routes.adapter.ind_offset;
6908cba80c3SFrank Blaschka sum_bit = pbdev->routes.adapter.summary_offset;
6918cba80c3SFrank Blaschka
6928cba80c3SFrank Blaschka set_ind_atomic(pbdev->routes.adapter.ind_addr + (ind_bit + vec) / 8,
6938cba80c3SFrank Blaschka 0x80 >> ((ind_bit + vec) % 8));
6948cba80c3SFrank Blaschka if (!set_ind_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8,
6958cba80c3SFrank Blaschka 0x80 >> (sum_bit % 8))) {
69625a08b8dSYi Min Zhao css_adapter_interrupt(CSS_IO_ADAPTER_PCI, pbdev->isc);
6978cba80c3SFrank Blaschka }
6988cba80c3SFrank Blaschka }
6998cba80c3SFrank Blaschka
s390_msi_ctrl_read(void * opaque,hwaddr addr,unsigned size)7008cba80c3SFrank Blaschka static uint64_t s390_msi_ctrl_read(void *opaque, hwaddr addr, unsigned size)
7018cba80c3SFrank Blaschka {
7028cba80c3SFrank Blaschka return 0xffffffff;
7038cba80c3SFrank Blaschka }
7048cba80c3SFrank Blaschka
7058cba80c3SFrank Blaschka static const MemoryRegionOps s390_msi_ctrl_ops = {
7068cba80c3SFrank Blaschka .write = s390_msi_ctrl_write,
7078cba80c3SFrank Blaschka .read = s390_msi_ctrl_read,
7088cba80c3SFrank Blaschka .endianness = DEVICE_LITTLE_ENDIAN,
7098cba80c3SFrank Blaschka };
7108cba80c3SFrank Blaschka
s390_pci_iommu_enable(S390PCIIOMMU * iommu)711de91ea92SYi Min Zhao void s390_pci_iommu_enable(S390PCIIOMMU *iommu)
712f0a399dbSYi Min Zhao {
7137df1dac5SMatthew Rosato /*
7147df1dac5SMatthew Rosato * The iommu region is initialized against a 0-mapped address space,
7157df1dac5SMatthew Rosato * so the smallest IOMMU region we can define runs from 0 to the end
7167df1dac5SMatthew Rosato * of the PCI address space.
7177df1dac5SMatthew Rosato */
718de91ea92SYi Min Zhao char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid);
7191221a474SAlexey Kardashevskiy memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr),
7201221a474SAlexey Kardashevskiy TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr),
7217df1dac5SMatthew Rosato name, iommu->pal + 1);
722de91ea92SYi Min Zhao iommu->enabled = true;
7233df9d748SAlexey Kardashevskiy memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr));
724de91ea92SYi Min Zhao g_free(name);
725f0a399dbSYi Min Zhao }
726f0a399dbSYi Min Zhao
s390_pci_iommu_disable(S390PCIIOMMU * iommu)727de91ea92SYi Min Zhao void s390_pci_iommu_disable(S390PCIIOMMU *iommu)
72871583888SYi Min Zhao {
729de91ea92SYi Min Zhao iommu->enabled = false;
730b3f05d8cSYi Min Zhao g_hash_table_remove_all(iommu->iotlb);
7313df9d748SAlexey Kardashevskiy memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr));
732de91ea92SYi Min Zhao object_unparent(OBJECT(&iommu->iommu_mr));
733f0a399dbSYi Min Zhao }
734f0a399dbSYi Min Zhao
s390_pci_iommu_free(S390pciState * s,PCIBus * bus,int32_t devfn)735a975a24aSYi Min Zhao static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn)
7368cba80c3SFrank Blaschka {
73703805be0SYi Min Zhao uint64_t key = (uintptr_t)bus;
73803805be0SYi Min Zhao S390PCIIOMMUTable *table = g_hash_table_lookup(s->iommu_table, &key);
73903805be0SYi Min Zhao S390PCIIOMMU *iommu = table ? table->iommu[PCI_SLOT(devfn)] : NULL;
7408cba80c3SFrank Blaschka
74103805be0SYi Min Zhao if (!table || !iommu) {
74203805be0SYi Min Zhao return;
7438cba80c3SFrank Blaschka }
74403805be0SYi Min Zhao
74503805be0SYi Min Zhao table->iommu[PCI_SLOT(devfn)] = NULL;
746b3f05d8cSYi Min Zhao g_hash_table_destroy(iommu->iotlb);
747e67ad058SMatthew Rosato /*
748e67ad058SMatthew Rosato * An attached PCI device may have memory listeners, eg. VFIO PCI.
749e67ad058SMatthew Rosato * The associated subregion will already have been unmapped in
750e67ad058SMatthew Rosato * s390_pci_iommu_disable in response to the guest deconfigure request.
751e67ad058SMatthew Rosato * Remove the listeners now before destroying the address space.
752e67ad058SMatthew Rosato */
753e67ad058SMatthew Rosato address_space_remove_listeners(&iommu->as);
75403805be0SYi Min Zhao address_space_destroy(&iommu->as);
75503805be0SYi Min Zhao object_unparent(OBJECT(&iommu->mr));
75603805be0SYi Min Zhao object_unparent(OBJECT(iommu));
75703805be0SYi Min Zhao object_unref(OBJECT(iommu));
7588cba80c3SFrank Blaschka }
7598cba80c3SFrank Blaschka
s390_group_create(int id,int host_id)76030dcf4f7SMatthew Rosato S390PCIGroup *s390_group_create(int id, int host_id)
76128dc86a0SPierre Morel {
76228dc86a0SPierre Morel S390PCIGroup *group;
76328dc86a0SPierre Morel S390pciState *s = s390_get_phb();
76428dc86a0SPierre Morel
76528dc86a0SPierre Morel group = g_new0(S390PCIGroup, 1);
76628dc86a0SPierre Morel group->id = id;
76730dcf4f7SMatthew Rosato group->host_id = host_id;
76828dc86a0SPierre Morel QTAILQ_INSERT_TAIL(&s->zpci_groups, group, link);
76928dc86a0SPierre Morel return group;
77028dc86a0SPierre Morel }
77128dc86a0SPierre Morel
s390_group_find(int id)77228dc86a0SPierre Morel S390PCIGroup *s390_group_find(int id)
77328dc86a0SPierre Morel {
77428dc86a0SPierre Morel S390PCIGroup *group;
77528dc86a0SPierre Morel S390pciState *s = s390_get_phb();
77628dc86a0SPierre Morel
77728dc86a0SPierre Morel QTAILQ_FOREACH(group, &s->zpci_groups, link) {
77828dc86a0SPierre Morel if (group->id == id) {
77928dc86a0SPierre Morel return group;
78028dc86a0SPierre Morel }
78128dc86a0SPierre Morel }
78228dc86a0SPierre Morel return NULL;
78328dc86a0SPierre Morel }
78428dc86a0SPierre Morel
s390_group_find_host_sim(int host_id)78530dcf4f7SMatthew Rosato S390PCIGroup *s390_group_find_host_sim(int host_id)
78630dcf4f7SMatthew Rosato {
78730dcf4f7SMatthew Rosato S390PCIGroup *group;
78830dcf4f7SMatthew Rosato S390pciState *s = s390_get_phb();
78930dcf4f7SMatthew Rosato
79030dcf4f7SMatthew Rosato QTAILQ_FOREACH(group, &s->zpci_groups, link) {
79130dcf4f7SMatthew Rosato if (group->id >= ZPCI_SIM_GRP_START && group->host_id == host_id) {
79230dcf4f7SMatthew Rosato return group;
79330dcf4f7SMatthew Rosato }
79430dcf4f7SMatthew Rosato }
79530dcf4f7SMatthew Rosato return NULL;
79630dcf4f7SMatthew Rosato }
79730dcf4f7SMatthew Rosato
s390_pci_init_default_group(void)79828dc86a0SPierre Morel static void s390_pci_init_default_group(void)
79928dc86a0SPierre Morel {
80028dc86a0SPierre Morel S390PCIGroup *group;
80128dc86a0SPierre Morel ClpRspQueryPciGrp *resgrp;
80228dc86a0SPierre Morel
80330dcf4f7SMatthew Rosato group = s390_group_create(ZPCI_DEFAULT_FN_GRP, ZPCI_DEFAULT_FN_GRP);
80428dc86a0SPierre Morel resgrp = &group->zpci_group;
80528dc86a0SPierre Morel resgrp->fr = 1;
806a4e2fff1SCornelia Huck resgrp->dasm = 0;
807a4e2fff1SCornelia Huck resgrp->msia = ZPCI_MSI_ADDR;
808a4e2fff1SCornelia Huck resgrp->mui = DEFAULT_MUI;
809a4e2fff1SCornelia Huck resgrp->i = 128;
810a4e2fff1SCornelia Huck resgrp->maxstbl = 128;
81128dc86a0SPierre Morel resgrp->version = 0;
812ac6aa30aSMatthew Rosato resgrp->dtsm = ZPCI_DTSM;
81328dc86a0SPierre Morel }
81428dc86a0SPierre Morel
set_pbdev_info(S390PCIBusDevice * pbdev)8159670ee75SPierre Morel static void set_pbdev_info(S390PCIBusDevice *pbdev)
8169670ee75SPierre Morel {
8179670ee75SPierre Morel pbdev->zpci_fn.sdma = ZPCI_SDMA_ADDR;
8189670ee75SPierre Morel pbdev->zpci_fn.edma = ZPCI_EDMA_ADDR;
8199670ee75SPierre Morel pbdev->zpci_fn.pchid = 0;
8201e7552ffSMatthew Rosato pbdev->zpci_fn.pfgid = ZPCI_DEFAULT_FN_GRP;
8219670ee75SPierre Morel pbdev->zpci_fn.fid = pbdev->fid;
8229670ee75SPierre Morel pbdev->zpci_fn.uid = pbdev->uid;
8239670ee75SPierre Morel pbdev->pci_group = s390_group_find(ZPCI_DEFAULT_FN_GRP);
8249670ee75SPierre Morel }
8259670ee75SPierre Morel
s390_pcihost_realize(DeviceState * dev,Error ** errp)826b576d582SThomas Huth static void s390_pcihost_realize(DeviceState *dev, Error **errp)
8278cba80c3SFrank Blaschka {
8288cba80c3SFrank Blaschka PCIBus *b;
8298cba80c3SFrank Blaschka BusState *bus;
8308cba80c3SFrank Blaschka PCIHostState *phb = PCI_HOST_BRIDGE(dev);
8318cba80c3SFrank Blaschka S390pciState *s = S390_PCI_HOST_BRIDGE(dev);
8328cba80c3SFrank Blaschka
833f4a69168SCédric Le Goater trace_s390_pcihost("realize");
8348cba80c3SFrank Blaschka
835b576d582SThomas Huth b = pci_register_root_bus(dev, NULL, s390_pci_set_irq, s390_pci_map_irq,
836b576d582SThomas Huth NULL, get_system_memory(), get_system_io(), 0,
837b576d582SThomas Huth 64, TYPE_PCI_BUS);
838ba7d12ebSYi Liu pci_setup_iommu(b, &s390_iommu_ops, s);
8398cba80c3SFrank Blaschka
8408cba80c3SFrank Blaschka bus = BUS(b);
8419bc6bfdfSMarkus Armbruster qbus_set_hotplug_handler(bus, OBJECT(dev));
8428cba80c3SFrank Blaschka phb->bus = b;
84390a0f9afSYi Min Zhao
8449388d170SPeter Maydell s->bus = S390_PCI_BUS(qbus_new(TYPE_S390_PCI_BUS, dev, NULL));
8459bc6bfdfSMarkus Armbruster qbus_set_hotplug_handler(BUS(s->bus), OBJECT(dev));
84690a0f9afSYi Min Zhao
84703805be0SYi Min Zhao s->iommu_table = g_hash_table_new_full(g_int64_hash, g_int64_equal,
84803805be0SYi Min Zhao NULL, g_free);
849df8dd91bSYi Min Zhao s->zpci_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL);
850d2f07120SPierre Morel s->bus_no = 0;
85130dcf4f7SMatthew Rosato s->next_sim_grp = ZPCI_SIM_GRP_START;
8528cba80c3SFrank Blaschka QTAILQ_INIT(&s->pending_sei);
853e70377dfSPierre Morel QTAILQ_INIT(&s->zpci_devs);
85437fa32deSMatthew Rosato QTAILQ_INIT(&s->zpci_dma_limit);
85528dc86a0SPierre Morel QTAILQ_INIT(&s->zpci_groups);
856dde522bbSFei Li
85728dc86a0SPierre Morel s390_pci_init_default_group();
8581497c160SFei Li css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false,
859992861fbSMarkus Armbruster S390_ADAPTER_SUPPRESSIBLE, errp);
860b576d582SThomas Huth }
8618cba80c3SFrank Blaschka
s390_pcihost_unrealize(DeviceState * dev)862b354d5d8SMatthew Rosato static void s390_pcihost_unrealize(DeviceState *dev)
863b354d5d8SMatthew Rosato {
864b354d5d8SMatthew Rosato S390PCIGroup *group;
865b354d5d8SMatthew Rosato S390pciState *s = S390_PCI_HOST_BRIDGE(dev);
866b354d5d8SMatthew Rosato
867b354d5d8SMatthew Rosato while (!QTAILQ_EMPTY(&s->zpci_groups)) {
868b354d5d8SMatthew Rosato group = QTAILQ_FIRST(&s->zpci_groups);
869b354d5d8SMatthew Rosato QTAILQ_REMOVE(&s->zpci_groups, group, link);
870b354d5d8SMatthew Rosato }
871b354d5d8SMatthew Rosato }
872b354d5d8SMatthew Rosato
s390_pci_msix_init(S390PCIBusDevice * pbdev)873857cc719SYi Min Zhao static int s390_pci_msix_init(S390PCIBusDevice *pbdev)
8748cba80c3SFrank Blaschka {
875857cc719SYi Min Zhao char *name;
8768cba80c3SFrank Blaschka uint8_t pos;
8778cba80c3SFrank Blaschka uint16_t ctrl;
8788cba80c3SFrank Blaschka uint32_t table, pba;
8798cba80c3SFrank Blaschka
8808cba80c3SFrank Blaschka pos = pci_find_capability(pbdev->pdev, PCI_CAP_ID_MSIX);
8818cba80c3SFrank Blaschka if (!pos) {
882857cc719SYi Min Zhao return -1;
8838cba80c3SFrank Blaschka }
8848cba80c3SFrank Blaschka
885ce1307e1SWei Yang ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_FLAGS,
8868cba80c3SFrank Blaschka pci_config_size(pbdev->pdev), sizeof(ctrl));
8878cba80c3SFrank Blaschka table = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_TABLE,
8888cba80c3SFrank Blaschka pci_config_size(pbdev->pdev), sizeof(table));
8898cba80c3SFrank Blaschka pba = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_PBA,
8908cba80c3SFrank Blaschka pci_config_size(pbdev->pdev), sizeof(pba));
8918cba80c3SFrank Blaschka
8928cba80c3SFrank Blaschka pbdev->msix.table_bar = table & PCI_MSIX_FLAGS_BIRMASK;
8938cba80c3SFrank Blaschka pbdev->msix.table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
8948cba80c3SFrank Blaschka pbdev->msix.pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK;
8958cba80c3SFrank Blaschka pbdev->msix.pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
8968cba80c3SFrank Blaschka pbdev->msix.entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
8978f955950SYi Min Zhao
8988f955950SYi Min Zhao name = g_strdup_printf("msix-s390-%04x", pbdev->uid);
8998f955950SYi Min Zhao memory_region_init_io(&pbdev->msix_notify_mr, OBJECT(pbdev),
900ed3288ffSThomas Huth &s390_msi_ctrl_ops, pbdev, name, TARGET_PAGE_SIZE);
9011e7552ffSMatthew Rosato memory_region_add_subregion(&pbdev->iommu->mr,
9021e7552ffSMatthew Rosato pbdev->pci_group->zpci_group.msia,
9038f955950SYi Min Zhao &pbdev->msix_notify_mr);
9048f955950SYi Min Zhao g_free(name);
905857cc719SYi Min Zhao
906857cc719SYi Min Zhao return 0;
9078f955950SYi Min Zhao }
9088f955950SYi Min Zhao
s390_pci_msix_free(S390PCIBusDevice * pbdev)9098f955950SYi Min Zhao static void s390_pci_msix_free(S390PCIBusDevice *pbdev)
9108f955950SYi Min Zhao {
91115d0e794SMatthew Rosato if (pbdev->msix.entries == 0) {
91215d0e794SMatthew Rosato return;
91315d0e794SMatthew Rosato }
91415d0e794SMatthew Rosato
9158f955950SYi Min Zhao memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->msix_notify_mr);
9168f955950SYi Min Zhao object_unparent(OBJECT(&pbdev->msix_notify_mr));
9178f955950SYi Min Zhao }
9188f955950SYi Min Zhao
s390_pci_device_new(S390pciState * s,const char * target,Error ** errp)919a975a24aSYi Min Zhao static S390PCIBusDevice *s390_pci_device_new(S390pciState *s,
920b6e67eccSDavid Hildenbrand const char *target, Error **errp)
9213e5cfba3SYi Min Zhao {
922b6e67eccSDavid Hildenbrand Error *local_err = NULL;
923b6e67eccSDavid Hildenbrand DeviceState *dev;
9243e5cfba3SYi Min Zhao
925df707969SMarkus Armbruster dev = qdev_try_new(TYPE_S390_PCI_DEVICE);
9263e5cfba3SYi Min Zhao if (!dev) {
927b6e67eccSDavid Hildenbrand error_setg(errp, "zPCI device could not be created");
9283e5cfba3SYi Min Zhao return NULL;
9293e5cfba3SYi Min Zhao }
9303e5cfba3SYi Min Zhao
931778a2dc5SMarkus Armbruster if (!object_property_set_str(OBJECT(dev), "target", target, &local_err)) {
932b6e67eccSDavid Hildenbrand object_unparent(OBJECT(dev));
933b6e67eccSDavid Hildenbrand error_propagate_prepend(errp, local_err,
934b6e67eccSDavid Hildenbrand "zPCI device could not be created: ");
935b6e67eccSDavid Hildenbrand return NULL;
936b6e67eccSDavid Hildenbrand }
937118bfd76SMarkus Armbruster if (!qdev_realize_and_unref(dev, BUS(s->bus), &local_err)) {
938b6e67eccSDavid Hildenbrand object_unparent(OBJECT(dev));
939b6e67eccSDavid Hildenbrand error_propagate_prepend(errp, local_err,
940b6e67eccSDavid Hildenbrand "zPCI device could not be created: ");
941b6e67eccSDavid Hildenbrand return NULL;
942b6e67eccSDavid Hildenbrand }
9433e5cfba3SYi Min Zhao
9443e5cfba3SYi Min Zhao return S390_PCI_DEVICE(dev);
9453e5cfba3SYi Min Zhao }
9463e5cfba3SYi Min Zhao
s390_pci_alloc_idx(S390pciState * s,S390PCIBusDevice * pbdev)947a975a24aSYi Min Zhao static bool s390_pci_alloc_idx(S390pciState *s, S390PCIBusDevice *pbdev)
948e70377dfSPierre Morel {
949e70377dfSPierre Morel uint32_t idx;
950e70377dfSPierre Morel
951e70377dfSPierre Morel idx = s->next_idx;
952a975a24aSYi Min Zhao while (s390_pci_find_dev_by_idx(s, idx)) {
953e70377dfSPierre Morel idx = (idx + 1) & FH_MASK_INDEX;
954e70377dfSPierre Morel if (idx == s->next_idx) {
955e70377dfSPierre Morel return false;
956e70377dfSPierre Morel }
957e70377dfSPierre Morel }
958e70377dfSPierre Morel
959e70377dfSPierre Morel pbdev->idx = idx;
960e70377dfSPierre Morel return true;
961e70377dfSPierre Morel }
962e70377dfSPierre Morel
s390_pcihost_pre_plug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)9636069bcdeSDavid Hildenbrand static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
9646069bcdeSDavid Hildenbrand Error **errp)
9656069bcdeSDavid Hildenbrand {
9666069bcdeSDavid Hildenbrand S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
9676069bcdeSDavid Hildenbrand
968703fef6fSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_ZPCI)) {
969703fef6fSDavid Hildenbrand warn_report("Plugging a PCI/zPCI device without the 'zpci' CPU "
970703fef6fSDavid Hildenbrand "feature enabled; the guest will not be able to see/use "
971703fef6fSDavid Hildenbrand "this device");
972703fef6fSDavid Hildenbrand }
973703fef6fSDavid Hildenbrand
9746069bcdeSDavid Hildenbrand if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
9756069bcdeSDavid Hildenbrand PCIDevice *pdev = PCI_DEVICE(dev);
9766069bcdeSDavid Hildenbrand
9776069bcdeSDavid Hildenbrand if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
9786069bcdeSDavid Hildenbrand error_setg(errp, "multifunction not supported in s390");
9796069bcdeSDavid Hildenbrand return;
9806069bcdeSDavid Hildenbrand }
9816069bcdeSDavid Hildenbrand } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
9826069bcdeSDavid Hildenbrand S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);
9836069bcdeSDavid Hildenbrand
9846069bcdeSDavid Hildenbrand if (!s390_pci_alloc_idx(s, pbdev)) {
9856069bcdeSDavid Hildenbrand error_setg(errp, "no slot for plugging zpci device");
9866069bcdeSDavid Hildenbrand return;
9876069bcdeSDavid Hildenbrand }
9886069bcdeSDavid Hildenbrand }
9896069bcdeSDavid Hildenbrand }
9906069bcdeSDavid Hildenbrand
s390_pci_update_subordinate(PCIDevice * dev,uint32_t nr)991150f4625SDavid Hildenbrand static void s390_pci_update_subordinate(PCIDevice *dev, uint32_t nr)
992150f4625SDavid Hildenbrand {
993150f4625SDavid Hildenbrand uint32_t old_nr;
994150f4625SDavid Hildenbrand
995150f4625SDavid Hildenbrand pci_default_write_config(dev, PCI_SUBORDINATE_BUS, nr, 1);
996150f4625SDavid Hildenbrand while (!pci_bus_is_root(pci_get_bus(dev))) {
997150f4625SDavid Hildenbrand dev = pci_get_bus(dev)->parent_dev;
998150f4625SDavid Hildenbrand
999150f4625SDavid Hildenbrand old_nr = pci_default_read_config(dev, PCI_SUBORDINATE_BUS, 1);
1000150f4625SDavid Hildenbrand if (old_nr < nr) {
1001150f4625SDavid Hildenbrand pci_default_write_config(dev, PCI_SUBORDINATE_BUS, nr, 1);
1002150f4625SDavid Hildenbrand }
1003150f4625SDavid Hildenbrand }
1004150f4625SDavid Hildenbrand }
1005150f4625SDavid Hildenbrand
s390_pci_interp_plug(S390pciState * s,S390PCIBusDevice * pbdev)1006dd1d5fd9SMatthew Rosato static int s390_pci_interp_plug(S390pciState *s, S390PCIBusDevice *pbdev)
1007dd1d5fd9SMatthew Rosato {
1008dd1d5fd9SMatthew Rosato uint32_t idx, fh;
1009dd1d5fd9SMatthew Rosato
1010dd1d5fd9SMatthew Rosato if (!s390_pci_get_host_fh(pbdev, &fh)) {
1011dd1d5fd9SMatthew Rosato return -EPERM;
1012dd1d5fd9SMatthew Rosato }
1013dd1d5fd9SMatthew Rosato
1014dd1d5fd9SMatthew Rosato /*
1015dd1d5fd9SMatthew Rosato * The host device is already in an enabled state, but we always present
1016dd1d5fd9SMatthew Rosato * the initial device state to the guest as disabled (ZPCI_FS_DISABLED).
1017dd1d5fd9SMatthew Rosato * Therefore, mask off the enable bit from the passthrough handle until
1018dd1d5fd9SMatthew Rosato * the guest issues a CLP SET PCI FN later to enable the device.
1019dd1d5fd9SMatthew Rosato */
1020dd1d5fd9SMatthew Rosato pbdev->fh = fh & ~FH_MASK_ENABLE;
1021dd1d5fd9SMatthew Rosato
1022dd1d5fd9SMatthew Rosato /* Next, see if the idx is already in-use */
1023dd1d5fd9SMatthew Rosato idx = pbdev->fh & FH_MASK_INDEX;
1024dd1d5fd9SMatthew Rosato if (pbdev->idx != idx) {
1025dd1d5fd9SMatthew Rosato if (s390_pci_find_dev_by_idx(s, idx)) {
1026dd1d5fd9SMatthew Rosato return -EINVAL;
1027dd1d5fd9SMatthew Rosato }
1028dd1d5fd9SMatthew Rosato /*
1029dd1d5fd9SMatthew Rosato * Update the idx entry with the passed through idx
1030dd1d5fd9SMatthew Rosato * If the relinquished idx is lower than next_idx, use it
1031dd1d5fd9SMatthew Rosato * to replace next_idx
1032dd1d5fd9SMatthew Rosato */
1033dd1d5fd9SMatthew Rosato g_hash_table_remove(s->zpci_table, &pbdev->idx);
1034dd1d5fd9SMatthew Rosato if (idx < s->next_idx) {
1035dd1d5fd9SMatthew Rosato s->next_idx = idx;
1036dd1d5fd9SMatthew Rosato }
1037dd1d5fd9SMatthew Rosato pbdev->idx = idx;
1038dd1d5fd9SMatthew Rosato g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev);
1039dd1d5fd9SMatthew Rosato }
1040dd1d5fd9SMatthew Rosato
1041dd1d5fd9SMatthew Rosato return 0;
1042dd1d5fd9SMatthew Rosato }
1043dd1d5fd9SMatthew Rosato
s390_pcihost_plug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)1044fa2a7751SDavid Hildenbrand static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
1045fa2a7751SDavid Hildenbrand Error **errp)
10468cba80c3SFrank Blaschka {
104719375e9bSDavid Hildenbrand S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
1048af9ed379SYi Min Zhao PCIDevice *pdev = NULL;
1049af9ed379SYi Min Zhao S390PCIBusDevice *pbdev = NULL;
1050dd1d5fd9SMatthew Rosato int rc;
1051af9ed379SYi Min Zhao
10523fc92a24SPierre Morel if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
10533fc92a24SPierre Morel PCIBridge *pb = PCI_BRIDGE(dev);
10543fc92a24SPierre Morel
1055150f4625SDavid Hildenbrand pdev = PCI_DEVICE(dev);
10563fc92a24SPierre Morel pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq);
1057ba7d12ebSYi Liu pci_setup_iommu(&pb->sec_bus, &s390_iommu_ops, s);
10583fc92a24SPierre Morel
10599bc6bfdfSMarkus Armbruster qbus_set_hotplug_handler(BUS(&pb->sec_bus), OBJECT(s));
1060d2f07120SPierre Morel
1061d2f07120SPierre Morel if (dev->hotplugged) {
1062d30a7507SDavid Hildenbrand pci_default_write_config(pdev, PCI_PRIMARY_BUS,
1063d30a7507SDavid Hildenbrand pci_dev_bus_num(pdev), 1);
1064d2f07120SPierre Morel s->bus_no += 1;
1065d2f07120SPierre Morel pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1);
1066150f4625SDavid Hildenbrand
1067150f4625SDavid Hildenbrand s390_pci_update_subordinate(pdev, s->bus_no);
1068d2f07120SPierre Morel }
10693fc92a24SPierre Morel } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
1070af9ed379SYi Min Zhao pdev = PCI_DEVICE(dev);
10718cba80c3SFrank Blaschka
10723e5cfba3SYi Min Zhao if (!dev->id) {
10733e5cfba3SYi Min Zhao /* In the case the PCI device does not define an id */
10743e5cfba3SYi Min Zhao /* we generate one based on the PCI address */
10753e5cfba3SYi Min Zhao dev->id = g_strdup_printf("auto_%02x:%02x.%01x",
1076cdc57472SDavid Gibson pci_dev_bus_num(pdev),
1077af9ed379SYi Min Zhao PCI_SLOT(pdev->devfn),
1078af9ed379SYi Min Zhao PCI_FUNC(pdev->devfn));
10793e5cfba3SYi Min Zhao }
10808cba80c3SFrank Blaschka
1081a975a24aSYi Min Zhao pbdev = s390_pci_find_dev_by_target(s, dev->id);
10823e5cfba3SYi Min Zhao if (!pbdev) {
1083b6e67eccSDavid Hildenbrand pbdev = s390_pci_device_new(s, dev->id, errp);
10843e5cfba3SYi Min Zhao if (!pbdev) {
10850d36d791SYi Min Zhao return;
10863e5cfba3SYi Min Zhao }
10873e5cfba3SYi Min Zhao }
10883e5cfba3SYi Min Zhao
1089af9ed379SYi Min Zhao pbdev->pdev = pdev;
1090fd56e061SDavid Gibson pbdev->iommu = s390_pci_get_iommu(s, pci_get_bus(pdev), pdev->devfn);
1091de91ea92SYi Min Zhao pbdev->iommu->pbdev = pbdev;
10922c28c490SChristian Borntraeger pbdev->state = ZPCI_FS_DISABLED;
10939670ee75SPierre Morel set_pbdev_info(pbdev);
10948f955950SYi Min Zhao
109537fa32deSMatthew Rosato if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) {
1096dd1d5fd9SMatthew Rosato /*
1097dd1d5fd9SMatthew Rosato * By default, interpretation is always requested; if the available
1098dd1d5fd9SMatthew Rosato * facilities indicate it is not available, fallback to the
1099dd1d5fd9SMatthew Rosato * interception model.
1100dd1d5fd9SMatthew Rosato */
1101dd1d5fd9SMatthew Rosato if (pbdev->interp) {
1102dd1d5fd9SMatthew Rosato if (s390_pci_kvm_interp_allowed()) {
1103dd1d5fd9SMatthew Rosato rc = s390_pci_interp_plug(s, pbdev);
1104dd1d5fd9SMatthew Rosato if (rc) {
1105dd1d5fd9SMatthew Rosato error_setg(errp, "Plug failed for zPCI device in "
1106dd1d5fd9SMatthew Rosato "interpretation mode: %d", rc);
1107dd1d5fd9SMatthew Rosato return;
1108dd1d5fd9SMatthew Rosato }
1109dd1d5fd9SMatthew Rosato } else {
1110f4a69168SCédric Le Goater trace_s390_pcihost("zPCI interpretation missing");
1111dd1d5fd9SMatthew Rosato pbdev->interp = false;
1112d0bc7091SMatthew Rosato pbdev->forwarding_assist = false;
1113dd1d5fd9SMatthew Rosato }
1114dd1d5fd9SMatthew Rosato }
111537fa32deSMatthew Rosato pbdev->iommu->dma_limit = s390_pci_start_dma_count(s, pbdev);
11161e7552ffSMatthew Rosato /* Fill in CLP information passed via the vfio region */
11171e7552ffSMatthew Rosato s390_pci_get_clp_info(pbdev);
1118dd1d5fd9SMatthew Rosato if (!pbdev->interp) {
1119dd1d5fd9SMatthew Rosato /* Do vfio passthrough but intercept for I/O */
1120dd1d5fd9SMatthew Rosato pbdev->fh |= FH_SHM_VFIO;
1121d0bc7091SMatthew Rosato pbdev->forwarding_assist = false;
1122dd1d5fd9SMatthew Rosato }
112303451953SMatthew Rosato /* Register shutdown notifier and reset callback for ISM devices */
112403451953SMatthew Rosato if (pbdev->pft == ZPCI_PFT_ISM) {
112503451953SMatthew Rosato pbdev->shutdown_notifier.notify = s390_pci_shutdown_notifier;
112603451953SMatthew Rosato qemu_register_shutdown_notifier(&pbdev->shutdown_notifier);
112703451953SMatthew Rosato }
112837fa32deSMatthew Rosato } else {
112937fa32deSMatthew Rosato pbdev->fh |= FH_SHM_EMUL;
1130dd1d5fd9SMatthew Rosato /* Always intercept emulated devices */
1131dd1d5fd9SMatthew Rosato pbdev->interp = false;
1132d0bc7091SMatthew Rosato pbdev->forwarding_assist = false;
113337fa32deSMatthew Rosato }
113437fa32deSMatthew Rosato
113515d0e794SMatthew Rosato if (s390_pci_msix_init(pbdev) && !pbdev->interp) {
1136857cc719SYi Min Zhao error_setg(errp, "MSI-X support is mandatory "
1137857cc719SYi Min Zhao "in the S390 architecture");
1138857cc719SYi Min Zhao return;
1139857cc719SYi Min Zhao }
11408cba80c3SFrank Blaschka
11418cba80c3SFrank Blaschka if (dev->hotplugged) {
1142d57d6abcSDavid Hildenbrand s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED ,
11438cba80c3SFrank Blaschka pbdev->fh, pbdev->fid);
1144af9ed379SYi Min Zhao }
1145af9ed379SYi Min Zhao } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
1146af9ed379SYi Min Zhao pbdev = S390_PCI_DEVICE(dev);
1147e70377dfSPierre Morel
11486069bcdeSDavid Hildenbrand /* the allocated idx is actually getting used */
11496069bcdeSDavid Hildenbrand s->next_idx = (pbdev->idx + 1) & FH_MASK_INDEX;
1150e70377dfSPierre Morel pbdev->fh = pbdev->idx;
1151e70377dfSPierre Morel QTAILQ_INSERT_TAIL(&s->zpci_devs, pbdev, link);
1152df8dd91bSYi Min Zhao g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev);
11536ed675c9SLi Qiang } else {
11546ed675c9SLi Qiang g_assert_not_reached();
11558cba80c3SFrank Blaschka }
11568cba80c3SFrank Blaschka }
11578cba80c3SFrank Blaschka
s390_pcihost_unplug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)1158fa2a7751SDavid Hildenbrand static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
1159fa2a7751SDavid Hildenbrand Error **errp)
11608cba80c3SFrank Blaschka {
116119375e9bSDavid Hildenbrand S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
1162e0998fe8SDavid Hildenbrand S390PCIBusDevice *pbdev = NULL;
1163e0998fe8SDavid Hildenbrand
1164e0998fe8SDavid Hildenbrand if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
1165e0998fe8SDavid Hildenbrand PCIDevice *pci_dev = PCI_DEVICE(dev);
116603805be0SYi Min Zhao PCIBus *bus;
1167e70377dfSPierre Morel int32_t devfn;
1168e0998fe8SDavid Hildenbrand
1169e0998fe8SDavid Hildenbrand pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
1170e0998fe8SDavid Hildenbrand g_assert(pbdev);
1171e0998fe8SDavid Hildenbrand
1172e0998fe8SDavid Hildenbrand s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
1173e0998fe8SDavid Hildenbrand pbdev->fh, pbdev->fid);
1174e0998fe8SDavid Hildenbrand bus = pci_get_bus(pci_dev);
1175e0998fe8SDavid Hildenbrand devfn = pci_dev->devfn;
1176981c3dcdSMarkus Armbruster qdev_unrealize(dev);
1177e0998fe8SDavid Hildenbrand
1178e0998fe8SDavid Hildenbrand s390_pci_msix_free(pbdev);
1179e0998fe8SDavid Hildenbrand s390_pci_iommu_free(s, bus, devfn);
1180e0998fe8SDavid Hildenbrand pbdev->pdev = NULL;
1181e0998fe8SDavid Hildenbrand pbdev->state = ZPCI_FS_RESERVED;
1182e0998fe8SDavid Hildenbrand } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
1183e0998fe8SDavid Hildenbrand pbdev = S390_PCI_DEVICE(dev);
1184e0998fe8SDavid Hildenbrand pbdev->fid = 0;
1185e0998fe8SDavid Hildenbrand QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
1186e0998fe8SDavid Hildenbrand g_hash_table_remove(s->zpci_table, &pbdev->idx);
118737fa32deSMatthew Rosato if (pbdev->iommu->dma_limit) {
118837fa32deSMatthew Rosato s390_pci_end_dma_count(s, pbdev->iommu->dma_limit);
118937fa32deSMatthew Rosato }
1190981c3dcdSMarkus Armbruster qdev_unrealize(dev);
1191e0998fe8SDavid Hildenbrand }
1192e0998fe8SDavid Hildenbrand }
1193e0998fe8SDavid Hildenbrand
s390_pcihost_unplug_request(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)1194e0998fe8SDavid Hildenbrand static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev,
1195e0998fe8SDavid Hildenbrand DeviceState *dev,
1196e0998fe8SDavid Hildenbrand Error **errp)
1197e0998fe8SDavid Hildenbrand {
1198e0998fe8SDavid Hildenbrand S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
1199e0998fe8SDavid Hildenbrand S390PCIBusDevice *pbdev;
1200af9ed379SYi Min Zhao
12013fc92a24SPierre Morel if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
12023fc92a24SPierre Morel error_setg(errp, "PCI bridge hot unplug currently not supported");
12033fc92a24SPierre Morel } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
1204e0998fe8SDavid Hildenbrand /*
1205e0998fe8SDavid Hildenbrand * Redirect the unplug request to the zPCI device and remember that
1206e0998fe8SDavid Hildenbrand * we've checked the PCI device already (to prevent endless recursion).
1207e0998fe8SDavid Hildenbrand */
1208e0998fe8SDavid Hildenbrand pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
1209e0998fe8SDavid Hildenbrand g_assert(pbdev);
1210e0998fe8SDavid Hildenbrand pbdev->pci_unplug_request_processed = true;
1211e0998fe8SDavid Hildenbrand qdev_unplug(DEVICE(pbdev), errp);
1212af9ed379SYi Min Zhao } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
1213af9ed379SYi Min Zhao pbdev = S390_PCI_DEVICE(dev);
1214e0998fe8SDavid Hildenbrand
1215e0998fe8SDavid Hildenbrand /*
1216e0998fe8SDavid Hildenbrand * If unplug was initially requested for the zPCI device, we
1217e0998fe8SDavid Hildenbrand * first have to redirect to the PCI device, which will in return
1218e0998fe8SDavid Hildenbrand * redirect back to us after performing its checks (if the request
1219e0998fe8SDavid Hildenbrand * is not blocked, e.g. because it's a PCI bridge).
1220e0998fe8SDavid Hildenbrand */
1221e0998fe8SDavid Hildenbrand if (pbdev->pdev && !pbdev->pci_unplug_request_processed) {
1222e0998fe8SDavid Hildenbrand qdev_unplug(DEVICE(pbdev->pdev), errp);
1223e0998fe8SDavid Hildenbrand return;
1224af9ed379SYi Min Zhao }
1225e0998fe8SDavid Hildenbrand pbdev->pci_unplug_request_processed = false;
12268cba80c3SFrank Blaschka
12275d1abf23SYi Min Zhao switch (pbdev->state) {
12285d1abf23SYi Min Zhao case ZPCI_FS_STANDBY:
1229e0998fe8SDavid Hildenbrand case ZPCI_FS_RESERVED:
1230e0998fe8SDavid Hildenbrand s390_pci_perform_unplug(pbdev);
12315d1abf23SYi Min Zhao break;
12325d1abf23SYi Min Zhao default:
12339f2a46b1SDavid Hildenbrand /*
12349f2a46b1SDavid Hildenbrand * Allow to send multiple requests, e.g. if the guest crashed
12359f2a46b1SDavid Hildenbrand * before releasing the device, we would not be able to send
12369f2a46b1SDavid Hildenbrand * another request to the same VM (e.g. fresh OS).
12379f2a46b1SDavid Hildenbrand */
12389f2a46b1SDavid Hildenbrand pbdev->unplug_requested = true;
123993d16d81SYi Min Zhao s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST,
12408cba80c3SFrank Blaschka pbdev->fh, pbdev->fid);
124193d16d81SYi Min Zhao }
1242e0998fe8SDavid Hildenbrand } else {
1243e0998fe8SDavid Hildenbrand g_assert_not_reached();
12448cba80c3SFrank Blaschka }
12458cba80c3SFrank Blaschka }
12468cba80c3SFrank Blaschka
s390_pci_enumerate_bridge(PCIBus * bus,PCIDevice * pdev,void * opaque)1247d2f07120SPierre Morel static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev,
1248d2f07120SPierre Morel void *opaque)
1249d2f07120SPierre Morel {
1250d2f07120SPierre Morel S390pciState *s = opaque;
1251d2f07120SPierre Morel PCIBus *sec_bus = NULL;
1252d2f07120SPierre Morel
1253d2f07120SPierre Morel if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) !=
1254d2f07120SPierre Morel PCI_HEADER_TYPE_BRIDGE)) {
1255d2f07120SPierre Morel return;
1256d2f07120SPierre Morel }
1257d2f07120SPierre Morel
1258d2f07120SPierre Morel (s->bus_no)++;
1259d30a7507SDavid Hildenbrand pci_default_write_config(pdev, PCI_PRIMARY_BUS, pci_dev_bus_num(pdev), 1);
1260d2f07120SPierre Morel pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1);
1261d2f07120SPierre Morel pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1);
1262d2f07120SPierre Morel
1263d2f07120SPierre Morel sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
1264d2f07120SPierre Morel if (!sec_bus) {
1265d2f07120SPierre Morel return;
1266d2f07120SPierre Morel }
1267d2f07120SPierre Morel
1268d30a7507SDavid Hildenbrand /* Assign numbers to all child bridges. The last is the highest number. */
12692914fc61SPeter Xu pci_for_each_device_under_bus(sec_bus, s390_pci_enumerate_bridge, s);
1270d2f07120SPierre Morel pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1);
1271d2f07120SPierre Morel }
1272d2f07120SPierre Morel
s390_pci_ism_reset(void)127368c691caSMatthew Rosato void s390_pci_ism_reset(void)
127468c691caSMatthew Rosato {
127568c691caSMatthew Rosato S390pciState *s = s390_get_phb();
127668c691caSMatthew Rosato
127768c691caSMatthew Rosato S390PCIBusDevice *pbdev, *next;
127868c691caSMatthew Rosato
127968c691caSMatthew Rosato /* Trigger reset event for each passthrough ISM device currently in-use */
128068c691caSMatthew Rosato QTAILQ_FOREACH_SAFE(pbdev, &s->zpci_devs, link, next) {
128168c691caSMatthew Rosato if (pbdev->interp && pbdev->pft == ZPCI_PFT_ISM &&
128268c691caSMatthew Rosato pbdev->fh & FH_MASK_ENABLE) {
128368c691caSMatthew Rosato s390_pci_kvm_aif_disable(pbdev);
128468c691caSMatthew Rosato
128568c691caSMatthew Rosato pci_device_reset(pbdev->pdev);
128668c691caSMatthew Rosato }
128768c691caSMatthew Rosato }
128868c691caSMatthew Rosato }
128968c691caSMatthew Rosato
s390_pcihost_reset(DeviceState * dev)1290d2f07120SPierre Morel static void s390_pcihost_reset(DeviceState *dev)
1291d2f07120SPierre Morel {
1292d2f07120SPierre Morel S390pciState *s = S390_PCI_HOST_BRIDGE(dev);
1293d2f07120SPierre Morel PCIBus *bus = s->parent_obj.bus;
12942313a88fSDavid Hildenbrand S390PCIBusDevice *pbdev, *next;
12952313a88fSDavid Hildenbrand
12962313a88fSDavid Hildenbrand /* Process all pending unplug requests */
12972313a88fSDavid Hildenbrand QTAILQ_FOREACH_SAFE(pbdev, &s->zpci_devs, link, next) {
12982313a88fSDavid Hildenbrand if (pbdev->unplug_requested) {
1299d0bc7091SMatthew Rosato if (pbdev->interp && (pbdev->fh & FH_MASK_ENABLE)) {
1300d0bc7091SMatthew Rosato /* Interpreted devices were using interrupt forwarding */
1301d0bc7091SMatthew Rosato s390_pci_kvm_aif_disable(pbdev);
1302d0bc7091SMatthew Rosato } else if (pbdev->summary_ind) {
13032313a88fSDavid Hildenbrand pci_dereg_irqs(pbdev);
13042313a88fSDavid Hildenbrand }
13052313a88fSDavid Hildenbrand if (pbdev->iommu->enabled) {
13062313a88fSDavid Hildenbrand pci_dereg_ioat(pbdev->iommu);
13072313a88fSDavid Hildenbrand }
13082313a88fSDavid Hildenbrand pbdev->state = ZPCI_FS_STANDBY;
13092313a88fSDavid Hildenbrand s390_pci_perform_unplug(pbdev);
13102313a88fSDavid Hildenbrand }
13112313a88fSDavid Hildenbrand }
1312d2f07120SPierre Morel
1313d30a7507SDavid Hildenbrand /*
1314d30a7507SDavid Hildenbrand * When resetting a PCI bridge, the assigned numbers are set to 0. So
1315d30a7507SDavid Hildenbrand * on every system reset, we also have to reassign numbers.
1316d30a7507SDavid Hildenbrand */
1317d2f07120SPierre Morel s->bus_no = 0;
13182914fc61SPeter Xu pci_for_each_device_under_bus(bus, s390_pci_enumerate_bridge, s);
1319d2f07120SPierre Morel }
1320d2f07120SPierre Morel
s390_pcihost_class_init(ObjectClass * klass,void * data)13218cba80c3SFrank Blaschka static void s390_pcihost_class_init(ObjectClass *klass, void *data)
13228cba80c3SFrank Blaschka {
13238cba80c3SFrank Blaschka DeviceClass *dc = DEVICE_CLASS(klass);
13248cba80c3SFrank Blaschka HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
13258cba80c3SFrank Blaschka
1326*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, s390_pcihost_reset);
1327b576d582SThomas Huth dc->realize = s390_pcihost_realize;
1328b354d5d8SMatthew Rosato dc->unrealize = s390_pcihost_unrealize;
13296069bcdeSDavid Hildenbrand hc->pre_plug = s390_pcihost_pre_plug;
1330fa2a7751SDavid Hildenbrand hc->plug = s390_pcihost_plug;
1331e0998fe8SDavid Hildenbrand hc->unplug_request = s390_pcihost_unplug_request;
1332fa2a7751SDavid Hildenbrand hc->unplug = s390_pcihost_unplug;
1333226419d6SMichael S. Tsirkin msi_nonbroken = true;
13348cba80c3SFrank Blaschka }
13358cba80c3SFrank Blaschka
13368cba80c3SFrank Blaschka static const TypeInfo s390_pcihost_info = {
13378cba80c3SFrank Blaschka .name = TYPE_S390_PCI_HOST_BRIDGE,
13388cba80c3SFrank Blaschka .parent = TYPE_PCI_HOST_BRIDGE,
13398cba80c3SFrank Blaschka .instance_size = sizeof(S390pciState),
13408cba80c3SFrank Blaschka .class_init = s390_pcihost_class_init,
13418cba80c3SFrank Blaschka .interfaces = (InterfaceInfo[]) {
13428cba80c3SFrank Blaschka { TYPE_HOTPLUG_HANDLER },
13438cba80c3SFrank Blaschka { }
13448cba80c3SFrank Blaschka }
13458cba80c3SFrank Blaschka };
13468cba80c3SFrank Blaschka
134790a0f9afSYi Min Zhao static const TypeInfo s390_pcibus_info = {
134890a0f9afSYi Min Zhao .name = TYPE_S390_PCI_BUS,
134990a0f9afSYi Min Zhao .parent = TYPE_BUS,
135090a0f9afSYi Min Zhao .instance_size = sizeof(S390PCIBus),
135190a0f9afSYi Min Zhao };
135290a0f9afSYi Min Zhao
s390_pci_generate_uid(S390pciState * s)1353a975a24aSYi Min Zhao static uint16_t s390_pci_generate_uid(S390pciState *s)
13543e5cfba3SYi Min Zhao {
13553e5cfba3SYi Min Zhao uint16_t uid = 0;
13563e5cfba3SYi Min Zhao
13573e5cfba3SYi Min Zhao do {
13583e5cfba3SYi Min Zhao uid++;
1359a975a24aSYi Min Zhao if (!s390_pci_find_dev_by_uid(s, uid)) {
13603e5cfba3SYi Min Zhao return uid;
13613e5cfba3SYi Min Zhao }
13623e5cfba3SYi Min Zhao } while (uid < ZPCI_MAX_UID);
13633e5cfba3SYi Min Zhao
13643e5cfba3SYi Min Zhao return UID_UNDEFINED;
13653e5cfba3SYi Min Zhao }
13663e5cfba3SYi Min Zhao
s390_pci_generate_fid(S390pciState * s,Error ** errp)1367a975a24aSYi Min Zhao static uint32_t s390_pci_generate_fid(S390pciState *s, Error **errp)
13683e5cfba3SYi Min Zhao {
13693e5cfba3SYi Min Zhao uint32_t fid = 0;
13703e5cfba3SYi Min Zhao
137135b6e94bSPeter Maydell do {
1372a975a24aSYi Min Zhao if (!s390_pci_find_dev_by_fid(s, fid)) {
13733e5cfba3SYi Min Zhao return fid;
13743e5cfba3SYi Min Zhao }
137535b6e94bSPeter Maydell } while (fid++ != ZPCI_MAX_FID);
13763e5cfba3SYi Min Zhao
13773e5cfba3SYi Min Zhao error_setg(errp, "no free fid could be found");
13783e5cfba3SYi Min Zhao return 0;
13793e5cfba3SYi Min Zhao }
13803e5cfba3SYi Min Zhao
s390_pci_device_realize(DeviceState * dev,Error ** errp)13813e5cfba3SYi Min Zhao static void s390_pci_device_realize(DeviceState *dev, Error **errp)
13823e5cfba3SYi Min Zhao {
13833e5cfba3SYi Min Zhao S390PCIBusDevice *zpci = S390_PCI_DEVICE(dev);
1384a975a24aSYi Min Zhao S390pciState *s = s390_get_phb();
13853e5cfba3SYi Min Zhao
13863e5cfba3SYi Min Zhao if (!zpci->target) {
13873e5cfba3SYi Min Zhao error_setg(errp, "target must be defined");
13883e5cfba3SYi Min Zhao return;
13893e5cfba3SYi Min Zhao }
13903e5cfba3SYi Min Zhao
1391a975a24aSYi Min Zhao if (s390_pci_find_dev_by_target(s, zpci->target)) {
13923e5cfba3SYi Min Zhao error_setg(errp, "target %s already has an associated zpci device",
13933e5cfba3SYi Min Zhao zpci->target);
13943e5cfba3SYi Min Zhao return;
13953e5cfba3SYi Min Zhao }
13963e5cfba3SYi Min Zhao
13973e5cfba3SYi Min Zhao if (zpci->uid == UID_UNDEFINED) {
1398a975a24aSYi Min Zhao zpci->uid = s390_pci_generate_uid(s);
13993e5cfba3SYi Min Zhao if (!zpci->uid) {
14003e5cfba3SYi Min Zhao error_setg(errp, "no free uid could be found");
14013e5cfba3SYi Min Zhao return;
14023e5cfba3SYi Min Zhao }
1403a975a24aSYi Min Zhao } else if (s390_pci_find_dev_by_uid(s, zpci->uid)) {
14043e5cfba3SYi Min Zhao error_setg(errp, "uid %u already in use", zpci->uid);
14053e5cfba3SYi Min Zhao return;
14063e5cfba3SYi Min Zhao }
14073e5cfba3SYi Min Zhao
14083e5cfba3SYi Min Zhao if (!zpci->fid_defined) {
14093e5cfba3SYi Min Zhao Error *local_error = NULL;
14103e5cfba3SYi Min Zhao
1411a975a24aSYi Min Zhao zpci->fid = s390_pci_generate_fid(s, &local_error);
14123e5cfba3SYi Min Zhao if (local_error) {
14133e5cfba3SYi Min Zhao error_propagate(errp, local_error);
14143e5cfba3SYi Min Zhao return;
14153e5cfba3SYi Min Zhao }
1416a975a24aSYi Min Zhao } else if (s390_pci_find_dev_by_fid(s, zpci->fid)) {
14173e5cfba3SYi Min Zhao error_setg(errp, "fid %u already in use", zpci->fid);
14183e5cfba3SYi Min Zhao return;
14193e5cfba3SYi Min Zhao }
14203e5cfba3SYi Min Zhao
14213e5cfba3SYi Min Zhao zpci->state = ZPCI_FS_RESERVED;
14226e92c70cSYi Min Zhao zpci->fmb.format = ZPCI_FMB_FORMAT;
14233e5cfba3SYi Min Zhao }
14243e5cfba3SYi Min Zhao
s390_pci_device_reset(DeviceState * dev)14253e5cfba3SYi Min Zhao static void s390_pci_device_reset(DeviceState *dev)
14263e5cfba3SYi Min Zhao {
14273e5cfba3SYi Min Zhao S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);
14283e5cfba3SYi Min Zhao
14293e5cfba3SYi Min Zhao switch (pbdev->state) {
14303e5cfba3SYi Min Zhao case ZPCI_FS_RESERVED:
14313e5cfba3SYi Min Zhao return;
14323e5cfba3SYi Min Zhao case ZPCI_FS_STANDBY:
14333e5cfba3SYi Min Zhao break;
14343e5cfba3SYi Min Zhao default:
14353e5cfba3SYi Min Zhao pbdev->fh &= ~FH_MASK_ENABLE;
14363e5cfba3SYi Min Zhao pbdev->state = ZPCI_FS_DISABLED;
14373e5cfba3SYi Min Zhao break;
14383e5cfba3SYi Min Zhao }
14393e5cfba3SYi Min Zhao
1440d0bc7091SMatthew Rosato if (pbdev->interp && (pbdev->fh & FH_MASK_ENABLE)) {
1441d0bc7091SMatthew Rosato /* Interpreted devices were using interrupt forwarding */
1442d0bc7091SMatthew Rosato s390_pci_kvm_aif_disable(pbdev);
1443d0bc7091SMatthew Rosato } else if (pbdev->summary_ind) {
14443e5cfba3SYi Min Zhao pci_dereg_irqs(pbdev);
14453e5cfba3SYi Min Zhao }
1446de91ea92SYi Min Zhao if (pbdev->iommu->enabled) {
1447de91ea92SYi Min Zhao pci_dereg_ioat(pbdev->iommu);
14483e5cfba3SYi Min Zhao }
14493e5cfba3SYi Min Zhao
14506e92c70cSYi Min Zhao fmb_timer_free(pbdev);
14513e5cfba3SYi Min Zhao }
14523e5cfba3SYi Min Zhao
s390_pci_get_fid(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)14533e5cfba3SYi Min Zhao static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name,
14543e5cfba3SYi Min Zhao void *opaque, Error **errp)
14553e5cfba3SYi Min Zhao {
14563e5cfba3SYi Min Zhao Property *prop = opaque;
14571e198715SEduardo Habkost uint32_t *ptr = object_field_prop_ptr(obj, prop);
14583e5cfba3SYi Min Zhao
14593e5cfba3SYi Min Zhao visit_type_uint32(v, name, ptr, errp);
14603e5cfba3SYi Min Zhao }
14613e5cfba3SYi Min Zhao
s390_pci_set_fid(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)14623e5cfba3SYi Min Zhao static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name,
14633e5cfba3SYi Min Zhao void *opaque, Error **errp)
14643e5cfba3SYi Min Zhao {
14653e5cfba3SYi Min Zhao S390PCIBusDevice *zpci = S390_PCI_DEVICE(obj);
14663e5cfba3SYi Min Zhao Property *prop = opaque;
14671e198715SEduardo Habkost uint32_t *ptr = object_field_prop_ptr(obj, prop);
14683e5cfba3SYi Min Zhao
14695af3a056SMarkus Armbruster if (!visit_type_uint32(v, name, ptr, errp)) {
14705af3a056SMarkus Armbruster return;
14715af3a056SMarkus Armbruster }
14723e5cfba3SYi Min Zhao zpci->fid_defined = true;
14733e5cfba3SYi Min Zhao }
14743e5cfba3SYi Min Zhao
14751b6b7d10SFam Zheng static const PropertyInfo s390_pci_fid_propinfo = {
14763e5cfba3SYi Min Zhao .name = "zpci_fid",
14773e5cfba3SYi Min Zhao .get = s390_pci_get_fid,
14783e5cfba3SYi Min Zhao .set = s390_pci_set_fid,
14793e5cfba3SYi Min Zhao };
14803e5cfba3SYi Min Zhao
14813e5cfba3SYi Min Zhao #define DEFINE_PROP_S390_PCI_FID(_n, _s, _f) \
14823e5cfba3SYi Min Zhao DEFINE_PROP(_n, _s, _f, s390_pci_fid_propinfo, uint32_t)
14833e5cfba3SYi Min Zhao
14843e5cfba3SYi Min Zhao static Property s390_pci_device_properties[] = {
14853e5cfba3SYi Min Zhao DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED),
14863e5cfba3SYi Min Zhao DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid),
14873e5cfba3SYi Min Zhao DEFINE_PROP_STRING("target", S390PCIBusDevice, target),
1488dd1d5fd9SMatthew Rosato DEFINE_PROP_BOOL("interpret", S390PCIBusDevice, interp, true),
1489d0bc7091SMatthew Rosato DEFINE_PROP_BOOL("forwarding-assist", S390PCIBusDevice, forwarding_assist,
1490d0bc7091SMatthew Rosato true),
14913e5cfba3SYi Min Zhao DEFINE_PROP_END_OF_LIST(),
14923e5cfba3SYi Min Zhao };
14933e5cfba3SYi Min Zhao
1494aede5d5dSCornelia Huck static const VMStateDescription s390_pci_device_vmstate = {
1495aede5d5dSCornelia Huck .name = TYPE_S390_PCI_DEVICE,
1496aede5d5dSCornelia Huck /*
1497aede5d5dSCornelia Huck * TODO: add state handling here, so migration works at least with
1498aede5d5dSCornelia Huck * emulated pci devices on s390x
1499aede5d5dSCornelia Huck */
1500aede5d5dSCornelia Huck .unmigratable = 1,
1501aede5d5dSCornelia Huck };
1502aede5d5dSCornelia Huck
s390_pci_device_class_init(ObjectClass * klass,void * data)15033e5cfba3SYi Min Zhao static void s390_pci_device_class_init(ObjectClass *klass, void *data)
15043e5cfba3SYi Min Zhao {
15053e5cfba3SYi Min Zhao DeviceClass *dc = DEVICE_CLASS(klass);
15063e5cfba3SYi Min Zhao
15073e5cfba3SYi Min Zhao dc->desc = "zpci device";
1508bd2aef10SCornelia Huck set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1509*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, s390_pci_device_reset);
15103e5cfba3SYi Min Zhao dc->bus_type = TYPE_S390_PCI_BUS;
15113e5cfba3SYi Min Zhao dc->realize = s390_pci_device_realize;
15124f67d30bSMarc-André Lureau device_class_set_props(dc, s390_pci_device_properties);
1513aede5d5dSCornelia Huck dc->vmsd = &s390_pci_device_vmstate;
15143e5cfba3SYi Min Zhao }
15153e5cfba3SYi Min Zhao
15163e5cfba3SYi Min Zhao static const TypeInfo s390_pci_device_info = {
15173e5cfba3SYi Min Zhao .name = TYPE_S390_PCI_DEVICE,
15183e5cfba3SYi Min Zhao .parent = TYPE_DEVICE,
15193e5cfba3SYi Min Zhao .instance_size = sizeof(S390PCIBusDevice),
15203e5cfba3SYi Min Zhao .class_init = s390_pci_device_class_init,
15213e5cfba3SYi Min Zhao };
15223e5cfba3SYi Min Zhao
15235e78c98bSBernhard Beschow static const TypeInfo s390_pci_iommu_info = {
1524de91ea92SYi Min Zhao .name = TYPE_S390_PCI_IOMMU,
1525de91ea92SYi Min Zhao .parent = TYPE_OBJECT,
1526de91ea92SYi Min Zhao .instance_size = sizeof(S390PCIIOMMU),
1527de91ea92SYi Min Zhao };
1528de91ea92SYi Min Zhao
s390_iommu_memory_region_class_init(ObjectClass * klass,void * data)15291221a474SAlexey Kardashevskiy static void s390_iommu_memory_region_class_init(ObjectClass *klass, void *data)
15301221a474SAlexey Kardashevskiy {
15311221a474SAlexey Kardashevskiy IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
15321221a474SAlexey Kardashevskiy
15331221a474SAlexey Kardashevskiy imrc->translate = s390_translate_iommu;
15346c5e7402SYi Min Zhao imrc->replay = s390_pci_iommu_replay;
15351221a474SAlexey Kardashevskiy }
15361221a474SAlexey Kardashevskiy
15371221a474SAlexey Kardashevskiy static const TypeInfo s390_iommu_memory_region_info = {
15381221a474SAlexey Kardashevskiy .parent = TYPE_IOMMU_MEMORY_REGION,
15391221a474SAlexey Kardashevskiy .name = TYPE_S390_IOMMU_MEMORY_REGION,
15401221a474SAlexey Kardashevskiy .class_init = s390_iommu_memory_region_class_init,
15411221a474SAlexey Kardashevskiy };
15421221a474SAlexey Kardashevskiy
s390_pci_register_types(void)15438cba80c3SFrank Blaschka static void s390_pci_register_types(void)
15448cba80c3SFrank Blaschka {
15458cba80c3SFrank Blaschka type_register_static(&s390_pcihost_info);
154690a0f9afSYi Min Zhao type_register_static(&s390_pcibus_info);
15473e5cfba3SYi Min Zhao type_register_static(&s390_pci_device_info);
1548de91ea92SYi Min Zhao type_register_static(&s390_pci_iommu_info);
15491221a474SAlexey Kardashevskiy type_register_static(&s390_iommu_memory_region_info);
15508cba80c3SFrank Blaschka }
15518cba80c3SFrank Blaschka
15528cba80c3SFrank Blaschka type_init(s390_pci_register_types)
1553