@@ -524,9 +524,21 @@ static void cf_check cmd_write(
{
struct vpci_header *header = data;
+ if ( !is_hardware_domain(pdev->domain) )
+ {
+ const struct vpci *vpci = pdev->vpci;
+
+ if ( (vpci->msi && vpci->msi->enabled) ||
+ (vpci->msix && vpci->msix->enabled) )
+ cmd |= PCI_COMMAND_INTX_DISABLE;
+
+ header->guest_cmd = cmd;
+ }
+
/*
* Let Dom0 play with all the bits directly except for the memory
- * decoding one.
+ * decoding one. Bits that are not allowed for DomU are already
+ * handled above and by the rsvdp_mask.
*/
if ( header->bars_mapped != !!(cmd & PCI_COMMAND_MEMORY) )
/*
@@ -540,6 +552,14 @@ static void cf_check cmd_write(
pci_conf_write16(pdev->sbdf, reg, cmd);
}
+static uint32_t cf_check guest_cmd_read(
+ const struct pci_dev *pdev, unsigned int reg, void *data)
+{
+ const struct vpci_header *header = data;
+
+ return header->guest_cmd;
+}
+
static void cf_check bar_write(
const struct pci_dev *pdev, unsigned int reg, uint32_t val, void *data)
{
@@ -754,9 +774,23 @@ static int cf_check init_header(struct pci_dev *pdev)
return -EOPNOTSUPP;
}
- /* Setup a handler for the command register. */
- rc = vpci_add_register(pdev->vpci, vpci_hw_read16, cmd_write, PCI_COMMAND,
- 2, header);
+ /*
+ * Setup a handler for the command register.
+ *
+ * TODO: If support for emulated bits is added, re-visit how to handle
+ * PCI_COMMAND_PARITY, PCI_COMMAND_SERR, and PCI_COMMAND_FAST_BACK.
+ */
+ rc = vpci_add_register_mask(pdev->vpci,
+ is_hwdom ? vpci_hw_read16 : guest_cmd_read,
+ cmd_write, PCI_COMMAND, 2, header, 0, 0,
+ is_hwdom ? 0
+ : PCI_COMMAND_RSVDP_MASK |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_PARITY |
+ PCI_COMMAND_WAIT |
+ PCI_COMMAND_SERR |
+ PCI_COMMAND_FAST_BACK,
+ 0);
if ( rc )
return rc;
@@ -836,9 +870,23 @@ static int cf_check init_header(struct pci_dev *pdev)
if ( pdev->ignore_bars )
return 0;
- /* Disable memory decoding before sizing. */
cmd = pci_conf_read16(pdev->sbdf, PCI_COMMAND);
- if ( cmd & PCI_COMMAND_MEMORY )
+
+ /*
+ * For DomUs, clear PCI_COMMAND_{MASTER,MEMORY,IO} and other
+ * DomU-controllable bits in PCI_COMMAND. Devices assigned to DomUs will
+ * start with memory decoding disabled, and modify_bars() will not be called
+ * at the end of this function.
+ */
+ if ( !is_hwdom )
+ cmd &= ~(PCI_COMMAND_VGA_PALETTE | PCI_COMMAND_INVALIDATE |
+ PCI_COMMAND_SPECIAL | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_IO);
+
+ header->guest_cmd = cmd;
+
+ /* Disable memory decoding before sizing. */
+ if ( !is_hwdom || (cmd & PCI_COMMAND_MEMORY) )
pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY);
for ( i = 0; i < num_bars; i++ )
@@ -57,6 +57,8 @@ static void cf_check control_write(
if ( new_enabled )
{
+ bool old_enabled = msi->enabled;
+
/*
* If the device is already enabled it means the number of
* enabled messages has changed. Disable and re-enable the
@@ -70,6 +72,13 @@ static void cf_check control_write(
if ( vpci_msi_arch_enable(msi, pdev, vectors) )
return;
+
+ /* Make sure domU doesn't enable INTx while enabling MSI. */
+ if ( !old_enabled && !is_hardware_domain(pdev->domain) )
+ {
+ pci_intx(pdev, false);
+ pdev->vpci->header.guest_cmd |= PCI_COMMAND_INTX_DISABLE;
+ }
}
else
vpci_msi_arch_disable(msi, pdev);
@@ -135,6 +135,13 @@ static void cf_check control_write(
}
}
+ /* Make sure domU doesn't enable INTx while enabling MSI-X. */
+ if ( new_enabled && !msix->enabled && !is_hardware_domain(pdev->domain) )
+ {
+ pci_intx(pdev, false);
+ pdev->vpci->header.guest_cmd |= PCI_COMMAND_INTX_DISABLE;
+ }
+
msix->masked = new_masked;
msix->enabled = new_enabled;
@@ -48,6 +48,7 @@
#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
+#define PCI_COMMAND_RSVDP_MASK 0xf800
#define PCI_STATUS 0x06 /* 16 bits */
#define PCI_STATUS_IMM_READY 0x01 /* Immediate Readiness */
@@ -107,6 +107,9 @@ struct vpci {
} bars[PCI_HEADER_NORMAL_NR_BARS + 1];
/* At most 6 BARS + 1 expansion ROM BAR. */
+ /* Guest (domU only) view of the PCI_COMMAND register. */
+ uint16_t guest_cmd;
+
/*
* Store whether the ROM enable bit is set (doesn't imply ROM BAR
* is mapped into guest p2m) if there's a ROM BAR on the device.