2 * Jailhouse, a Linux-based partitioning hypervisor
4 * Copyright (c) ARM Limited, 2014
7 * Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
13 #include <jailhouse/mmio.h>
14 #include <asm/irqchip.h>
15 #include <asm/processor.h>
17 #include <asm/traps.h>
19 /* Taken from the ARM ARM pseudocode for taking a data abort */
20 static void arch_inject_dabt(struct trap_context *ctx, unsigned long addr)
22 unsigned int lr_offset;
27 arm_read_sysreg(SCTLR_EL1, sctlr);
28 arm_read_sysreg(TTBCR, ttbcr);
31 is_thumb = ctx->cpsr & PSR_T_BIT;
32 ctx->cpsr &= ~(PSR_MODE_MASK | PSR_IT_MASK(0xff) | PSR_T_BIT
33 | PSR_J_BIT | PSR_E_BIT);
34 ctx->cpsr |= (PSR_ABT_MODE | PSR_I_BIT | PSR_A_BIT);
35 if (sctlr & SCTLR_TE_BIT)
36 ctx->cpsr |= PSR_T_BIT;
37 if (sctlr & SCTLR_EE_BIT)
38 ctx->cpsr |= PSR_E_BIT;
40 lr_offset = (is_thumb ? 4 : 0);
41 arm_write_banked_reg(LR_abt, ctx->pc + lr_offset);
43 /* Branch to dabt vector */
44 if (sctlr & SCTLR_V_BIT)
47 arm_read_sysreg(VBAR, vbar);
48 ctx->pc = vbar + 0x10;
50 /* Signal a debug fault. DFSR layout depends on the LPAE bit */
52 arm_write_sysreg(DFSR, (1 << 9) | 0x22);
54 arm_write_sysreg(DFSR, 0x2);
55 arm_write_sysreg(DFAR, addr);
58 int arch_mmio_access(struct mmio_access *access)
60 void *addr = (void *)access->addr;
62 if (access->is_write) {
63 switch (access->size) {
65 mmio_write8(addr, access->val);
68 mmio_write16(addr, access->val);
71 mmio_write32(addr, access->val);
77 switch (access->size) {
79 access->val = mmio_read8(addr);
82 access->val = mmio_read16(addr);
85 access->val = mmio_read32(addr);
95 int arch_handle_dabt(struct per_cpu *cpu_data, struct trap_context *ctx)
97 struct mmio_access access;
100 int ret = TRAP_UNHANDLED;
101 /* Decode the syndrome fields */
102 u32 icc = ESR_ICC(ctx->esr);
104 u32 sas = icc >> 22 & 0x3;
105 u32 sse = icc >> 21 & 0x1;
106 u32 srt = icc >> 16 & 0xf;
107 u32 ea = icc >> 9 & 0x1;
108 u32 cm = icc >> 8 & 0x1;
109 u32 s1ptw = icc >> 7 & 0x1;
110 u32 is_write = icc >> 6 & 0x1;
113 arm_read_sysreg(HPFAR, hpfar);
114 arm_read_sysreg(HDFAR, hdfar);
115 access.addr = hpfar << 8;
116 access.addr |= hdfar & 0xfff;
118 cpu_data->stats[JAILHOUSE_CPU_STAT_VMEXITS_MMIO]++;
121 * Invalid instruction syndrome means multiple access or writeback, there
122 * is nothing we can do.
124 if (!isv || size > sizeof(unsigned long))
125 goto error_unhandled;
127 /* Re-inject abort during page walk, cache maintenance or external */
128 if (s1ptw || ea || cm) {
129 arch_inject_dabt(ctx, hdfar);
134 /* Load the value to write from the src register */
135 access_cell_reg(ctx, srt, &access.val, true);
137 access.val = sign_extend(access.val, 8 * size);
141 access.is_write = is_write;
144 ret = irqchip_mmio_access(cpu_data, &access);
145 if (ret == TRAP_UNHANDLED)
146 ret = arch_smp_mmio_access(cpu_data, &access);
148 if (ret == TRAP_HANDLED) {
149 /* Put the read value into the dest register */
152 access.val = sign_extend(access.val, 8 * size);
153 access_cell_reg(ctx, srt, &access.val, false);
156 arch_skip_instruction(ctx);
159 if (ret != TRAP_UNHANDLED)
163 panic_printk("Unhandled data %s at 0x%x(%d)\n",
164 (is_write ? "write" : "read"), access.addr, size);