[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v6 3/7] xen/arm: setup MMIO range trap handlers for hardware domain
Hi, Julien! On 22.11.21 19:36, Julien Grall wrote: > Hi, > > On 18/11/2021 10:46, Oleksandr Andrushchenko wrote: >> On 18.11.21 09:27, Oleksandr Andrushchenko wrote: >>> Hi, Julien! >>> >>> On 16.11.21 21:12, Julien Grall wrote: >>>> Hi Oleksandr, >>>> >>>> On 05/11/2021 06:33, Oleksandr Andrushchenko wrote: >>>>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx> >>>>> >>>>> 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@xxxxxxxx> >>>>> --- >>>>> Since v5: >>>>> - add vpci_sbdf_from_gpa helper for gpa to SBDF translation >>>>> - take bridge's bus start into account while calculating SBDF >>>>> Since v4: >>>>> - unsigned int for functions working with count >>>>> - gate number of MMIO handlers needed for CONFIG_HAS_PCI_MSI >>>>> and fix their number, e.g. single handler for PBA and >>>>> MSI-X tables (Roger) >>>>> - re-work code for assigning MMIO handlers to be simpler >>>>> and account on the fact that there could multiple host bridges >>>>> exist for the hwdom >>>>> Since v3: >>>>> - fixed comment formatting >>>>> Since v2: >>>>> - removed unneeded assignment (count = 0) >>>>> - removed unneeded header inclusion >>>>> - update commit message >>>>> Since v1: >>>>> - Dynamically calculate the number of MMIO handlers required for vPCI >>>>> and update the total number accordingly >>>>> - s/clb/cb >>>>> - Do not introduce a new callback for MMIO handler setup >>>>> --- >>>>> xen/arch/arm/domain.c | 2 + >>>>> xen/arch/arm/pci/pci-host-common.c | 27 ++++++++++++ >>>>> xen/arch/arm/vpci.c | 66 ++++++++++++++++++++++++++---- >>>>> xen/arch/arm/vpci.h | 6 +++ >>>>> xen/include/asm-arm/pci.h | 5 +++ >>>>> 5 files changed, 98 insertions(+), 8 deletions(-) >>>>> >>>>> 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 47104b22b221..0d271a6e8881 100644 >>>>> --- a/xen/arch/arm/pci/pci-host-common.c >>>>> +++ b/xen/arch/arm/pci/pci-host-common.c >>>>> @@ -289,6 +289,33 @@ int pci_get_host_bridge_segment(const struct >>>>> dt_device_node *node, >>>>> return -EINVAL; >>>>> } >>>>> +int pci_host_iterate_bridges(struct domain *d, >>>>> + int (*cb)(struct domain *d, >>>>> + struct pci_host_bridge *bridge)) >>>>> +{ >>>>> + struct pci_host_bridge *bridge; >>>>> + int err; >>>>> + >>>>> + list_for_each_entry( bridge, &pci_host_bridges, node ) >>>>> + { >>>>> + err = cb(d, bridge); >>>>> + if ( err ) >>>>> + return err; >>>>> + } >>>>> + return 0; >>>>> +} >>>>> + >>>>> +unsigned int pci_host_get_num_bridges(void) >>>>> +{ >>>>> + struct pci_host_bridge *bridge; >>>>> + unsigned int count = 0; >>>> How about making this static and... >>>> >>>>> + >>>>> + list_for_each_entry( bridge, &pci_host_bridges, node ) >>>>> + count++; >>>> ... only call list_for_each_entry() when count is -1? So we would only go >>>> through the list once. >>>> >>>> This should be fine given hostbridge can only be added during boot (we >>>> would need to protect pci_host_bridges with a lock otherwise). >>> Ok, I can do that >> I have re-worked the patch so that more code can be re-used: >> >> if ( is_hardware_domain(d) ) >> { >> int count; >> >> count = pci_host_iterate_bridges_and_count(d, >> vpci_setup_mmio_handler_cb); >> if ( count < 0 ) >> return count; >> >> return 0; >> } >> >> register_mmio_handler(d, &vpci_mmio_handler, >> GUEST_VPCI_ECAM_BASE, GUEST_VPCI_ECAM_SIZE, >> NULL); >> >> so pci_host_get_num_bridges goes away. > > If this will be the only caller that needs to know the number hostbridges, > then I am happy with this appropach. Otherwise, I would prefer to keep the > helper pci_host_get_num_bridges(). pci_host_get_num_bridges won't be needed, so it is ok to not introduce pci_host_get_num_bridges > >>>>> + >>>>> + return count; >>>>> +} >>>>> + >>>>> /* >>>>> * Local variables: >>>>> * mode: C >>>>> diff --git a/xen/arch/arm/vpci.c b/xen/arch/arm/vpci.c >>>>> index 23f45386f4b3..5a6ebd8b9868 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,17 +68,54 @@ 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); >>>>> + return 0; >>>>> +} >>>>> + >>>>> int domain_vpci_init(struct domain *d) >>>>> { >>>>> if ( !has_vpci(d) ) >>>>> return 0; >>>>> + if ( is_hardware_domain(d) ) >>>>> + return pci_host_iterate_bridges(d, vpci_setup_mmio_handler_cb); >>>>> + >>>>> + /* Guest domains use what is programmed in their device tree. */ >>>> I would rather not make the assumption that the guest is using a >>>> Device-Tree. So how about: >>>> >>>> /* >>>> * The hardware domain gets one virtual hostbridge by "real" >>>> * hostbridges. >>>> * Guests get the virtual platform layout (one virtual host bridge for >>>> * now). >>>> */ >>>> >>>> The comment would have to be moved before if ( is_hardware_domain(d) ). >>> Sure, I can extend the comment >> /* >> * 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. >> */ > > LGTM. > >> >>>>> register_mmio_handler(d, &vpci_mmio_handler, >>>>> GUEST_VPCI_ECAM_BASE, GUEST_VPCI_ECAM_SIZE, >>>>> NULL); >>>>> return 0; >>>>> } >>>>> +unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d) >>>> AFAICT, this function would also be called even if vPCI is not enabled for >>>> the domain. So we should add: >>>> >>>> if ( !has_vpci(d) ) >>>> return 0; >>>> >>> Good catch, will add >> Hm... but we have >> >> static inline unsigned int domain_vpci_get_num_mmio_handlers(struct domain >> *d) >> +{ >> + return 0; >> +} >> fir that case > > This would only cover the case where Xen was built without vPCI support. When > Xen is built with vPCI support, we only want to increase the number of > regions for domain with vPCI enabled. Yes, you are right, I will add the check > >> >>>>> +{ >>>>> + unsigned int count; >>>>> + >>>>> + if ( is_hardware_domain(d) ) >>>>> + /* For each PCI host bridge's configuration space. */ >>>>> + count = pci_host_get_num_bridges(); >>>> This first part makes sense to me. But... >>>> >>>>> + else >>>> ... I don't understand how the else is related to this commit. Can you >>>> clarify it? >>>> >>>>> + /* >>>>> + * There's a single MSI-X MMIO handler that deals with both PBA >>>>> + * and MSI-X tables per each PCI device being passed through. >>>>> + * Maximum number of supported devices is 32 as virtual bus >>>>> + * topology emulates the devices as embedded endpoints. >>>>> + * +1 for a single emulated host bridge's configuration space. >>>>> + */ >>>>> + count = 1; >>>>> +#ifdef CONFIG_HAS_PCI_MSI >>>>> + count += 32; >>>> Surely, this is a decision that is based on other factor in the vPCI code. >>>> So can use a define and avoid hardcoding the number? >>> Well, in the later series [1] this is defined via PCI_SLOT(~0) + 1 and >>> there is no dedicated >>> constant for that. I can use the same here, e.g. s/32/PCI_SLOT(~0) + 1 > > I would prefer if we introduce a new constant for that. This makes easier to > update the code if we decide to increase the number of virtual devices. > > However, I am still not sure how the 'else' part is related to this commit. > Can you please clarify it? Well, yes, this is too early for this patch to introduce some future knowledge, so I'll instead have: 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; } /* * This is a guest domain: * * 1 for a single emulated host bridge's configuration space. */ return 1; } > > Cheers, > Thank you, Oleksandr
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |