Message ID | 20211209072918.460902-3-andr2000@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | PCI devices passthrough on Arm, part 2 | expand |
Hi Oleksandr, > On 9 Dec 2021, at 7:29 am, Oleksandr Andrushchenko <andr2000@gmail.com> wrote: > > From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> > > In order for vPCI to work it needs to maintain guest and hardware > domain's views of the configuration space. For example, BARs and > COMMAND registers require emulation for guests and the guest view > of the registers needs to be in sync with the real contents of the > relevant registers. For that ECAM address space needs to also be > trapped for the hardware domain, so we need to implement PCI host > bridge specific callbacks to properly setup MMIO handlers for those > ranges depending on particular host bridge implementation. > > Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> Reviewed-by: Rahul Singh <rahul.singh@arm.com> Tested-by: Rahul Singh <rahul.singh@arm.com> Regards, Rahul
Hi Oleksandr, On 09/12/2021 07:29, Oleksandr Andrushchenko wrote: > +unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d) > +{ > + if ( !has_vpci(d) ) > + return 0; > + > + if ( is_hardware_domain(d) ) > + { > + int ret = pci_host_iterate_bridges_and_count(d, vpci_get_num_handlers_cb); > + > + return ret < 0 ? 0 : ret; Sorry I only spotted this now. AFAICT, ret is not meant to return ret < 0 in this case. But if it were then I think it would be wrong to continue as nothing happened because the code will likely fall over/crash when registering the I/O handlers. I would document this oddity with if ( ret < 0 ) { ASSERT_UNREACHABLE(); return 0; } I can do the change on commit if the others are happy with it. Cheers,
Hi, Julien! On 10.12.21 19:52, Julien Grall wrote: > Hi Oleksandr, > > On 09/12/2021 07:29, Oleksandr Andrushchenko wrote: >> +unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d) >> +{ >> + if ( !has_vpci(d) ) >> + return 0; >> + >> + if ( is_hardware_domain(d) ) >> + { >> + int ret = pci_host_iterate_bridges_and_count(d, vpci_get_num_handlers_cb); >> + >> + return ret < 0 ? 0 : ret; > > Sorry I only spotted this now. AFAICT, ret is not meant to return ret < 0 in this case. But if it were then I think it would be wrong to continue as nothing happened because the code will likely fall over/crash when registering the I/O handlers. > > I would document this oddity with > > if ( ret < 0 ) > { > ASSERT_UNREACHABLE(); > return 0; > } > > I can do the change on commit if the others are happy with it. Yes, please, do me a favor > > Cheers, > Thank you, Oleksandr
On 10/12/2021 18:37, Oleksandr Andrushchenko wrote: > Hi, Julien! Hello, > On 10.12.21 19:52, Julien Grall wrote: >> Hi Oleksandr, >> >> On 09/12/2021 07:29, Oleksandr Andrushchenko wrote: >>> +unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d) >>> +{ >>> + if ( !has_vpci(d) ) >>> + return 0; >>> + >>> + if ( is_hardware_domain(d) ) >>> + { >>> + int ret = pci_host_iterate_bridges_and_count(d, vpci_get_num_handlers_cb); >>> + >>> + return ret < 0 ? 0 : ret; >> >> Sorry I only spotted this now. AFAICT, ret is not meant to return ret < 0 in this case. But if it were then I think it would be wrong to continue as nothing happened because the code will likely fall over/crash when registering the I/O handlers. >> >> I would document this oddity with >> >> if ( ret < 0 ) >> { >> ASSERT_UNREACHABLE(); >> return 0; >> } >> >> I can do the change on commit if the others are happy with it. > Yes, please, do me a favor Ok. With that: Acked-by: Julien Grall <jgrall@amazon.com> Cheers, >> >> Cheers, >> > Thank you, > Oleksandr
diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c index 96e1b235501d..92a6c509e5c5 100644 --- a/xen/arch/arm/domain.c +++ b/xen/arch/arm/domain.c @@ -739,6 +739,8 @@ int arch_domain_create(struct domain *d, if ( (rc = domain_vgic_register(d, &count)) != 0 ) goto fail; + count += domain_vpci_get_num_mmio_handlers(d); + if ( (rc = domain_io_init(d, count + MAX_IO_HANDLER)) != 0 ) goto fail; diff --git a/xen/arch/arm/pci/pci-host-common.c b/xen/arch/arm/pci/pci-host-common.c index 40e779b5d803..84aab371cd9a 100644 --- a/xen/arch/arm/pci/pci-host-common.c +++ b/xen/arch/arm/pci/pci-host-common.c @@ -294,6 +294,25 @@ int pci_get_host_bridge_segment(const struct dt_device_node *node, return -EINVAL; } +int pci_host_iterate_bridges_and_count(struct domain *d, + int (*cb)(struct domain *d, + struct pci_host_bridge *bridge)) +{ + struct pci_host_bridge *bridge; + int count = 0; + + list_for_each_entry( bridge, &pci_host_bridges, node ) + { + int ret; + + ret = cb(d, bridge); + if ( ret < 0 ) + return ret; + count += ret; + } + return count; +} + /* * Local variables: * mode: C diff --git a/xen/arch/arm/vpci.c b/xen/arch/arm/vpci.c index 23f45386f4b3..1564448c6c8d 100644 --- a/xen/arch/arm/vpci.c +++ b/xen/arch/arm/vpci.c @@ -16,16 +16,31 @@ #include <asm/mmio.h> +static pci_sbdf_t vpci_sbdf_from_gpa(const struct pci_host_bridge *bridge, + paddr_t gpa) +{ + pci_sbdf_t sbdf; + + if ( bridge ) + { + sbdf.sbdf = VPCI_ECAM_BDF(gpa - bridge->cfg->phys_addr); + sbdf.seg = bridge->segment; + sbdf.bus += bridge->cfg->busn_start; + } + else + sbdf.sbdf = VPCI_ECAM_BDF(gpa - GUEST_VPCI_ECAM_BASE); + + return sbdf; +} + static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info, register_t *r, void *p) { - pci_sbdf_t sbdf; + struct pci_host_bridge *bridge = p; + pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa); /* data is needed to prevent a pointer cast on 32bit */ unsigned long data; - /* We ignore segment part and always handle segment 0 */ - sbdf.sbdf = VPCI_ECAM_BDF(info->gpa - GUEST_VPCI_ECAM_BASE); - if ( vpci_ecam_read(sbdf, ECAM_REG_OFFSET(info->gpa), 1U << info->dabt.size, &data) ) { @@ -41,10 +56,8 @@ static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info, static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info, register_t r, void *p) { - pci_sbdf_t sbdf; - - /* We ignore segment part and always handle segment 0 */ - sbdf.sbdf = VPCI_ECAM_BDF(info->gpa - GUEST_VPCI_ECAM_BASE); + struct pci_host_bridge *bridge = p; + pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa); return vpci_ecam_write(sbdf, ECAM_REG_OFFSET(info->gpa), 1U << info->dabt.size, r); @@ -55,13 +68,61 @@ static const struct mmio_handler_ops vpci_mmio_handler = { .write = vpci_mmio_write, }; +static int vpci_setup_mmio_handler_cb(struct domain *d, + struct pci_host_bridge *bridge) +{ + struct pci_config_window *cfg = bridge->cfg; + + register_mmio_handler(d, &vpci_mmio_handler, + cfg->phys_addr, cfg->size, bridge); + + /* We have registered a single MMIO handler. */ + return 1; +} + int domain_vpci_init(struct domain *d) { if ( !has_vpci(d) ) return 0; - register_mmio_handler(d, &vpci_mmio_handler, - GUEST_VPCI_ECAM_BASE, GUEST_VPCI_ECAM_SIZE, NULL); + /* + * The hardware domain gets as many MMIOs as required by the + * physical host bridge. + * Guests get the virtual platform layout: one virtual host bridge for now. + */ + if ( is_hardware_domain(d) ) + { + int ret; + + ret = pci_host_iterate_bridges_and_count(d, vpci_setup_mmio_handler_cb); + if ( ret < 0 ) + return ret; + } + else + register_mmio_handler(d, &vpci_mmio_handler, + GUEST_VPCI_ECAM_BASE, GUEST_VPCI_ECAM_SIZE, NULL); + + return 0; +} + +static int vpci_get_num_handlers_cb(struct domain *d, + struct pci_host_bridge *bridge) +{ + /* Each bridge has a single MMIO handler for the configuration space. */ + return 1; +} + +unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d) +{ + if ( !has_vpci(d) ) + return 0; + + if ( is_hardware_domain(d) ) + { + int ret = pci_host_iterate_bridges_and_count(d, vpci_get_num_handlers_cb); + + return ret < 0 ? 0 : ret; + } return 0; } diff --git a/xen/arch/arm/vpci.h b/xen/arch/arm/vpci.h index d8a7b0e3e802..3c713f3fcdb5 100644 --- a/xen/arch/arm/vpci.h +++ b/xen/arch/arm/vpci.h @@ -17,11 +17,17 @@ #ifdef CONFIG_HAS_VPCI int domain_vpci_init(struct domain *d); +unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d); #else static inline int domain_vpci_init(struct domain *d) { return 0; } + +static inline unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d) +{ + return 0; +} #endif #endif /* __ARCH_ARM_VPCI_H__ */ diff --git a/xen/include/asm-arm/pci.h b/xen/include/asm-arm/pci.h index c313423cdcb2..94f003a07ca2 100644 --- a/xen/include/asm-arm/pci.h +++ b/xen/include/asm-arm/pci.h @@ -111,6 +111,10 @@ void arch_pci_init_pdev(struct pci_dev *pdev); int pci_get_new_domain_nr(void); +int pci_host_iterate_bridges_and_count(struct domain *d, + int (*cb)(struct domain *d, + struct pci_host_bridge *bridge)); + #else /*!CONFIG_HAS_PCI*/ struct arch_pci_dev { };