[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v7 08/12] vpci/header: emulate PCI_COMMAND register for guests
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. 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 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> --- lorc's rebase: check for the logic after rebase 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 | 2 ++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c index 0524fbd220..677b93226c 100644 --- a/xen/drivers/vpci/header.c +++ b/xen/drivers/vpci/header.c @@ -465,11 +465,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; + + 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. @@ -486,6 +502,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); +} + static void cf_check bar_write( const struct pci_dev *pdev, unsigned int reg, uint32_t val, void *data) { @@ -692,8 +721,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); + /* 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); if ( rc ) return rc; diff --git a/xen/drivers/vpci/msi.c b/xen/drivers/vpci/msi.c index e7d1f916a0..687aef54d1 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 b5a7dfdf9c..e4b957d5f3 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); } else if ( !new_enabled && msix->enabled ) { diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h index 1e42a59c1d..fe100a8cb7 100644 --- a/xen/include/xen/vpci.h +++ b/xen/include/xen/vpci.h @@ -92,6 +92,8 @@ struct vpci { /* Offset to the ROM BAR register if any. */ unsigned int rom_reg; + /* Guest view of the PCI_COMMAND register. */ + uint16_t guest_cmd; /* * Store whether the ROM enable bit is set (doesn't imply ROM BAR -- 2.40.1
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |