]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
inmates: Add PCI services to inmates framework
authorJan Kiszka <jan.kiszka@siemens.com>
Fri, 18 Jul 2014 07:50:21 +0000 (09:50 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Tue, 22 Jul 2014 13:49:30 +0000 (15:49 +0200)
Provide library services for PCI config space access, bus scanning,
capability scanning (non-extended only so far) and MSI vector
programming (MSI-X to be added later).

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
inmates/lib/pci.c [new file with mode: 0644]
inmates/lib/x86/inmate.h
inmates/lib/x86/pci.c [new file with mode: 0644]

diff --git a/inmates/lib/pci.c b/inmates/lib/pci.c
new file mode 100644 (file)
index 0000000..af82d21
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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;
+       }
+}
index a9f915d0036e81ad1edb3ed09b0b62fca9579a0c..adc3b56ed1842acf0b44c1549bebdaa8d3ecab6b 100644 (file)
 
 #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;
@@ -201,4 +217,11 @@ enum map_type { MAP_CACHED, MAP_UNCACHED };
 
 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
diff --git a/inmates/lib/x86/pci.c b/inmates/lib/x86/pci.c
new file mode 100644 (file)
index 0000000..640c799
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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);
+}