@@ -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;
@@ -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);
@@ -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 )
{
@@ -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