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