]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/arch/arm/mmio.c
arm: mmio emulation skeleton
[jailhouse.git] / hypervisor / arch / arm / mmio.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) ARM Limited, 2014
5  *
6  * Authors:
7  *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include <jailhouse/mmio.h>
14 #include <asm/irqchip.h>
15 #include <asm/processor.h>
16 #include <asm/traps.h>
17
18 /* Taken from the ARM ARM pseudocode for taking a data abort */
19 static void arch_inject_dabt(struct trap_context *ctx, unsigned long addr)
20 {
21         unsigned int lr_offset;
22         unsigned long vbar;
23         bool is_thumb;
24         u32 sctlr, ttbcr;
25
26         arm_read_sysreg(SCTLR_EL1, sctlr);
27         arm_read_sysreg(TTBCR, ttbcr);
28
29         /* Set cpsr */
30         is_thumb = ctx->cpsr & PSR_T_BIT;
31         ctx->cpsr &= ~(PSR_MODE_MASK | PSR_IT_MASK(0xff) | PSR_T_BIT
32                         | PSR_J_BIT | PSR_E_BIT);
33         ctx->cpsr |= (PSR_ABT_MODE | PSR_I_BIT | PSR_A_BIT);
34         if (sctlr & SCTLR_TE_BIT)
35                 ctx->cpsr |= PSR_T_BIT;
36         if (sctlr & SCTLR_EE_BIT)
37                 ctx->cpsr |= PSR_E_BIT;
38
39         lr_offset = (is_thumb ? 4 : 0);
40         arm_write_banked_reg(LR_abt, ctx->pc + lr_offset);
41
42         /* Branch to dabt vector */
43         if (sctlr & SCTLR_V_BIT)
44                 vbar = 0xffff0000;
45         else
46                 arm_read_sysreg(VBAR, vbar);
47         ctx->pc = vbar + 0x10;
48
49         /* Signal a debug fault. DFSR layout depends on the LPAE bit */
50         if (ttbcr >> 31)
51                 arm_write_sysreg(DFSR, (1 << 9) | 0x22);
52         else
53                 arm_write_sysreg(DFSR, 0x2);
54         arm_write_sysreg(DFAR, addr);
55 }
56
57 int arch_mmio_access(struct mmio_access *access)
58 {
59         void *addr = (void *)access->addr;
60
61         if (access->is_write) {
62                 switch (access->size) {
63                 case 1:
64                         mmio_write8(addr, access->val);
65                         break;
66                 case 2:
67                         mmio_write16(addr, access->val);
68                         break;
69                 case 4:
70                         mmio_write32(addr, access->val);
71                         break;
72                 default:
73                         return -EINVAL;
74                 }
75         } else {
76                 switch (access->size) {
77                 case 1:
78                         access->val = mmio_read8(addr);
79                         break;
80                 case 2:
81                         access->val = mmio_read16(addr);
82                         break;
83                 case 4:
84                         access->val = mmio_read32(addr);
85                         break;
86                 default:
87                         return -EINVAL;
88                 }
89         }
90
91         return 0;
92 }
93
94 int arch_handle_dabt(struct per_cpu *cpu_data, struct trap_context *ctx)
95 {
96         struct mmio_access access;
97         unsigned long hpfar;
98         unsigned long hdfar;
99         int ret         = TRAP_UNHANDLED;
100         /* Decode the syndrome fields */
101         u32 icc         = ESR_ICC(ctx->esr);
102         u32 isv         = icc >> 24;
103         u32 sas         = icc >> 22 & 0x3;
104         u32 sse         = icc >> 21 & 0x1;
105         u32 srt         = icc >> 16 & 0xf;
106         u32 ea          = icc >> 9 & 0x1;
107         u32 cm          = icc >> 8 & 0x1;
108         u32 s1ptw       = icc >> 7 & 0x1;
109         u32 is_write    = icc >> 6 & 0x1;
110         u32 size        = 1 << sas;
111
112         arm_read_sysreg(HPFAR, hpfar);
113         arm_read_sysreg(HDFAR, hdfar);
114         access.addr = hpfar << 8;
115         access.addr |= hdfar & 0xfff;
116
117         /*
118          * Invalid instruction syndrome means multiple access or writeback, there
119          * is nothing we can do.
120          */
121         if (!isv || size > sizeof(unsigned long))
122                 goto error_unhandled;
123
124         /* Re-inject abort during page walk, cache maintenance or external */
125         if (s1ptw || ea || cm) {
126                 arch_inject_dabt(ctx, hdfar);
127                 return TRAP_HANDLED;
128         }
129
130         if (is_write) {
131                 /* Load the value to write from the src register */
132                 access_cell_reg(ctx, srt, &access.val, true);
133                 if (sse)
134                         access.val = sign_extend(access.val, 8 * size);
135         } else {
136                 access.val = 0;
137         }
138         access.is_write = is_write;
139         access.size = size;
140
141         /* ret = sub-handler call... */
142
143         if (ret == TRAP_HANDLED) {
144                 /* Put the read value into the dest register */
145                 if (!is_write) {
146                         if (sse)
147                                 access.val = sign_extend(access.val, 8 * size);
148                         access_cell_reg(ctx, srt, &access.val, false);
149                 }
150
151                 arch_skip_instruction(ctx);
152         }
153
154         if (ret != TRAP_UNHANDLED)
155                 return ret;
156
157 error_unhandled:
158         panic_printk("Unhandled data %s at 0x%x(%d)\n",
159                 (is_write ? "write" : "read"), access.addr, size);
160
161         return ret;
162 }