always := built-in.o
obj-y := apic.o dbg-write.o entry.o setup.o vmx.o control.o mmio.o \
- ../../acpi.o vtd.o paging.o
+ ../../acpi.o vtd.o paging.o ../../pci.o pci.o
#include <jailhouse/cell-config.h>
#include <jailhouse/hypercall.h>
+/**
+ * struct cell - cell-related state information
+ * ...
+ * @pci_addr_port_val: virtual address port for PCI config space
+ * ...
+ */
+/* TODO: factor out arch-independent bits, define struct arch_cell */
struct cell {
struct {
/* should be first as it requires page alignment */
struct cell *next;
+ u32 pci_addr_port_val;
+
union {
struct jailhouse_comm_region comm_region;
u8 padding[PAGE_SIZE];
--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2014
+ *
+ * Authors:
+ * Ivan Kolchin <ivan.kolchin@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _JAILHOUSE_ASM_PCI_H
+#define _JAILHOUSE_ASM_PCI_H
+
+#include <asm/percpu.h>
+#include <asm/types.h>
+
+/* --- PCI configuration ports --- */
+#define PCI_REG_ADDR_PORT 0xcf8
+#define PCI_REG_DATA_PORT 0xcfc
+
+/* --- Address register fields --- */
+/* Bits 31: Enable bit*/
+#define PCI_ADDR_ENABLE 0x80000000
+/* Bits 23-16: Bus number */
+#define PCI_ADDR_BUS_MASK 0x00ff0000
+/* Bits 15-11: Device number */
+#define PCI_ADDR_DEV_MASK 0x0000f800
+/* Bits 10-8: Function number */
+#define PCI_ADDR_FUNC_MASK 0x00000700
+/* Bits 7-2: Register number */
+#define PCI_ADDR_REGNUM_MASK 0x000000fc
+#define PCI_ADDR_VALID_MASK \
+ (PCI_ADDR_ENABLE | PCI_ADDR_BUS_MASK | PCI_ADDR_DEV_MASK | \
+ PCI_ADDR_FUNC_MASK | PCI_ADDR_REGNUM_MASK)
+#define PCI_ADDR_BDF_SHIFT 8
+
+int x86_pci_config_handler(struct registers *guest_regs, struct cell *cell,
+ u16 port, bool dir_in, unsigned int size);
+
+#endif /* !_JAILHOUSE_ASM_PCI_H */
--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2014
+ *
+ * Authors:
+ * Ivan Kolchin <ivan.kolchin@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/pci.h>
+#include <jailhouse/utils.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+
+/* protects the root bridge's PIO interface to the PCI config space */
+static DEFINE_SPINLOCK(pci_lock);
+
+/**
+ * set_rax_reg() - Set value of RAX in guest register set
+ * @guest_regs: Guest register set
+ * @value_new: New value to be written
+ * @size: Access size (1, 2 or 4 bytes)
+ */
+static void set_rax_reg(struct registers *guest_regs,
+ u32 value_new, u8 size)
+{
+ u64 value_old = guest_regs->rax;
+ /* 32-bit access is special, since it clears all the upper
+ * part of RAX. Another types of access leave it intact */
+ u64 mask = (size == 4 ? BYTE_MASK(8) : BYTE_MASK(size));
+
+ guest_regs->rax = (value_old & ~mask) | (value_new & mask);
+}
+
+/**
+ * get_rax_reg() - Get value of RAX from guest register set
+ * @guest_regs: Guest register set
+ * @size: Access size (1, 2 or 4 bytes)
+ *
+ * Return: Register value
+ */
+static u32 get_rax_reg(struct registers *guest_regs, u8 size)
+{
+ return guest_regs->rax & BYTE_MASK(size);
+}
+
+/**
+ * data_port_in_handler() - Handler for IN accesses to data port
+ * @guest_regs: Guest register set
+ * @port: I/O port number
+ * @size: Access size (1, 2 or 4 bytes)
+ * @device: Structure describing PCI device
+ */
+static int
+data_port_in_handler(struct registers *guest_regs, u16 port, unsigned int size,
+ const struct jailhouse_pci_device *device)
+{
+ u32 reg_data;
+
+ if (!device)
+ reg_data = -1;
+ else
+ reg_data = inl(PCI_REG_DATA_PORT);
+ reg_data >>= (port - PCI_REG_DATA_PORT) * 8;
+
+ set_rax_reg(guest_regs, reg_data, size);
+
+ return 1;
+}
+
+/**
+ * data_port_out_handler() - Handler for OUT accesses to data port
+ * @guest_regs: Guest register set
+ * @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
+ */
+static int data_port_out_handler(struct registers *guest_regs, 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)) {
+ if (size == 1)
+ outb(reg_data, port);
+ else if (size == 2)
+ outw(reg_data, port);
+ else if (size == 4)
+ outl(reg_data, port);
+ return 1;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * x86_pci_config_handler() - Handler for accesses to PCI config space
+ * @guest_regs: Guest registers
+ * @cell: Issuing cell
+ * @port: I/O port number
+ * @dir_in: True for input, false for output
+ * @size: Size of access in bytes (1, 2 or 4 bytes)
+ *
+ * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
+ */
+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;
+ u32 addr_port_val;
+ int result = 0;
+ u8 reg_num;
+ u16 bdf;
+
+ if (port == PCI_REG_ADDR_PORT) {
+ /* only 4-byte accesses are valid */
+ if (size != 4)
+ return -1;
+
+ if (dir_in)
+ set_rax_reg(guest_regs, cell->pci_addr_port_val, size);
+ else
+ cell->pci_addr_port_val =
+ get_rax_reg(guest_regs, size);
+ result = 1;
+ } else if (port >= PCI_REG_DATA_PORT &&
+ port < (PCI_REG_DATA_PORT + 4)) {
+ /* overflowing accesses are invalid */
+ if (port + size > PCI_REG_DATA_PORT + 4)
+ return -1;
+
+ /*
+ * Decode which register in PCI config space is accessed. It is
+ * essential to store the address port value locally so that we
+ * are not affected by concurrent manipulations by other CPUs
+ * of this cell.
+ */
+ addr_port_val = cell->pci_addr_port_val;
+ reg_num = addr_port_val & PCI_ADDR_REGNUM_MASK;
+
+ /* get device (NULL if it doesn't belong to the cell) */
+ bdf = addr_port_val >> PCI_ADDR_BDF_SHIFT;
+ device = pci_get_assigned_device(cell, bdf);
+
+ spin_lock(&pci_lock);
+
+ 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);
+ else
+ result = data_port_out_handler(guest_regs, port, size,
+ device, reg_num);
+
+ spin_unlock(&pci_lock);
+ }
+
+ return result;
+}
#include <asm/control.h>
#include <asm/vmx.h>
#include <asm/vtd.h>
+#include <asm/io.h>
+#include <asm/pci.h>
static const struct segment invalid_seg = {
.access_rights = 0x10000
panic_printk("EFER: %p\n", vmcs_read64(GUEST_IA32_EFER));
}
+static bool vmx_handle_io_access(struct registers *guest_regs,
+ struct per_cpu *cpu_data)
+{
+ /* parse exit qualification for I/O instructions (see SDM, 27.2.1 ) */
+ u64 exitq = vmcs_read64(EXIT_QUALIFICATION);
+ u16 port = (exitq >> 16) & 0xFFFF;
+ bool dir_in = (exitq & 0x8) >> 3;
+ unsigned int size = (exitq & 0x3) + 1;
+
+ vmx_skip_emulated_instruction(vmcs_read64(VM_EXIT_INSTRUCTION_LEN));
+
+ /* string and REP-prefixed instructions are not supported */
+ if (exitq & 0x30)
+ return false;
+
+ if (x86_pci_config_handler(guest_regs, cpu_data->cell, port, dir_in,
+ size) == 1)
+ return true;
+
+ panic_printk("FATAL: Invalid PIO %s, port: %x size: %d\n",
+ dir_in ? "read" : "write", port, size);
+ panic_printk("PCI address port: %x\n",
+ cpu_data->cell->pci_addr_port_val);
+ return false;
+}
+
void vmx_handle_exit(struct registers *guest_regs, struct per_cpu *cpu_data)
{
u32 reason = vmcs_read32(VM_EXIT_REASON);
"xcr[%d] = %08x:%08x\n", guest_regs->rcx,
guest_regs->rdx, guest_regs->rax);
break;
+ case EXIT_REASON_IO_INSTRUCTION:
+ if (vmx_handle_io_access(guest_regs, cpu_data))
+ return;
+ break;
default:
panic_printk("FATAL: Unhandled VM-Exit, reason %d, ",
(u16)reason);
return id;
}
+/* cell must be zero-initialized */
int cell_init(struct cell *cell, bool copy_cpu_set)
{
const unsigned long *config_cpu_set =
--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2014
+ *
+ * Authors:
+ * Ivan Kolchin <ivan.kolchin@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _JAILHOUSE_PCI_H
+#define _JAILHOUSE_PCI_H
+
+#include <asm/cell.h>
+
+#define PCI_CONFIG_HEADER_SIZE 0x40
+
+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);
+
+#endif /* !_JAILHOUSE_PCI_H */
--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2014
+ *
+ * Authors:
+ * Ivan Kolchin <ivan.kolchin@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/pci.h>
+#include <jailhouse/utils.h>
+
+/* entry for PCI config space whitelist (granting access) */
+struct pci_cfg_access {
+ u32 reg_num; /** Register number (4-byte aligned) */
+ u32 mask; /** Bit set: access allowed */
+};
+
+/* --- Whilelist for writing to PCI config space registers --- */
+/* Type 1: Endpoints */
+static const struct pci_cfg_access endpoint_write_access[] = {
+ { 0x04, 0xffffffff }, /* Command, Status */
+ { 0x0c, 0xff000000 }, /* BIST */
+ { 0x3c, 0x000000ff }, /* Int Line */
+};
+/* Type 2: Bridges */
+static const struct pci_cfg_access bridge_write_access[] = {
+ { 0x04, 0xffffffff }, /* Command, Status */
+ { 0x0c, 0xff000000 }, /* BIST */
+ { 0x3c, 0xffff00ff }, /* Int Line, Bridge Control */
+};
+
+/**
+ * pci_get_assigned_device() - Look up device owned by a cell
+ * @cell: Owning cell
+ * @bdf: 16-bit bus/device/function ID
+ *
+ * Return: Valid pointer - owns, NULL - doesn't own.
+ */
+const struct jailhouse_pci_device *
+pci_get_assigned_device(const struct cell *cell, u16 bdf)
+{
+ const struct jailhouse_pci_device *device =
+ jailhouse_cell_pci_devices(cell->config);
+ u32 n;
+
+ for (n = 0; n < cell->config->num_pci_devices; n++)
+ if (((device[n].bus << 8) | device[n].devfn) == bdf)
+ return &device[n];
+
+ return NULL;
+}
+
+/**
+ * pci_cfg_write_allowed() - Check general config space write permission
+ * @type: JAILHOUSE_PCI_TYPE_DEVICE or JAILHOUSE_PCI_TYPE_BRIDGE
+ * @reg_num: Register number (4-byte aligned)
+ * @bias: Bias from register base address in bytes
+ * @size: Access size (1, 2 or 4 bytes)
+ *
+ * Return: True if writing is allowed, false otherwise.
+ */
+bool pci_cfg_write_allowed(u32 type, u8 reg_num, unsigned int reg_bias,
+ unsigned int size)
+{
+ /* 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);
+ }
+
+ 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);
+
+ return false;
+}