/**
* 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,
+ ®_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);
/**
* 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,
+ ®_data) == PCI_ACCESS_REJECT)
+ return -1;
if (size == 1)
outb(reg_data, port);
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);
}
#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);
}
/**
- * 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;
}
/**
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))
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;