]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
core: Rework PCI config space access handling
authorJan Kiszka <jan.kiszka@siemens.com>
Mon, 21 Jul 2014 18:22:08 +0000 (20:22 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Sat, 26 Jul 2014 11:00:12 +0000 (13:00 +0200)
Move more logic into generic code by extending the write handler to
pci_cfg_write_moderate and introducing pci_cfg_read_moderate. These
handlers are responsible for any config space access, including to
unowned or non-existent devices. They can reject the access, return an
emulated value on read or a real value to be written to hardware, or
they instruct the caller to perform the access directly.

We already pass a reference to the issuing cell to the access handlers.
It stays unused for now but will be needed by succeeding changes. So
add it now to avoid changing API and callers once again later on.

This commit lays the foundation for capability access moderation and,
specifically, MSI/MSI-X emulation.

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

index 8426ad372dbba2022114c53181236be1adfc9fda..2a9f1f7ddd53884b539bd39f41d71a8693a335db 100644 (file)
@@ -50,21 +50,31 @@ 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
  * @port:      I/O port number
  * @size:      Access size (1, 2 or 4 bytes)
  * @device:    Structure describing PCI device
+ * @reg_num:   Register number in PCI configuration space
+ *
+ * Return: 1 if handled successfully, -1 on access error
  */
 static int
-data_port_in_handler(struct registers *guest_regs, u16 port, unsigned int size,
-                    const struct jailhouse_pci_device *device)
+data_port_in_handler(struct registers *guest_regs, const struct cell *cell,
+                    u16 port, unsigned int size,
+                    const struct jailhouse_pci_device *device, u8 reg_num)
 {
        u32 reg_data;
 
-       if (!device)
-               reg_data = -1;
-       else
-               reg_data = inl(PCI_REG_DATA_PORT);
-       reg_data >>= (port - PCI_REG_DATA_PORT) * 8;
+       if (pci_cfg_read_moderate(cell, device, reg_num,
+                                 port - PCI_REG_DATA_PORT, size,
+                                 &reg_data) == PCI_ACCESS_PERFORM) {
+               if (size == 1)
+                       reg_data = inb(port);
+               else if (size == 2)
+                       reg_data = inw(port);
+               else if (size == 4)
+                       reg_data = inl(port);
+       }
 
        set_rax_reg(guest_regs, reg_data, size);
 
@@ -74,31 +84,27 @@ data_port_in_handler(struct registers *guest_regs, u16 port, unsigned int size,
 /**
  * data_port_out_handler() - Handler for OUT accesses to data port
  * @guest_regs:        Guest register set
+ * @cell:      Issuing cell
  * @port:      I/O port number
  * @size:      Access size (1, 2 or 4 bytes)
  * @device:    Structure describing PCI device
  * @reg_num:   Register number in PCI configuration space
+ *
+ * Return: 1 if handled successfully, -1 on access error
  */
-static int data_port_out_handler(struct registers *guest_regs, u16 port,
-                                unsigned int size,
-                                const struct jailhouse_pci_device *device,
-                                u8 reg_num)
+static int
+data_port_out_handler(struct registers *guest_regs, const struct cell *cell,
+                     u16 port, unsigned int size,
+                     const struct jailhouse_pci_device *device, u8 reg_num)
 {
        u32 reg_data;
 
-       if (!device)
-               return -1;
-
        reg_data = get_rax_reg(guest_regs, size);
 
-       if (reg_num < PCI_CONFIG_HEADER_SIZE) {
-               if (!pci_cfg_write_allowed(device->type, reg_num,
-                                          port - PCI_REG_DATA_PORT, size))
-                       return -1;
-       } else {
-               // HACK: permit capability access until we properly filter it
-               // return -1;
-       }
+       if (pci_cfg_write_moderate(cell, device, reg_num,
+                                  port - PCI_REG_DATA_PORT, size,
+                                  &reg_data) == PCI_ACCESS_REJECT)
+               return -1;
 
        if (size == 1)
                outb(reg_data, port);
@@ -163,11 +169,11 @@ int x86_pci_config_handler(struct registers *guest_regs, struct cell *cell,
                outl(addr_port_val & PCI_ADDR_VALID_MASK, PCI_REG_ADDR_PORT);
 
                if (dir_in)
-                       result = data_port_in_handler(guest_regs, port, size,
-                                                     device);
+                       result = data_port_in_handler(guest_regs, cell, port,
+                                                     size, device, reg_num);
                else
-                       result = data_port_out_handler(guest_regs, port, size,
-                                                      device, reg_num);
+                       result = data_port_out_handler(guest_regs, cell, port,
+                                                      size, device, reg_num);
 
                spin_unlock(&pci_lock);
        }
index 0cff9fc9c3668a24fe92b5079a6ba7ba1d39aefe..e2c0aab5337992a3f48516e4b4917f0706c9eeb1 100644 (file)
 
 #define PCI_CONFIG_HEADER_SIZE         0x40
 
+enum pci_access { PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM, PCI_ACCESS_EMULATE };
+
 int pci_init(void);
 
 const struct jailhouse_pci_device *
 pci_get_assigned_device(const struct cell *cell, u16 bdf);
 
-bool pci_cfg_write_allowed(u32 type, u8 reg_num, unsigned int reg_bias,
-                          unsigned int size);
+enum pci_access
+pci_cfg_read_moderate(const struct cell *cell,
+                     const struct jailhouse_pci_device *device, u8 reg_num,
+                     unsigned int reg_bias, unsigned int size, u32 *value);
+enum pci_access
+pci_cfg_write_moderate(const struct cell *cell,
+                      const struct jailhouse_pci_device *device, u8 reg_num,
+                      unsigned int reg_bias, unsigned int size, u32 *value);
 
 int pci_mmio_access_handler(const struct cell *cell, bool is_write, u64 addr,
                            u32 *value);
index a54cbe251bc0305f845460f6f63206979f4a91f0..ecd0e7d3f5db797a236abb05dfc8960b1d6a178d 100644 (file)
@@ -76,35 +76,80 @@ pci_get_assigned_device(const struct cell *cell, u16 bdf)
 }
 
 /**
- * pci_cfg_write_allowed() - Check general config space write permission
- * @type:      JAILHOUSE_PCI_TYPE_DEVICE or JAILHOUSE_PCI_TYPE_BRIDGE
+ * 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
+ * @reg_num:   Register number (4-byte aligned)
+ * @bias:      Bias from register base address in bytes
+ * @size:      Access size (1, 2 or 4 bytes)
+ * @value:     Pointer to buffer to receive the emulated value if
+ *             PCI_ACCESS_EMULATE is returned
+ *
+ * Return: PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
+ */
+enum pci_access
+pci_cfg_read_moderate(const struct cell *cell,
+                     const struct jailhouse_pci_device *device, u8 reg_num,
+                     unsigned int reg_bias, unsigned int size, u32 *value)
+{
+       if (!device) {
+               *value = -1;
+               return PCI_ACCESS_EMULATE;
+       }
+
+       if (reg_num < PCI_CONFIG_HEADER_SIZE)
+               return PCI_ACCESS_PERFORM;
+
+       // HACK: permit capability access until we properly filter it
+       return PCI_ACCESS_PERFORM;
+}
+
+/**
+ * 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
  * @reg_num:   Register number (4-byte aligned)
  * @bias:      Bias from register base address in bytes
  * @size:      Access size (1, 2 or 4 bytes)
+ * @value:     Pointer to value to be written, initialized with cell value,
+ *             set to the to-be-written hardware value if PCI_ACCESS_EMULATE
+ *             is returned
  *
- * Return: True if writing is allowed, false otherwise.
+ * Return: PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
  */
-bool pci_cfg_write_allowed(u32 type, u8 reg_num, unsigned int reg_bias,
-                          unsigned int size)
+enum pci_access
+pci_cfg_write_moderate(const struct cell *cell,
+                      const struct jailhouse_pci_device *device, u8 reg_num,
+                      unsigned int reg_bias, unsigned int size, u32 *value)
 {
        /* initialize list to work around wrong compiler warning */
        const struct pci_cfg_access *list = NULL;
        unsigned int n, len = 0;
 
-       if (type == JAILHOUSE_PCI_TYPE_DEVICE) {
-               list = endpoint_write_access;
-               len = ARRAY_SIZE(endpoint_write_access);
-       } else if (type == JAILHOUSE_PCI_TYPE_BRIDGE) {
-               list = bridge_write_access;
-               len = ARRAY_SIZE(bridge_write_access);
-       }
+       if (!device)
+               return PCI_ACCESS_REJECT;
 
-       for (n = 0; n < len; n++)
-               if (list[n].reg_num == reg_num)
-                       return ((list[n].mask >> (reg_bias * 8)) &
-                                BYTE_MASK(size)) == BYTE_MASK(size);
+       if (reg_num < PCI_CONFIG_HEADER_SIZE) {
+               if (device->type == JAILHOUSE_PCI_TYPE_DEVICE) {
+                       list = endpoint_write_access;
+                       len = ARRAY_SIZE(endpoint_write_access);
+               } else if (device->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
+                       list = bridge_write_access;
+                       len = ARRAY_SIZE(bridge_write_access);
+               }
 
-       return false;
+               for (n = 0; n < len; n++) {
+                       if (list[n].reg_num == reg_num &&
+                           ((list[n].mask >> (reg_bias * 8)) &
+                            BYTE_MASK(size)) == BYTE_MASK(size))
+                               return PCI_ACCESS_PERFORM;
+               }
+               return PCI_ACCESS_REJECT;
+       }
+
+       // HACK: permit capability access until we properly filter it
+       return PCI_ACCESS_PERFORM;
 }
 
 /**
@@ -150,10 +195,9 @@ int pci_init(void)
 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
                            u64 addr, u32 *value)
 {
+       u32 mmcfg_offset, val, reg_bias, reg_num;
        const struct jailhouse_pci_device *device;
-       u32 mmcfg_offset;
-       u32 reg_num;
-       u32 reg_bias;
+       enum pci_access access;
 
        if (addr < pci_mmcfg_addr ||
            addr >= (pci_mmcfg_addr + pci_mmcfg_size - 4))
@@ -165,23 +209,20 @@ int pci_mmio_access_handler(const struct cell *cell, bool is_write,
        device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
 
        if (is_write) {
-               if (!device)
-                       goto invalid_access;
-               if (reg_num < PCI_CONFIG_HEADER_SIZE) {
-                       if (!pci_cfg_write_allowed(device->type,
-                                                  reg_num - reg_bias,
-                                                  reg_bias, 4))
-                               goto invalid_access;
-               } else {
-                       // TODO: moderate capability access
+               val = *value;
+               access = pci_cfg_write_moderate(cell, device,
+                                               reg_num - reg_bias, reg_bias,
+                                               4, &val);
+               if (access == PCI_ACCESS_REJECT)
                        goto invalid_access;
-               }
-               mmio_write32(pci_space + mmcfg_offset, *value);
-       } else
-               if (device)
+               mmio_write32(pci_space + mmcfg_offset, val);
+       } else {
+               access = pci_cfg_read_moderate(cell, device,
+                                              reg_num - reg_bias, reg_bias,
+                                              4, value);
+               if (access == PCI_ACCESS_PERFORM)
                        *value = mmio_read32(pci_space + mmcfg_offset);
-               else
-                       *value = -1;
+       }
 
        return 1;