]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
core: Introduce PCI device state
authorJan Kiszka <jan.kiszka@siemens.com>
Tue, 29 Jul 2014 18:34:24 +0000 (20:34 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Mon, 4 Aug 2014 08:11:42 +0000 (10:11 +0200)
We will have to store a number of runtime state information for PCI
devices, specifically its owner. Allocate these states as an array
during cell creation and release them on cell destruction.

We can already use the structure to keep a reference to the cell the
device belongs to. This avoids having to pass this around over multiple
hops. It will also be used soon to encode runtime ownership by setting
or clearing the reference.

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

index b581b7cd898940884a5ac506fbdb7c9fa3509adb..47302576211b063bbd724414fd5227352ab86b28 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <jailhouse/control.h>
+#include <jailhouse/pci.h>
 #include <jailhouse/printk.h>
 #include <jailhouse/processor.h>
 #include <asm/apic.h>
@@ -38,10 +39,12 @@ int arch_cell_create(struct per_cpu *cpu_data, struct cell *cell)
                return err;
 
        err = vtd_cell_init(cell);
-       if (err) {
-               vmx_cell_exit(cell);
-               return err;
-       }
+       if (err)
+               goto error_vmx_exit;
+
+       err = pci_cell_init(cell);
+       if (err)
+               goto error_vtd_exit;
 
        ioapic_cell_init(cell);
        ioapic_root_cell_shrink(cell->config);
@@ -50,6 +53,12 @@ int arch_cell_create(struct per_cpu *cpu_data, struct cell *cell)
                system_config->platform_info.x86.pm_timer_address;
 
        return 0;
+
+error_vtd_exit:
+       vtd_cell_exit(cell);
+error_vmx_exit:
+       vmx_cell_exit(cell);
+       return err;
 }
 
 int arch_map_memory_region(struct cell *cell,
@@ -82,6 +91,7 @@ int arch_unmap_memory_region(struct cell *cell,
 void arch_cell_destroy(struct per_cpu *cpu_data, struct cell *cell)
 {
        ioapic_cell_exit(cell);
+       pci_cell_exit(cell);
        vtd_cell_exit(cell);
        vmx_cell_exit(cell);
 }
index 75ef6811f3a1ebc735c13f88f2c9d4be2284ff2f..b7f1ecbc7751b3c26606690f9d7c484c68e869a4 100644 (file)
@@ -18,6 +18,8 @@
 #include <jailhouse/cell-config.h>
 #include <jailhouse/hypercall.h>
 
+struct pci_device;
+
 /**
  * struct cell - cell-related state information
  * ...
@@ -47,6 +49,7 @@ struct cell {
 
        struct cell *next;
 
+       struct pci_device *pci_devices;
        u32 pci_addr_port_val;
 
        u32 ioapic_index_reg_val;
index 738d3ff3175532d77241a357d16d777e6d132a7c..00904c211e7fc9e5579f3157204e087add9dabf5 100644 (file)
@@ -105,7 +105,6 @@ static u32 get_rax_reg(struct registers *guest_regs, u8 size)
 /**
  * data_port_in_handler() - Handler for IN accesses to data port
  * @guest_regs:                Guest register set
- * @cell:              Issuing cell
  * @device:            Structure describing PCI device
  * @address:           Config space access address
  * @size:              Access size (1, 2 or 4 bytes)
@@ -113,15 +112,15 @@ static u32 get_rax_reg(struct registers *guest_regs, u8 size)
  * Return: 1 if handled successfully, -1 on access error
  */
 static int
-data_port_in_handler(struct registers *guest_regs, const struct cell *cell,
-                    const struct jailhouse_pci_device *device,
+data_port_in_handler(struct registers *guest_regs, struct pci_device *device,
                     u16 address, unsigned int size)
 {
        u32 reg_data;
 
-       if (pci_cfg_read_moderate(cell, device, address, size,
-                                 &reg_data) == PCI_ACCESS_PERFORM)
-               reg_data = arch_pci_read_config(device->bdf, address, size);
+       if (pci_cfg_read_moderate(device, address,
+                                 size, &reg_data) == PCI_ACCESS_PERFORM)
+               reg_data = arch_pci_read_config(device->info->bdf, address,
+                                               size);
 
        set_rax_reg(guest_regs, reg_data, size);
 
@@ -131,7 +130,6 @@ data_port_in_handler(struct registers *guest_regs, const struct cell *cell,
 /**
  * data_port_out_handler() - Handler for OUT accesses to data port
  * @guest_regs:                Guest register set
- * @cell:              Issuing cell
  * @device:            Structure describing PCI device
  * @address:           Config space access address
  * @size:              Access size (1, 2 or 4 bytes)
@@ -139,18 +137,18 @@ data_port_in_handler(struct registers *guest_regs, const struct cell *cell,
  * Return: 1 if handled successfully, -1 on access error
  */
 static int
-data_port_out_handler(struct registers *guest_regs, const struct cell *cell,
-                     const struct jailhouse_pci_device *device,
+data_port_out_handler(struct registers *guest_regs, struct pci_device *device,
                      u16 address, unsigned int size)
 {
        u32 reg_data = get_rax_reg(guest_regs, size);
        enum pci_access access;
 
-       access = pci_cfg_write_moderate(cell, device, address, size, reg_data);
+       access = pci_cfg_write_moderate(device, address, size, reg_data);
        if (access == PCI_ACCESS_REJECT)
                return -1;
        if (access == PCI_ACCESS_PERFORM)
-               arch_pci_write_config(device->bdf, address, reg_data, size);
+               arch_pci_write_config(device->info->bdf, address, reg_data,
+                                     size);
        return 1;
 }
 
@@ -167,7 +165,7 @@ data_port_out_handler(struct registers *guest_regs, const struct cell *cell,
 int x86_pci_config_handler(struct registers *guest_regs, struct cell *cell,
                           u16 port, bool dir_in, unsigned int size)
 {
-       const struct jailhouse_pci_device *device = NULL;
+       struct pci_device *device;
        u32 addr_port_val;
        u16 bdf, address;
        int result = 0;
@@ -204,11 +202,11 @@ int x86_pci_config_handler(struct registers *guest_regs, struct cell *cell,
                        port - PCI_REG_DATA_PORT;
 
                if (dir_in)
-                       result = data_port_in_handler(guest_regs, cell,
-                                                     device, address, size);
+                       result = data_port_in_handler(guest_regs, device,
+                                                     address, size);
                else
-                       result = data_port_out_handler(guest_regs, cell,
-                                                      device, address, size);
+                       result = data_port_out_handler(guest_regs, device,
+                                                      address, size);
        }
 
        return result;
index 2d96e5ee74338ca5226f8327f065c9dd11f63859..ada89789e729f0268c0c4e510ebba62d16f6b9a1 100644 (file)
 
 enum pci_access { PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM, PCI_ACCESS_DONE };
 
+struct pci_device {
+       const struct jailhouse_pci_device *info;
+       struct cell *cell;
+};
+
 int pci_init(void);
 
 u32 pci_read_config(u16 bdf, u16 address, unsigned int size);
 void pci_write_config(u16 bdf, u16 address, u32 value, unsigned int size);
 
-const struct jailhouse_pci_device *
-pci_get_assigned_device(const struct cell *cell, u16 bdf);
+struct pci_device *pci_get_assigned_device(const struct cell *cell, u16 bdf);
 
-enum pci_access
-pci_cfg_read_moderate(const struct cell *cell,
-                     const struct jailhouse_pci_device *device, u16 address,
-                     unsigned int size, u32 *value);
-enum pci_access
-pci_cfg_write_moderate(const struct cell *cell,
-                      const struct jailhouse_pci_device *device, u16 address,
-                      unsigned int size, u32 value);
+enum pci_access pci_cfg_read_moderate(struct pci_device *device, u16 address,
+                                     unsigned int size, u32 *value);
+enum pci_access pci_cfg_write_moderate(struct pci_device *device, u16 address,
+                                      unsigned int size, u32 value);
 
 int pci_mmio_access_handler(const struct cell *cell, bool is_write, u64 addr,
                            u32 *value);
 
+int pci_cell_init(struct cell *cell);
+void pci_cell_exit(struct cell *cell);
+
 u32 arch_pci_read_config(u16 bdf, u16 address, unsigned int size);
 void arch_pci_write_config(u16 bdf, u16 address, u32 value, unsigned int size);
 
index 57b451c71b28829da378fa34554d4cbd05c620d8..5d6072b39dd96f50545b2f319bdef11562c41872 100644 (file)
@@ -113,39 +113,39 @@ void pci_write_config(u16 bdf, u16 address, u32 value, unsigned int size)
  * @cell:      Owning cell
  * @bdf:       16-bit bus/device/function ID
  *
- * Return: Valid pointer - owns, NULL - doesn't own.
+ * Return: Pointer to owned PCI device or NULL.
  */
-const struct jailhouse_pci_device *
-pci_get_assigned_device(const struct cell *cell, u16 bdf)
+struct pci_device *pci_get_assigned_device(const struct cell *cell, u16 bdf)
 {
-       const struct jailhouse_pci_device *device =
+       const struct jailhouse_pci_device *dev_info =
                jailhouse_cell_pci_devices(cell->config);
        u32 n;
 
+       /* We iterate over the static device information to increase cache
+        * locality. */
        for (n = 0; n < cell->config->num_pci_devices; n++)
-               if (device[n].bdf == bdf)
-                       return &device[n];
+               if (dev_info[n].bdf == bdf)
+                       return &cell->pci_devices[n];
 
        return NULL;
 }
 
 /**
  * pci_find_capability() - Look up capability at given config space address
- * @cell:      Device owning cell
  * @device:    The device to be accessed
  * @address:   Config space access address
  *
  * Return: Corresponding capability structure or NULL if none found.
  */
 static const struct jailhouse_pci_capability *
-pci_find_capability(const struct cell *cell,
-                   const struct jailhouse_pci_device *device, u16 address)
+pci_find_capability(struct pci_device *device, u16 address)
 {
        const struct jailhouse_pci_capability *cap =
-               jailhouse_cell_pci_caps(cell->config) + device->caps_start;
+               jailhouse_cell_pci_caps(device->cell->config) +
+               device->info->caps_start;
        u32 n;
 
-       for (n = 0; n < device->num_caps; n++, cap++)
+       for (n = 0; n < device->info->num_caps; n++, cap++)
                if (cap->start <= address && cap->start + cap->len > address)
                        return cap;
 
@@ -154,7 +154,6 @@ pci_find_capability(const struct cell *cell,
 
 /**
  * pci_cfg_read_moderate() - Moderate config space read access
- * @cell:      Request issuing cell
  * @device:    The device to be accessed; if NULL, access will be emulated,
  *             returning a value of -1
  * @address:   Config space address
@@ -164,10 +163,8 @@ pci_find_capability(const struct cell *cell,
  *
  * Return: PCI_ACCESS_PERFORM or PCI_ACCESS_DONE.
  */
-enum pci_access
-pci_cfg_read_moderate(const struct cell *cell,
-                     const struct jailhouse_pci_device *device, u16 address,
-                     unsigned int size, u32 *value)
+enum pci_access pci_cfg_read_moderate(struct pci_device *device, u16 address,
+                                     unsigned int size, u32 *value)
 {
        const struct jailhouse_pci_capability *cap;
 
@@ -179,7 +176,7 @@ pci_cfg_read_moderate(const struct cell *cell,
        if (address < PCI_CONFIG_HEADER_SIZE)
                return PCI_ACCESS_PERFORM;
 
-       cap = pci_find_capability(cell, device, address);
+       cap = pci_find_capability(device, address);
        if (!cap)
                return PCI_ACCESS_PERFORM;
 
@@ -190,7 +187,6 @@ pci_cfg_read_moderate(const struct cell *cell,
 
 /**
  * pci_cfg_write_moderate() - Moderate config space write access
- * @cell:      Request issuing cell
  * @device:    The device to be accessed; if NULL, access will be rejected
  * @address:   Config space address
  * @size:      Access size (1, 2 or 4 bytes)
@@ -198,10 +194,8 @@ pci_cfg_read_moderate(const struct cell *cell,
  *
  * Return: PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM or PCI_ACCESS_DONE.
  */
-enum pci_access
-pci_cfg_write_moderate(const struct cell *cell,
-                      const struct jailhouse_pci_device *device, u16 address,
-                      unsigned int size, u32 value)
+enum pci_access pci_cfg_write_moderate(struct pci_device *device, u16 address,
+                                      unsigned int size, u32 value)
 {
        const struct jailhouse_pci_capability *cap;
        /* initialize list to work around wrong compiler warning */
@@ -213,10 +207,10 @@ pci_cfg_write_moderate(const struct cell *cell,
                return PCI_ACCESS_REJECT;
 
        if (address < PCI_CONFIG_HEADER_SIZE) {
-               if (device->type == JAILHOUSE_PCI_TYPE_DEVICE) {
+               if (device->info->type == JAILHOUSE_PCI_TYPE_DEVICE) {
                        list = endpoint_write_access;
                        len = ARRAY_SIZE(endpoint_write_access);
-               } else if (device->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
+               } else if (device->info->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
                        list = bridge_write_access;
                        len = ARRAY_SIZE(bridge_write_access);
                }
@@ -233,7 +227,7 @@ pci_cfg_write_moderate(const struct cell *cell,
                return PCI_ACCESS_REJECT;
        }
 
-       cap = pci_find_capability(cell, device, address);
+       cap = pci_find_capability(device, address);
        if (!cap || !(cap->flags & JAILHOUSE_PCICAPS_WRITE))
                return PCI_ACCESS_REJECT;
 
@@ -248,6 +242,11 @@ pci_cfg_write_moderate(const struct cell *cell,
 int pci_init(void)
 {
        struct acpi_mcfg_table *mcfg;
+       int err;
+
+       err = pci_cell_init(&root_cell);
+       if (err)
+               return err;
 
        mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
        if (!mcfg)
@@ -284,8 +283,8 @@ int pci_init(void)
 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
                            u64 addr, u32 *value)
 {
-       const struct jailhouse_pci_device *device;
        u32 mmcfg_offset, reg_addr;
+       struct pci_device *device;
        enum pci_access access;
 
        if (!pci_space || addr < pci_mmcfg_addr ||
@@ -297,15 +296,13 @@ int pci_mmio_access_handler(const struct cell *cell, bool is_write,
        device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
 
        if (is_write) {
-               access = pci_cfg_write_moderate(cell, device, reg_addr, 4,
-                                               *value);
+               access = pci_cfg_write_moderate(device, reg_addr, 4, *value);
                if (access == PCI_ACCESS_REJECT)
                        goto invalid_access;
                if (access == PCI_ACCESS_PERFORM)
                        mmio_write32(pci_space + mmcfg_offset, *value);
        } else {
-               access = pci_cfg_read_moderate(cell, device, reg_addr, 4,
-                                              value);
+               access = pci_cfg_read_moderate(device, reg_addr, 4, value);
                if (access == PCI_ACCESS_PERFORM)
                        *value = mmio_read32(pci_space + mmcfg_offset);
        }
@@ -318,3 +315,39 @@ invalid_access:
        return -1;
 
 }
+
+int pci_cell_init(struct cell *cell)
+{
+       unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
+                                             sizeof(struct pci_device));
+       const struct jailhouse_pci_device *dev_infos =
+               jailhouse_cell_pci_devices(cell->config);
+       struct pci_device *device;
+       unsigned int ndev;
+
+       cell->pci_devices = page_alloc(&mem_pool, array_size / PAGE_SIZE);
+       if (!cell->pci_devices)
+               return -ENOMEM;
+
+       /*
+        * We order device states in the same way as the static information
+        * so that we can use the index of the latter to find the former. For
+        * the other way around and for obtaining the owner cell, we use more
+        * handy pointers.
+        */
+       for (ndev = 0; ndev < cell->config->num_pci_devices; ndev++) {
+               device = &cell->pci_devices[ndev];
+               device->info = &dev_infos[ndev];
+               device->cell = cell;
+       }
+
+       return 0;
+}
+
+void pci_cell_exit(struct cell *cell)
+{
+       unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
+                                             sizeof(struct pci_device));
+
+       page_free(&mem_pool, cell->pci_devices, array_size / PAGE_SIZE);
+}