]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
Basic DMA remapping support via VT-d
authorJan Kiszka <jan.kiszka@siemens.com>
Mon, 2 Sep 2013 10:31:52 +0000 (12:31 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Wed, 13 Nov 2013 09:26:49 +0000 (10:26 +0100)
This just sets up the VT-d DMAR units for the Linux cell. We do not yet
configure things for additional cells.

Also, although this feature will have to be mandatory for a real target,
we keep VT-d support optional in order to preserve the ability to run
Jailhouse inside QEMU/KVM.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
config/h87i.c
hypervisor/arch/x86/Makefile
hypervisor/arch/x86/include/asm/cell.h
hypervisor/arch/x86/include/asm/vtd.h [new file with mode: 0644]
hypervisor/arch/x86/setup.c
hypervisor/arch/x86/vmx.c
hypervisor/arch/x86/vtd.c [new file with mode: 0644]
hypervisor/include/jailhouse/cell-config.h

index 812d17f4b5f1b1c2f348e2111dcf291a6652bff7..d1eae53ad420e4a5586b807434c2d18dd67621b3 100644 (file)
@@ -23,6 +23,7 @@ struct {
        __u64 ALIGN cpus[1];
        struct jailhouse_memory ALIGN mem_regions[8];
        __u8 ALIGN pio_bitmap[0x2000];
+       struct jailhouse_pci_device pci_devices[13];
 } ALIGN config = {
        .header = {
                .hypervisor_memory = {
@@ -41,7 +42,7 @@ struct {
                        .num_irq_lines = 0,
                        .pio_bitmap_size = ARRAY_SIZE(config.pio_bitmap),
 
-                       .num_pci_devices = 0,
+                       .num_pci_devices = ARRAY_SIZE(config.pci_devices),
                },
        },
 
@@ -129,4 +130,85 @@ struct {
                [ 0x3e0/8 ...  0xcf7/8] = -1,
                [ 0xcf8/8 ... 0xffff/8] = 0, /* HACK: full PCI */
        },
+
+       .pci_devices = {
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0x00,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_BRIDGE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0x08,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0x10,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0x18,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xa0,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xb0,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xc8,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xd0,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xd8,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xe0,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xf8,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xfa,
+               },
+               {
+                       .type = JAILHOUSE_PCI_TYPE_DEVICE,
+                       .domain = 0x0000,
+                       .bus = 0x00,
+                       .devfn = 0xfb,
+               },
+       },
 };
index 927804ff7b4795a248a5d081e5ff81c8028511ac..8c228a382b53a1e4515024d8a68e4fbc9b80ed9d 100644 (file)
@@ -13,4 +13,4 @@
 always := built-in.o
 
 obj-y := apic.o dbg-write.o entry.o setup.o fault.o vmx.o control.o mmio.o \
-        ../../acpi.o
+        ../../acpi.o vtd.o
index 172a7d71a13a51b53978137380b93db1ab3acd7d..2ff13d23abf688914d5b8a5017c69e8f43ac9c62 100644 (file)
@@ -25,6 +25,10 @@ struct cell {
                pgd_t *ept;
        } vmx;
 
+       struct {
+               pgd_t *page_table;
+       } vtd;
+
        char name[JAILHOUSE_CELL_NAME_MAXLEN+1];
        unsigned int id;
 
diff --git a/hypervisor/arch/x86/include/asm/vtd.h b/hypervisor/arch/x86/include/asm/vtd.h
new file mode 100644 (file)
index 0000000..adcc233
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2013
+ *
+ * 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 <jailhouse/acpi.h>
+#include <asm/types.h>
+
+#define ACPI_DMAR_DRHD                 0
+#define ACPI_DMAR_RMRR                 1
+#define ACPI_DMAR_ATSR                 2
+#define ACPI_DMAR_RHSA                 3
+
+struct acpi_dmar_remap_header {
+       u16 type;
+       u16 length;
+};
+
+struct acpi_dmar_table {
+       struct acpi_table_header header;
+       u8 host_address_width;
+       u8 flags;
+       u8 reserved[10];
+       struct acpi_dmar_remap_header remap_structs[];
+};
+
+struct acpi_dmar_drhd {
+       struct acpi_dmar_remap_header header;
+       u8 flags;
+       u8 reserved;
+       u16 segment;
+       u64 register_base_addr;
+       u8 device_scope[];
+};
+
+#define VTD_ROOT_PRESENT               0x00000001
+
+#define VTD_CTX_PRESENT                        0x00000001
+#define VTD_CTX_FPD                    0x00000002
+#define VTD_CTX_TTYPE_MLP_UNTRANS      0x00000000
+#define VTD_CTX_TTYPE_MLP_ALL          0x00000004
+#define VTD_CTX_TTYPE_PASSTHROUGH      0x00000008
+
+#define VTD_CTX_AGAW_30                        0x00000000
+#define VTD_CTX_AGAW_39                        0x00000001
+#define VTD_CTX_AGAW_48                        0x00000002
+#define VTD_CTX_AGAW_57                        0x00000003
+#define VTD_CTX_AGAW_64                        0x00000004
+#define VTD_CTX_DID_SHIFT              8
+#define VTD_CTX_DID16_MASK             0x00000f00
+
+struct vtd_entry {
+       u64 lo_word;
+       u64 hi_word;
+};
+
+#define VTD_PAGE_READ                  0x00000001
+#define VTD_PAGE_WRITE                 0x00000002
+
+#define VTD_CAP_REG                    0x08
+# define VTD_CAP_SAGAW30               0x00000100
+# define VTD_CAP_SAGAW39               0x00000200
+# define VTD_CAP_SAGAW48               0x00000400
+# define VTD_CAP_SAGAW57               0x00000800
+# define VTD_CAP_SAGAW64               0x00001000
+#define VTD_ECAP_REG                   0x10
+#define VTD_GCMD_REG                   0x18
+# define VTD_GCMD_SRTP                 0x40000000
+# define VTD_GCMD_TE                   0x80000000
+#define VTD_GSTS_REG                   0x1C
+# define VTD_GSTS_SRTP                 0x40000000
+# define VTD_GSTS_TE                   0x80000000
+#define VTD_RTADDR_REG                 0x20
+#define VTD_CCMD_REG                   0x28
+#define VTD_PMEN_REG                   0x64
+#define VTD_PLMBASE_REG                        0x68
+#define VTD_PLMLIMIT_REG               0x6C
+#define VTD_PHMBASE_REG                        0x70
+#define VTD_PHMLIMIT_REG               0x78
+
+int vtd_init(void);
+int vtd_cell_init(struct cell *cell, struct jailhouse_cell_desc *config);
+void vtd_shutdown(void);
index d1c6e9859674882362ce0a34a07bffe7abdde5b5..2bec7d641e0f251849f23e3c5b13b2b4cb4429e4 100644 (file)
  * the COPYING file in the top-level directory.
  */
 
+#include <jailhouse/control.h>
 #include <jailhouse/entry.h>
 #include <jailhouse/paging.h>
 #include <jailhouse/printk.h>
 #include <jailhouse/processor.h>
-#include <jailhouse/control.h>
-#include <asm/vmx.h>
 #include <asm/apic.h>
 #include <asm/bitops.h>
+#include <asm/vmx.h>
+#include <asm/vtd.h>
 
 #define TSS_BUSY_FLAG          (1UL << (9 + 32))
 
@@ -174,6 +175,16 @@ error_out:
 int arch_init_late(struct cell *linux_cell,
                   struct jailhouse_cell_desc *config)
 {
+       int err;
+
+       err = vtd_init();
+       if (err)
+               return err;
+
+       err = vtd_cell_init(linux_cell, config);
+       if (err)
+               return err;
+
        return 0;
 }
 
index 15578c637ed05381b5370ac84a6675d68403c81c..6dc0143aa0081cc9fd542cb465f19b14987b894c 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/apic.h>
 #include <asm/fault.h>
 #include <asm/vmx.h>
+#include <asm/vtd.h>
 
 static u8 __attribute__((aligned(PAGE_SIZE))) msr_bitmap[][0x2000/8] = {
        [ VMX_MSR_BITMAP_0000_READ ] = {
@@ -926,8 +927,10 @@ void vmx_handle_exit(struct registers *guest_regs, struct per_cpu *cpu_data)
                switch (guest_regs->rax) {
                case JAILHOUSE_HC_DISABLE:
                        guest_regs->rax = shutdown(cpu_data);
-                       if (guest_regs->rax == 0)
+                       if (guest_regs->rax == 0) {
+                               vtd_shutdown();
                                vmx_cpu_deactivate_vmm(guest_regs, cpu_data);
+                       }
                        break;
                case JAILHOUSE_HC_CELL_CREATE:
                        guest_regs->rax = cell_create(cpu_data,
diff --git a/hypervisor/arch/x86/vtd.c b/hypervisor/arch/x86/vtd.c
new file mode 100644 (file)
index 0000000..11473e9
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2013
+ *
+ * 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 <jailhouse/mmio.h>
+#include <jailhouse/paging.h>
+#include <jailhouse/printk.h>
+#include <asm/vtd.h>
+
+/* TODO: Support multiple segments */
+static struct vtd_entry __attribute__((aligned(PAGE_SIZE)))
+       root_entry_table[256];
+static void *dmar_reg_base;
+static unsigned int dmar_units;
+static unsigned dmar_pt_levels;
+
+int vtd_init(void)
+{
+       const struct acpi_dmar_table *dmar;
+       const struct acpi_dmar_drhd *drhd;
+       unsigned int pt_levels;
+       void *reg_base = NULL;
+       unsigned long offset;
+       unsigned long caps;
+       int err;
+
+       dmar = (struct acpi_dmar_table *)acpi_find_table("DMAR", NULL);
+       if (!dmar)
+//             return -ENODEV;
+               { printk("WARNING: No VT-d support found!\n"); return 0; }
+
+       if (sizeof(struct acpi_dmar_table) +
+           sizeof(struct acpi_dmar_drhd) > dmar->header.length)
+               return -EIO;
+
+       drhd = (struct acpi_dmar_drhd *)dmar->remap_structs;
+       if (drhd->header.type != ACPI_DMAR_DRHD)
+               return -EIO;
+
+       offset = (void *)dmar->remap_structs - (void *)dmar;
+       do {
+               if (drhd->header.length < sizeof(struct acpi_dmar_drhd) ||
+                   offset + drhd->header.length > dmar->header.length)
+                       return -EIO;
+
+               /* TODO: support multiple segments */
+               if (drhd->segment != 0)
+                       return -EIO;
+
+               printk("Found DMAR @%p\n", drhd->register_base_addr);
+
+               reg_base = page_alloc(&remap_pool, 1);
+               if (!reg_base)
+                       return -ENOMEM;
+
+               if (dmar_units == 0)
+                       dmar_reg_base = reg_base;
+               else if (reg_base != dmar_reg_base + dmar_units * PAGE_SIZE)
+                       return -ENOMEM;
+
+               err = page_map_create(hv_page_table, drhd->register_base_addr,
+                                     PAGE_SIZE, (unsigned long)reg_base,
+                                     PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
+                                     PAGE_DEFAULT_FLAGS, PAGE_DIR_LEVELS);
+               if (err)
+                       return err;
+
+               caps = mmio_read64(reg_base + VTD_CAP_REG);
+               if (caps & VTD_CAP_SAGAW39)
+                       pt_levels = 3;
+               else if (caps & VTD_CAP_SAGAW48)
+                       pt_levels = 4;
+               else
+                       return -EIO;
+
+               if (dmar_pt_levels > 0 && dmar_pt_levels != pt_levels)
+                       return -EIO;
+               dmar_pt_levels = pt_levels;
+
+               if (mmio_read32(reg_base + VTD_GSTS_REG) & VTD_GSTS_TE)
+                       return -EBUSY;
+
+               dmar_units++;
+
+               offset += drhd->header.length;
+               drhd = (struct acpi_dmar_drhd *)
+                       (((void *)drhd) + drhd->header.length);
+       } while (offset < dmar->header.length &&
+                drhd->header.type == ACPI_DMAR_DRHD);
+
+       return 0;
+}
+
+static bool vtd_add_device_to_cell(struct cell *cell,
+                                  struct jailhouse_pci_device *device)
+{
+       u64 root_entry_lo = root_entry_table[device->bus].lo_word;
+       struct vtd_entry *context_entry_table, *context_entry;
+
+       if (root_entry_lo & VTD_ROOT_PRESENT) {
+               context_entry_table =
+                       page_map_phys2hvirt(root_entry_lo & PAGE_MASK);
+       } else {
+               context_entry_table = page_alloc(&mem_pool, 1);
+               if (!context_entry_table)
+                       return false;
+               root_entry_table[device->bus].lo_word = VTD_ROOT_PRESENT |
+                       page_map_hvirt2phys(context_entry_table);
+       }
+
+       context_entry = &context_entry_table[device->devfn];
+       context_entry->lo_word = VTD_CTX_PRESENT |
+               VTD_CTX_FPD | VTD_CTX_TTYPE_MLP_UNTRANS |
+               page_map_hvirt2phys(cell->vtd.page_table);
+       context_entry->hi_word = (dmar_pt_levels == 3 ? VTD_CTX_AGAW_39
+                                                     : VTD_CTX_AGAW_48) |
+               ((cell->id << VTD_CTX_DID_SHIFT) & VTD_CTX_DID16_MASK);
+
+       return true;
+}
+
+int vtd_cell_init(struct cell *cell, struct jailhouse_cell_desc *config)
+{
+       struct jailhouse_pci_device *dev;
+       void *reg_base = dmar_reg_base;
+       struct jailhouse_memory *mem;
+       u32 page_flags;
+       int n, err;
+
+       // HACK for QEMU
+       if (dmar_units == 0)
+               return 0;
+
+       cell->vtd.page_table = page_alloc(&mem_pool, 1);
+       if (!cell->vtd.page_table)
+               return -ENOMEM;
+
+       mem = (void *)config + sizeof(struct jailhouse_cell_desc) +
+               config->cpu_set_size;
+
+       for (n = 0; n < config->num_memory_regions; n++, mem++) {
+               if (!(mem->access_flags & JAILHOUSE_MEM_DMA))
+                       continue;
+
+               page_flags = 0;
+               if (mem->access_flags & JAILHOUSE_MEM_READ)
+                       page_flags |= VTD_PAGE_READ;
+               if (mem->access_flags & JAILHOUSE_MEM_WRITE)
+                       page_flags |= VTD_PAGE_WRITE;
+
+               err = page_map_create(cell->vtd.page_table, mem->phys_start,
+                                     mem->size, mem->virt_start, page_flags,
+                                     VTD_PAGE_READ | VTD_PAGE_WRITE,
+                                     dmar_pt_levels);
+               if (err)
+                       /* FIXME: release vtd.page_table */
+                       return err;
+       }
+
+       dev = (void *)mem +
+               config->num_irq_lines * sizeof(struct jailhouse_irq_line) +
+               config->pio_bitmap_size;
+
+       for (n = 0; n < config->num_pci_devices; n++)
+               if (!vtd_add_device_to_cell(cell, &dev[n]))
+                       /* FIXME: release vtd.page_table,
+                        * revert device additions*/
+                       return -ENOMEM;
+
+       /* Brute-force write-back of CPU caches in case the hardware accesses
+        * translation structures non-coherently */
+       asm volatile("wbinvd");
+
+       for (n = 0; n < dmar_units; n++, reg_base += PAGE_SIZE) {
+               if (!(mmio_read32(reg_base + VTD_GSTS_REG) & VTD_GSTS_TE)) {
+                       mmio_write64(reg_base + VTD_RTADDR_REG,
+                                    page_map_hvirt2phys(root_entry_table));
+                       mmio_write32(reg_base + VTD_GCMD_REG, VTD_GCMD_SRTP);
+                       while (!(mmio_read32(reg_base + VTD_GSTS_REG) &
+                                VTD_GSTS_SRTP))
+                               cpu_relax();
+
+                       mmio_write32(reg_base + VTD_GCMD_REG, VTD_GCMD_TE);
+               } else {
+                       // cache flush, somehow
+               }
+       }
+
+       return 0;
+}
+
+void vtd_shutdown(void)
+{
+       void *reg_base = dmar_reg_base;
+       unsigned int n;
+
+       for (n = 0; n < dmar_units; n++, reg_base += PAGE_SIZE)
+               mmio_write32(reg_base + VTD_GCMD_REG, 0);
+}
index 822c3d218d1ac1ec6fc017b08e23dbb2ecb0f4a2..820dd7ef03df5efac98e7f7de456f4d921cb7ae6 100644 (file)
@@ -50,24 +50,16 @@ struct jailhouse_irq_line {
        __u32 irqchip;
 };
 
-
-struct jailhouse_pci_bridge {
-       // TODO
-       __u32 num_device;
-};
-
 #define JAILHOUSE_PCI_TYPE_DEVICE      0x01
 #define JAILHOUSE_PCI_TYPE_BRIDGE      0x02
 
 struct jailhouse_pci_device {
-       // TODO
        __u32 type;
        __u16 domain;
        __u8 bus;
        __u8 devfn;
 } __attribute__((packed));
 
-
 struct jailhouse_system {
        struct jailhouse_memory hypervisor_memory;
        struct jailhouse_memory config_memory;