]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
core: Add basic BAR write emulation for physical PCI devices
authorJan Kiszka <jan.kiszka@siemens.com>
Sun, 17 May 2015 09:50:04 +0000 (11:50 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 22 May 2015 04:53:48 +0000 (06:53 +0200)
This enables cell to explore the size of PCI device resources by writing
1's to base address registers and then reading back which bits got
modified. We so far didn't support this because Linux in the root cell
already retrieved the sized before Jailhouse ran and other cell could
have been customized to use preconfigured information.

However, adding this features only increases the code by few ten lines
while making life for preexisting inmate OSes, including Linux,
significantly easier. Moreover, we will save some code again when
switching ivshmem's BAR emulation to this version.

Note that this does NOT allow cells to remap PCI device resources in
their address space. That would require more effort with at limited
benefits. Given that we preconfigure all BARs, neither Linux nor other
OSes have a need to change them. Any attempt to do so will simply have
no effect.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/include/jailhouse/pci.h
hypervisor/pci.c

index 5974d7ffa9b701062aff7067786e79fef2bcc6be..8d85293661afb32348fda778f4b1348afce958ee 100644 (file)
 # define PCI_STS_CAPS          (1 << 4)
 #define PCI_CFG_BAR            0x10
 # define PCI_BAR_64BIT         0x4
+#define PCI_CFG_BAR_END                0x27
+#define PCI_CFG_ROMBAR         0x30
+#define PCI_CFG_CAPS           0x34
 #define PCI_CFG_INT            0x3c
 
 #define PCI_CONFIG_HEADER_SIZE 0x40
 
+#define PCI_NUM_BARS           6
+
 #define PCI_DEV_CLASS_MEM      0x05
 
 #define PCI_CAP_MSI            0x05
@@ -117,6 +122,8 @@ struct pci_device {
        const struct jailhouse_pci_device *info;
        /** Owning cell. */
        struct cell *cell;
+       /** Shadow BAR */
+       u32 bar[PCI_NUM_BARS];
 
        /** Shadow state of MSI config space registers. */
        union pci_msi_registers msi_registers;
index f0763627573036502c2287e24b7131524d9e3b8a..0e55119107f1bb6fe8185c9fc59e51a322cebb9a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Jailhouse, a Linux-based partitioning hypervisor
  *
- * Copyright (c) Siemens AG, 2014
+ * Copyright (c) Siemens AG, 2014, 2015
  *
  * Authors:
  *  Ivan Kolchin <ivan.kolchin@siemens.com>
@@ -45,6 +45,7 @@ struct pci_cfg_control {
 static const struct pci_cfg_control endpoint_write[PCI_CONFIG_HEADER_SIZE] = {
        [0x04/4] = {PCI_CONFIG_ALLOW,  0xffffffff}, /* Command, Status */
        [0x0c/4] = {PCI_CONFIG_ALLOW,  0xff00ffff}, /* BIST, Lat., Cacheline */
+       [0x30/4] = {PCI_CONFIG_RDONLY, 0xffffffff}, /* ROM BAR */
        [0x3c/4] = {PCI_CONFIG_ALLOW,  0x000000ff}, /* Int Line */
 };
 
@@ -182,7 +183,7 @@ enum pci_access pci_cfg_read_moderate(struct pci_device *device, u16 address,
                                      unsigned int size, u32 *value)
 {
        const struct jailhouse_pci_capability *cap;
-       unsigned int cap_offs;
+       unsigned int bar_no, cap_offs;
 
        if (!device) {
                *value = -1;
@@ -192,6 +193,22 @@ enum pci_access pci_cfg_read_moderate(struct pci_device *device, u16 address,
        if (device->info->type == JAILHOUSE_PCI_TYPE_IVSHMEM)
                return pci_ivshmem_cfg_read(device, address, size, value);
 
+       /* Emulate BARs for physical devices */
+       if (device->info->type == JAILHOUSE_PCI_TYPE_DEVICE) {
+               /* Emulate BAR access, always returning the shadow value. */
+               if (address >= PCI_CFG_BAR && address <= PCI_CFG_BAR_END) {
+                       bar_no = (address - PCI_CFG_BAR) / 4;
+                       *value = device->bar[bar_no] >> ((address % 4) * 8);
+                       return PCI_ACCESS_DONE;
+               }
+
+               /* We do not expose ROMs. */
+               if (address >= PCI_CFG_ROMBAR && address < PCI_CFG_CAPS) {
+                       *value = 0;
+                       return PCI_ACCESS_DONE;
+               }
+       }
+
        if (address < PCI_CONFIG_HEADER_SIZE)
                return PCI_ACCESS_PERFORM;
 
@@ -244,7 +261,7 @@ enum pci_access pci_cfg_write_moderate(struct pci_device *device, u16 address,
        unsigned int bias_shift = (address % 4) * 8;
        u32 mask = BYTE_MASK(size) << bias_shift;
        struct pci_cfg_control cfg_control;
-       unsigned int cap_offs;
+       unsigned int bar_no, cap_offs;
 
        if (!device)
                return PCI_ACCESS_REJECT;
@@ -252,6 +269,18 @@ enum pci_access pci_cfg_write_moderate(struct pci_device *device, u16 address,
        if (device->info->type == JAILHOUSE_PCI_TYPE_IVSHMEM)
                return pci_ivshmem_cfg_write(device, address, size, value);
 
+       value <<= bias_shift;
+
+       /* Emulate BARs for physical devices */
+       if (device->info->type == JAILHOUSE_PCI_TYPE_DEVICE &&
+           address >= PCI_CFG_BAR && address <= PCI_CFG_BAR_END) {
+               bar_no = (address - PCI_CFG_BAR) / 4;
+               mask &= device->info->bar_mask[bar_no];
+               device->bar[bar_no] &= ~mask;
+               device->bar[bar_no] |= value & mask;
+               return PCI_ACCESS_DONE;
+       }
+
        if (address < PCI_CONFIG_HEADER_SIZE) {
                if (device->info->type == JAILHOUSE_PCI_TYPE_BRIDGE)
                        cfg_control = bridge_write[address / 4];
@@ -275,8 +304,6 @@ enum pci_access pci_cfg_write_moderate(struct pci_device *device, u16 address,
        if (!cap || !(cap->flags & JAILHOUSE_PCICAPS_WRITE))
                return PCI_ACCESS_REJECT;
 
-       value <<= bias_shift;
-
        cap_offs = address - cap->start;
        if (cap->id == PCI_CAP_MSI &&
            (cap_offs < 10 || (device->info->msi_64bits && cap_offs < 14))) {
@@ -548,12 +575,16 @@ static int pci_add_virtual_device(struct cell *cell, struct pci_device *device)
 
 static int pci_add_physical_device(struct cell *cell, struct pci_device *device)
 {
-       unsigned int size = device->info->msix_region_size;
+       unsigned int n, size = device->info->msix_region_size;
        int err;
 
        printk("Adding PCI device %02x:%02x.%x to cell \"%s\"\n",
               PCI_BDF_PARAMS(device->info->bdf), cell->config->name);
 
+       for (n = 0; n < PCI_NUM_BARS; n ++)
+               device->bar[n] = pci_read_config(device->info->bdf,
+                                                PCI_CFG_BAR + n * 4, 4);
+
        err = arch_pci_add_physical_device(cell, device);
 
        if (!err && device->info->msix_address) {