--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2014
+ *
+ * Authors:
+ * Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+int pci_find_device(u16 vendor, u16 device)
+{
+ unsigned int bdf;
+ u16 id;
+
+ for (bdf = 0; bdf < 0x10000; bdf++) {
+ id = pci_read_config(bdf, PCI_CFG_VENDOR_ID, 2);
+ if (id == PCI_ID_ANY || (vendor != PCI_ID_ANY && vendor != id))
+ continue;
+ if (device == PCI_ID_ANY ||
+ pci_read_config(bdf, PCI_CFG_DEVICE_ID, 2) == device)
+ return bdf;
+ }
+ return -1;
+}
+
+int pci_find_cap(u16 bdf, u16 cap)
+{
+ u8 pos = PCI_CFG_CAP_PTR - 1;
+
+ while (1) {
+ pos = pci_read_config(bdf, pos + 1, 1);
+ if (pos == 0)
+ return -1;
+ if (pci_read_config(bdf, pos, 1) == cap)
+ return pos;
+ }
+}
#define X2APIC_ID 0x802
+#define PCI_CFG_VENDOR_ID 0x000
+#define PCI_CFG_DEVICE_ID 0x002
+#define PCI_CFG_COMMAND 0x004
+# define PCI_CMD_IO (1 << 0)
+# define PCI_CMD_MEM (1 << 1)
+# define PCI_CMD_MASTER (1 << 2)
+# define PCI_CMD_INTX_OFF (1 << 10)
+#define PCI_CFG_STATUS 0x006
+# define PCI_STS_INT (1 << 3)
+#define PCI_CFG_BAR 0x010
+#define PCI_CFG_CAP_PTR 0x034
+
+#define PCI_ID_ANY 0xffff
+
+#define PCI_CAP_MSI 0x05
+
#ifndef __ASSEMBLY__
typedef signed char s8;
typedef unsigned char u8;
void *alloc(unsigned long size, unsigned long align);
void map_range(void *start, unsigned long size, enum map_type map_type);
+
+u32 pci_read_config(u16 bdf, unsigned int addr, unsigned int size);
+void pci_write_config(u16 bdf, unsigned int addr, u32 value,
+ unsigned int size);
+int pci_find_device(u16 vendor, u16 device);
+int pci_find_cap(u16 bdf, u16 cap);
+void pci_msi_set_vector(u16 bdf, unsigned int vector);
#endif
--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2014
+ *
+ * Authors:
+ * Jan Kiszka <jan.kiszka@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 <inmate.h>
+
+#include "../pci.c"
+
+#define PCI_REG_ADDR_PORT 0xcf8
+#define PCI_REG_DATA_PORT 0xcfc
+
+#define PCI_CONE (1 << 31)
+
+u32 pci_read_config(u16 bdf, unsigned int addr, unsigned int size)
+{
+ outl(PCI_CONE | ((u32)bdf << 8) | (addr & 0xfc), PCI_REG_ADDR_PORT);
+ switch (size) {
+ case 1:
+ return inb(PCI_REG_DATA_PORT + (addr & 0x3));
+ case 2:
+ return inw(PCI_REG_DATA_PORT + (addr & 0x3));
+ case 4:
+ return inl(PCI_REG_DATA_PORT);
+ default:
+ return -1;
+ }
+}
+
+void pci_write_config(u16 bdf, unsigned int addr, u32 value, unsigned int size)
+{
+ outl(PCI_CONE | ((u32)bdf << 8) | (addr & 0xfc), PCI_REG_ADDR_PORT);
+ switch (size) {
+ case 1:
+ outb(value, PCI_REG_DATA_PORT + (addr & 0x3));
+ break;
+ case 2:
+ outw(value, PCI_REG_DATA_PORT + (addr & 0x3));
+ break;
+ case 4:
+ outl(value, PCI_REG_DATA_PORT);
+ break;
+ }
+}
+
+void pci_msi_set_vector(u16 bdf, unsigned int vector)
+{
+ int cap = pci_find_cap(bdf, PCI_CAP_MSI);
+ u16 ctl, data;
+
+ if (cap < 0)
+ return;
+
+ pci_write_config(bdf, cap + 0x04, 0xfee00000 | (cpu_id() << 12), 4);
+
+ ctl = pci_read_config(bdf, cap + 0x02, 2);
+ if (ctl & (1 << 7)) {
+ pci_write_config(bdf, cap + 0x08, 0, 4);
+ data = cap + 0x0c;
+ } else
+ data = cap + 0x08;
+ pci_write_config(bdf, data, vector, 2);
+
+ pci_write_config(bdf, cap + 0x02, 0x0001, 2);
+}