]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
arm: mmio emulation skeleton
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>
Wed, 2 Jul 2014 13:52:13 +0000 (14:52 +0100)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 19 Dec 2014 10:04:07 +0000 (11:04 +0100)
This patch adds the necessary code for handling MMIO accesses. The trap
handler fills a mmio_access struct according to the fields in the ESR,
and passes it to all relevant sub-handlers.
If all return UNHANDLED, the access is considered invalid and the CPU is
put into failed mode.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
[Jan: switch to mmio accessors, use asm/mmio.h]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/arm/Makefile
hypervisor/arch/arm/include/asm/bitops.h
hypervisor/arch/arm/include/asm/mmio.h
hypervisor/arch/arm/include/asm/processor.h
hypervisor/arch/arm/include/asm/traps.h
hypervisor/arch/arm/mmio.c [new file with mode: 0644]
hypervisor/arch/arm/traps.c

index e09f455c9b3a3bb1eaab6875261b7f0821cfc7e7..f4bc5d8fa7f87ce7786620e4ba72e943bc1b5b8a 100644 (file)
@@ -16,7 +16,8 @@ KBUILD_AFLAGS := $(filter-out -include asm/unified.h,$(KBUILD_AFLAGS))
 
 always := built-in.o
 
-obj-y := entry.o dbg-write.o exception.o setup.o control.o lib.o traps.o
+obj-y := entry.o dbg-write.o exception.o setup.o control.o lib.o
+obj-y += traps.o mmio.o
 obj-y += paging.o mmu_hyp.o mmu_cell.o caches.o
 obj-y += psci.o psci_low.o spin.o
 obj-y += irqchip.o
index 4ff0bcf574f1b4141f91b9ccc2755923215b553a..41e5ef1e70acc84359c8ef84526c1b4ca82da10f 100644 (file)
@@ -121,5 +121,17 @@ static inline unsigned long ffzl(unsigned long word)
        return ffsl(~word);
 }
 
+/* Extend the value of 'size' bits to a signed long */
+static inline unsigned long sign_extend(unsigned long val, unsigned int size)
+{
+       unsigned long mask;
+
+       if (size >= sizeof(unsigned long) * 8)
+               return val;
+
+       mask = 1U << (size - 1);
+       return (val ^ mask) - mask;
+}
+
 #endif /* !__ASSEMBLY__ */
 #endif /* !_JAILHOUSE_ASM_BITOPS_H */
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2d34f9bf4c90fe95b13eb3b444b4d963f7b972bb 100644 (file)
@@ -0,0 +1,20 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+struct mmio_access {
+       unsigned long addr;
+       bool is_write;
+       unsigned int size;
+       unsigned long val;
+};
+
+int arch_mmio_access(struct mmio_access *access);
index 049a838a44f2b376d8e172f5c3b5db7c0471d25e..e4d85370686ced5b794dc6d9488373158fd2dd21 100644 (file)
@@ -32,6 +32,8 @@
 #define PSR_F_BIT      (1 << 6)
 #define PSR_I_BIT      (1 << 7)
 #define PSR_A_BIT      (1 << 8)
+#define PSR_E_BIT      (1 << 9)
+#define PSR_J_BIT      (1 << 24)
 #define PSR_IT_MASK(it)        (((it) & 0x3) << 25 | ((it) & 0xfc) << 8)
 #define PSR_IT(psr)    (((psr) >> 25 & 0x3) | ((psr) >> 8 & 0xfc))
 
index 3f291d41c5a3ba59cf919dcc8c6362ae253a3184..cee06681c6e0e53c1593e6d0e09c7da1304132c9 100644 (file)
@@ -87,5 +87,11 @@ static inline void access_usr_reg(struct trap_context *ctx, u8 reg,
                ctx->regs[reg] = *val;
 }
 
+void access_cell_reg(struct trap_context *ctx, u8 reg, unsigned long *val,
+                    bool is_read);
+void arch_skip_instruction(struct trap_context *ctx);
+
+int arch_handle_dabt(struct per_cpu *cpu_data, struct trap_context *ctx);
+
 #endif /* !__ASSEMBLY__ */
 #endif /* !_JAILHOUSE_ASM_TRAPS_H */
diff --git a/hypervisor/arch/arm/mmio.c b/hypervisor/arch/arm/mmio.c
new file mode 100644 (file)
index 0000000..fc89edd
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.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 <asm/irqchip.h>
+#include <asm/processor.h>
+#include <asm/traps.h>
+
+/* Taken from the ARM ARM pseudocode for taking a data abort */
+static void arch_inject_dabt(struct trap_context *ctx, unsigned long addr)
+{
+       unsigned int lr_offset;
+       unsigned long vbar;
+       bool is_thumb;
+       u32 sctlr, ttbcr;
+
+       arm_read_sysreg(SCTLR_EL1, sctlr);
+       arm_read_sysreg(TTBCR, ttbcr);
+
+       /* Set cpsr */
+       is_thumb = ctx->cpsr & PSR_T_BIT;
+       ctx->cpsr &= ~(PSR_MODE_MASK | PSR_IT_MASK(0xff) | PSR_T_BIT
+                       | PSR_J_BIT | PSR_E_BIT);
+       ctx->cpsr |= (PSR_ABT_MODE | PSR_I_BIT | PSR_A_BIT);
+       if (sctlr & SCTLR_TE_BIT)
+               ctx->cpsr |= PSR_T_BIT;
+       if (sctlr & SCTLR_EE_BIT)
+               ctx->cpsr |= PSR_E_BIT;
+
+       lr_offset = (is_thumb ? 4 : 0);
+       arm_write_banked_reg(LR_abt, ctx->pc + lr_offset);
+
+       /* Branch to dabt vector */
+       if (sctlr & SCTLR_V_BIT)
+               vbar = 0xffff0000;
+       else
+               arm_read_sysreg(VBAR, vbar);
+       ctx->pc = vbar + 0x10;
+
+       /* Signal a debug fault. DFSR layout depends on the LPAE bit */
+       if (ttbcr >> 31)
+               arm_write_sysreg(DFSR, (1 << 9) | 0x22);
+       else
+               arm_write_sysreg(DFSR, 0x2);
+       arm_write_sysreg(DFAR, addr);
+}
+
+int arch_mmio_access(struct mmio_access *access)
+{
+       void *addr = (void *)access->addr;
+
+       if (access->is_write) {
+               switch (access->size) {
+               case 1:
+                       mmio_write8(addr, access->val);
+                       break;
+               case 2:
+                       mmio_write16(addr, access->val);
+                       break;
+               case 4:
+                       mmio_write32(addr, access->val);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       } else {
+               switch (access->size) {
+               case 1:
+                       access->val = mmio_read8(addr);
+                       break;
+               case 2:
+                       access->val = mmio_read16(addr);
+                       break;
+               case 4:
+                       access->val = mmio_read32(addr);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+int arch_handle_dabt(struct per_cpu *cpu_data, struct trap_context *ctx)
+{
+       struct mmio_access access;
+       unsigned long hpfar;
+       unsigned long hdfar;
+       int ret         = TRAP_UNHANDLED;
+       /* Decode the syndrome fields */
+       u32 icc         = ESR_ICC(ctx->esr);
+       u32 isv         = icc >> 24;
+       u32 sas         = icc >> 22 & 0x3;
+       u32 sse         = icc >> 21 & 0x1;
+       u32 srt         = icc >> 16 & 0xf;
+       u32 ea          = icc >> 9 & 0x1;
+       u32 cm          = icc >> 8 & 0x1;
+       u32 s1ptw       = icc >> 7 & 0x1;
+       u32 is_write    = icc >> 6 & 0x1;
+       u32 size        = 1 << sas;
+
+       arm_read_sysreg(HPFAR, hpfar);
+       arm_read_sysreg(HDFAR, hdfar);
+       access.addr = hpfar << 8;
+       access.addr |= hdfar & 0xfff;
+
+       /*
+        * Invalid instruction syndrome means multiple access or writeback, there
+        * is nothing we can do.
+        */
+       if (!isv || size > sizeof(unsigned long))
+               goto error_unhandled;
+
+       /* Re-inject abort during page walk, cache maintenance or external */
+       if (s1ptw || ea || cm) {
+               arch_inject_dabt(ctx, hdfar);
+               return TRAP_HANDLED;
+       }
+
+       if (is_write) {
+               /* Load the value to write from the src register */
+               access_cell_reg(ctx, srt, &access.val, true);
+               if (sse)
+                       access.val = sign_extend(access.val, 8 * size);
+       } else {
+               access.val = 0;
+       }
+       access.is_write = is_write;
+       access.size = size;
+
+       /* ret = sub-handler call... */
+
+       if (ret == TRAP_HANDLED) {
+               /* Put the read value into the dest register */
+               if (!is_write) {
+                       if (sse)
+                               access.val = sign_extend(access.val, 8 * size);
+                       access_cell_reg(ctx, srt, &access.val, false);
+               }
+
+               arch_skip_instruction(ctx);
+       }
+
+       if (ret != TRAP_UNHANDLED)
+               return ret;
+
+error_unhandled:
+       panic_printk("Unhandled data %s at 0x%x(%d)\n",
+               (is_write ? "write" : "read"), access.addr, size);
+
+       return ret;
+}
index 6c8e788e82e7889011d11d45e276616121796120..07dc5d479256dd504a16d825094c85fea54413d5 100644 (file)
@@ -115,7 +115,7 @@ static void arch_advance_itstate(struct trap_context *ctx)
        ctx->cpsr = cpsr;
 }
 
-static void arch_skip_instruction(struct trap_context *ctx)
+void arch_skip_instruction(struct trap_context *ctx)
 {
        u32 instruction_length = ESR_IL(ctx->esr);
 
@@ -123,8 +123,8 @@ static void arch_skip_instruction(struct trap_context *ctx)
        arch_advance_itstate(ctx);
 }
 
-static void access_cell_reg(struct trap_context *ctx, u8 reg,
-                               unsigned long *val, bool is_read)
+void access_cell_reg(struct trap_context *ctx, u8 reg, unsigned long *val,
+                    bool is_read)
 {
        unsigned long mode = ctx->cpsr & PSR_MODE_MASK;
 
@@ -247,6 +247,7 @@ static const trap_handler trap_handlers[38] =
 {
        [ESR_EC_CP15_64]        = arch_handle_cp15_64,
        [ESR_EC_HVC]            = arch_handle_hvc,
+       [ESR_EC_DABT]           = arch_handle_dabt,
 };
 
 void arch_handle_trap(struct per_cpu *cpu_data, struct registers *guest_regs)