[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v8 09/13] vpci/header: emulate PCI_COMMAND register for guests
On Thu, Jul 20, 2023 at 12:32:33AM +0000, Volodymyr Babchuk wrote: > From: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx> > > Xen and/or Dom0 may have put values in PCI_COMMAND which they expect > to remain unaltered. PCI_COMMAND_SERR bit is a good example: while the > guest's view of this will want to be zero initially, the host having set > it to 1 may not easily be overwritten with 0, or else we'd effectively > imply giving the guest control of the bit. Thus, PCI_COMMAND register needs > proper emulation in order to honor host's settings. You speak about SERR here, yet in the code all bits are togglable by domUs. > There are examples of emulators [1], [2] which already deal with PCI_COMMAND > register emulation and it seems that at most they care about is the only INTx ^ stray? > bit (besides IO/memory enable and bus master which are write through). > It could be because in order to properly emulate the PCI_COMMAND register > we need to know about the whole PCI topology, e.g. if any setting in device's > command register is aligned with the upstream port etc. > > This makes me think that because of this complexity others just ignore that. > Neither I think this can easily be done in Xen case. > > According to "PCI LOCAL BUS SPECIFICATION, REV. 3.0", section "6.2.2 > Device Control" the reset state of the command register is typically 0, > so when assigning a PCI device use 0 as the initial state for the guest's view > of the command register. > > For now our emulation only makes sure INTx is set according to the host > requirements, i.e. depending on MSI/MSI-X enabled state. > > This implementation and the decision to only emulate INTx bit for now > is based on the previous discussion at [3]. > > [1] https://github.com/qemu/qemu/blob/master/hw/xen/xen_pt_config_init.c#L310 > [2] > https://github.com/projectacrn/acrn-hypervisor/blob/master/hypervisor/hw/pci.c#L336 > [3] > https://patchwork.kernel.org/project/xen-devel/patch/20210903100831.177748-9-andr2000@xxxxxxxxx/ > > Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx> > --- > > Since v6: > - fold guest's logic into cmd_write > - implement cmd_read, so we can report emulated INTx state to guests > - introduce header->guest_cmd to hold the emulated state of the > PCI_COMMAND register for guests > Since v5: > - add additional check for MSI-X enabled while altering INTX bit > - make sure INTx disabled while guests enable MSI/MSI-X > Since v3: > - gate more code on CONFIG_HAS_MSI > - removed logic for the case when MSI/MSI-X not enabled > --- > xen/drivers/vpci/header.c | 38 +++++++++++++++++++++++++++++++++++++- > xen/drivers/vpci/msi.c | 4 ++++ > xen/drivers/vpci/msix.c | 4 ++++ > xen/include/xen/vpci.h | 3 +++ > 4 files changed, 48 insertions(+), 1 deletion(-) > > diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c > index e1a448b674..ae05d242a5 100644 > --- a/xen/drivers/vpci/header.c > +++ b/xen/drivers/vpci/header.c > @@ -486,11 +486,27 @@ static int modify_bars(const struct pci_dev *pdev, > uint16_t cmd, bool rom_only) > return 0; > } > > +/* TODO: Add proper emulation for all bits of the command register. */ > static void cf_check cmd_write( > const struct pci_dev *pdev, unsigned int reg, uint32_t cmd, void *data) > { > struct vpci_header *header = data; > > + if ( !is_hardware_domain(pdev->domain) ) > + { > + struct vpci_header *header = data; Why do you need this variable? You already have 'header' in the outer scope you can use here. > + > + header->guest_cmd = cmd; > +#ifdef CONFIG_HAS_PCI_MSI > + if ( pdev->vpci->msi->enabled || pdev->vpci->msix->enabled ) > + /* > + * Guest wants to enable INTx, but it can't be enabled > + * if MSI/MSI-X enabled. > + */ > + cmd |= PCI_COMMAND_INTX_DISABLE; > +#endif > + } > + > /* > * Let Dom0 play with all the bits directly except for the memory > * decoding one. This comments likely needs updating, to reflect that bits not allowed to domU are already masked. > @@ -507,6 +523,19 @@ static void cf_check cmd_write( > pci_conf_write16(pdev->sbdf, reg, cmd); > } > > +static uint32_t cmd_read(const struct pci_dev *pdev, unsigned int reg, > + void *data) > +{ > + if ( !is_hardware_domain(pdev->domain) ) > + { > + struct vpci_header *header = data; > + > + return header->guest_cmd; > + } > + > + return pci_conf_read16(pdev->sbdf, reg); Would IMO be simpler as: const struct vpci_header *header = data; return is_hardware_domain(pdev->domain) ? pci_conf_read16(pdev->sbdf, reg) : header->guest_cmd; In fact I wonder why not make this handler domU specific so that the hardware domain can continue to use vpci_hw_read16. > +} > + > static void cf_check bar_write( > const struct pci_dev *pdev, unsigned int reg, uint32_t val, void *data) > { > @@ -713,8 +742,15 @@ static int cf_check init_bars(struct pci_dev *pdev) > return -EOPNOTSUPP; > } > > + /* > + * According to "PCI LOCAL BUS SPECIFICATION, REV. 3.0", section "6.2.2 > + * Device Control" the reset state of the command register is > + * typically all 0's, so this is used as initial value for the guests. > + */ > + ASSERT(header->guest_cmd == 0); Hm, while that would be the expectation, shouldn't the command register reflect the current state of the hardware? I think you want to check 'cmd' so it's sane, and complain otherwise but propagate the value to the guest view. > + > /* Setup a handler for the command register. */ > - rc = vpci_add_register(pdev->vpci, vpci_hw_read16, cmd_write, > PCI_COMMAND, > + rc = vpci_add_register(pdev->vpci, cmd_read, cmd_write, PCI_COMMAND, > 2, header); See comment above about keeping the hw domain using vpci_hw_read16. > if ( rc ) > return rc; > diff --git a/xen/drivers/vpci/msi.c b/xen/drivers/vpci/msi.c > index e63152c224..c37845a949 100644 > --- a/xen/drivers/vpci/msi.c > +++ b/xen/drivers/vpci/msi.c > @@ -70,6 +70,10 @@ static void cf_check control_write( > > if ( vpci_msi_arch_enable(msi, pdev, vectors) ) > return; > + > + /* Make sure guest doesn't enable INTx while enabling MSI. */ > + if ( !is_hardware_domain(pdev->domain) ) > + pci_intx(pdev, false); > } > else > vpci_msi_arch_disable(msi, pdev); > diff --git a/xen/drivers/vpci/msix.c b/xen/drivers/vpci/msix.c > index 9481274579..eab1661b87 100644 > --- a/xen/drivers/vpci/msix.c > +++ b/xen/drivers/vpci/msix.c > @@ -97,6 +97,10 @@ static void cf_check control_write( > for ( i = 0; i < msix->max_entries; i++ ) > if ( !msix->entries[i].masked && msix->entries[i].updated ) > update_entry(&msix->entries[i], pdev, i); > + > + /* Make sure guest doesn't enable INTx while enabling MSI-X. */ > + if ( !is_hardware_domain(pdev->domain) ) > + pci_intx(pdev, false); I think here and in the MSI case you want to update the guest view of the command register if you unconditionally disable INTx. Maybe just use cmd_write() and let the logic there cache the new value? Thanks, Roger.
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |