]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
x86: Add support for IOAPIC access control
authorJan Kiszka <jan.kiszka@siemens.com>
Mon, 2 Jun 2014 10:17:48 +0000 (12:17 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Tue, 17 Jun 2014 15:38:48 +0000 (17:38 +0200)
This adds basic access control to the IOAPIC. Based on the IRQ chip
configuration, we permit or deny writing to redirection table entries.
This may require integration with interrupt remapping later on.

We furthermore allow reads from other valid IOAPIC registers but deny
any other write accesses.

EOI writing is currently passed through. This will have to be revisited
as well when interrupt remapping is added.

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

index 6c17a53767cf901f6c2c5df82bb0169a22fbedd1..b7b2cbe895e4848391574f83f3ee14d9a34dd647 100644 (file)
@@ -13,4 +13,4 @@
 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 ../../pci.o pci.o
+        ../../acpi.o vtd.o paging.o ../../pci.o pci.o ioapic.o
index c0047fed49e3c36eef4c31d99fdc35ee9f349e36..d4efa1a58d1ef7e1d0a95d950b9dc547fa3a4a8a 100644 (file)
@@ -15,6 +15,7 @@
 #include <jailhouse/processor.h>
 #include <asm/apic.h>
 #include <asm/control.h>
+#include <asm/ioapic.h>
 #include <asm/vmx.h>
 #include <asm/vtd.h>
 
@@ -40,6 +41,9 @@ int arch_cell_create(struct per_cpu *cpu_data, struct cell *cell)
        if (err)
                vmx_cell_exit(cell);
 
+       ioapic_cell_init(cell);
+       ioapic_root_cell_shrink(cell->config);
+
        return 0;
 }
 
@@ -72,6 +76,7 @@ int arch_unmap_memory_region(struct cell *cell,
 
 void arch_cell_destroy(struct per_cpu *cpu_data, struct cell *cell)
 {
+       ioapic_cell_exit(cell);
        vtd_cell_exit(cell);
        vmx_cell_exit(cell);
 }
index 6db37962ed848905d96dd233f0358676d22b8202..75ef6811f3a1ebc735c13f88f2c9d4be2284ff2f 100644 (file)
@@ -49,6 +49,9 @@ struct cell {
 
        u32 pci_addr_port_val;
 
+       u32 ioapic_index_reg_val;
+       u64 ioapic_pin_bitmap;
+
        union {
                struct jailhouse_comm_region comm_region;
                u8 padding[PAGE_SIZE];
diff --git a/hypervisor/arch/x86/include/asm/ioapic.h b/hypervisor/arch/x86/include/asm/ioapic.h
new file mode 100644 (file)
index 0000000..0f64a76
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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 <asm/cell.h>
+
+#define IOAPIC_BASE_ADDR       0xfec00000
+#define IOAPIC_REG_INDEX       0x00
+#define IOAPIC_REG_DATA                0x10
+#define IOAPIC_REG_EOI         0x40
+#define IOAPIC_ID              0x00
+#define IOAPIC_VER             0x01
+#define IOAPIC_REDIR_TBL_START 0x10
+#define IOAPIC_REDIR_TBL_END   0x3f
+
+int ioapic_init(void);
+
+void ioapic_cell_init(struct cell *cell);
+void ioapic_root_cell_shrink(struct jailhouse_cell_desc *config);
+void ioapic_cell_exit(struct cell *cell);
+
+int ioapic_access_handler(struct cell *cell, bool is_write, u64 addr,
+                         u32 *value);
diff --git a/hypervisor/arch/x86/ioapic.c b/hypervisor/arch/x86/ioapic.c
new file mode 100644 (file)
index 0000000..4cba2f9
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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 <jailhouse/mmio.h>
+#include <jailhouse/printk.h>
+#include <asm/ioapic.h>
+#include <asm/spinlock.h>
+
+#include <jailhouse/cell-config.h>
+
+static DEFINE_SPINLOCK(ioapic_lock);
+static void *ioapic;
+
+int ioapic_init(void)
+{
+       int err;
+
+       ioapic = page_alloc(&remap_pool, 1);
+       if (!ioapic)
+               return -ENOMEM;
+       err = page_map_create(&hv_paging_structs, IOAPIC_BASE_ADDR, PAGE_SIZE,
+                             (unsigned long)ioapic,
+                             PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
+                             PAGE_MAP_NON_COHERENT);
+       if (err)
+               return err;
+
+       ioapic_cell_init(&root_cell);
+
+       return 0;
+}
+
+static const struct jailhouse_irqchip *
+ioapic_find_config(struct jailhouse_cell_desc *config)
+{
+       const struct jailhouse_irqchip *irqchip =
+               jailhouse_cell_irqchips(config);
+       unsigned int n;
+
+       for (n = 0; n < config->num_irqchips; n++, irqchip++)
+               if (irqchip->address == IOAPIC_BASE_ADDR)
+                       return irqchip;
+       return NULL;
+}
+
+void ioapic_cell_init(struct cell *cell)
+{
+       const struct jailhouse_irqchip *irqchip =
+               ioapic_find_config(cell->config);
+
+       if (irqchip)
+               cell->ioapic_pin_bitmap = irqchip->pin_bitmap;
+}
+
+void ioapic_root_cell_shrink(struct jailhouse_cell_desc *config)
+{
+       const struct jailhouse_irqchip *irqchip = ioapic_find_config(config);
+
+       if (irqchip)
+               root_cell.ioapic_pin_bitmap &= ~irqchip->pin_bitmap;
+}
+
+void ioapic_cell_exit(struct cell *cell)
+{
+       const struct jailhouse_irqchip *cell_irqchip =
+               ioapic_find_config(cell->config);
+       const struct jailhouse_irqchip *root_irqchip =
+               ioapic_find_config(root_cell.config);
+
+       if (cell_irqchip && root_irqchip)
+               root_cell.ioapic_pin_bitmap |= cell_irqchip->pin_bitmap &
+                       root_irqchip->pin_bitmap;
+}
+
+/**
+ * x86_ioapic_handler() - Handler for accesses to IOAPIC
+ * @cell:      Request issuing cell
+ * @is_write:  True if write access
+ * @addr:      Address accessed
+ * @value:     Pointer to value for reading/writing
+ *
+ * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
+ */
+int ioapic_access_handler(struct cell *cell, bool is_write, u64 addr,
+                         u32 *value)
+{
+       u32 index, entry;
+
+       if (addr < IOAPIC_BASE_ADDR || addr >= IOAPIC_BASE_ADDR + PAGE_SIZE)
+               return 0;
+
+       switch (addr - IOAPIC_BASE_ADDR) {
+       case IOAPIC_REG_INDEX:
+               if (is_write)
+                       cell->ioapic_index_reg_val = *value;
+               else
+                       *value = cell->ioapic_index_reg_val;
+               return 1;
+       case IOAPIC_REG_DATA:
+               index = cell->ioapic_index_reg_val;
+               if (index >= IOAPIC_REDIR_TBL_START &&
+                   index <= IOAPIC_REDIR_TBL_END) {
+                       entry = (index - IOAPIC_REDIR_TBL_START) / 2;
+                       /* Note: we only support one IOAPIC per system */
+                       if ((cell->ioapic_pin_bitmap & (1UL << entry)) == 0)
+                               goto invalid_access;
+                       // TODO validate written value, virtualize
+               } else if ((index != IOAPIC_ID && index != IOAPIC_VER) ||
+                          is_write)
+                       goto invalid_access;
+
+               spin_lock(&ioapic_lock);
+               mmio_write32(ioapic + IOAPIC_REG_INDEX, index);
+               if (is_write)
+                       mmio_write32(ioapic + IOAPIC_REG_DATA, *value);
+               else
+                       *value = mmio_read32(ioapic + IOAPIC_REG_DATA);
+               spin_unlock(&ioapic_lock);
+               return 1;
+       case IOAPIC_REG_EOI:
+               if (!is_write)
+                       goto invalid_access;
+               // TODO: virtualize
+               mmio_write32(ioapic + IOAPIC_REG_EOI, *value);
+               return 1;
+       }
+
+invalid_access:
+       panic_printk("FATAL: Invalid IOAPIC %s, reg: %x, index: %x\n",
+                    is_write ? "write" : "read", addr - IOAPIC_BASE_ADDR,
+                    cell->ioapic_index_reg_val);
+       return -1;
+}
index f4ce23d68d018fa02c790741b5d614e9bc4e9af4..c5806d390ea25bda971998ac392f486ee8c9a681 100644 (file)
@@ -16,6 +16,7 @@
 #include <jailhouse/processor.h>
 #include <asm/apic.h>
 #include <asm/bitops.h>
+#include <asm/ioapic.h>
 #include <asm/vmx.h>
 #include <asm/vtd.h>
 
@@ -220,6 +221,10 @@ int arch_init_late()
        if (err)
                return err;
 
+       err = ioapic_init();
+       if (err)
+               return err;
+
        return 0;
 }
 
index 5a547375419f9319d69ef52aead6f88a272fa1ea..cf5e464ffe395f0656eb479990d2c56ea1ccc401 100644 (file)
 #include <jailhouse/pci.h>
 #include <asm/apic.h>
 #include <asm/control.h>
-#include <asm/vmx.h>
-#include <asm/vtd.h>
 #include <asm/io.h>
+#include <asm/ioapic.h>
 #include <asm/pci.h>
+#include <asm/vmx.h>
+#include <asm/vtd.h>
 
 static const struct segment invalid_seg = {
        .access_rights = 0x10000
@@ -1043,8 +1044,11 @@ static bool vmx_handle_ept_violation(struct registers *guest_regs,
        if (is_write)
                val = ((unsigned long *)guest_regs)[access.reg];
 
-       result = pci_mmio_access_handler(cpu_data->cell, is_write, phys_addr,
-                                        &val);
+       result = ioapic_access_handler(cpu_data->cell, is_write, phys_addr,
+                                      &val);
+       if (result == 0)
+               result = pci_mmio_access_handler(cpu_data->cell, is_write,
+                                                phys_addr, &val);
 
        if (result == 1) {
                if (!is_write)