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