]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/arch/x86/mmio.c
x86: Migrate VT-d interrupt remapping emulation to generic MMIO dispatcher
[jailhouse.git] / hypervisor / arch / x86 / mmio.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2013
5  * Copyright (c) Valentine Sinitsyn, 2014
6  *
7  * Authors:
8  *  Jan Kiszka <jan.kiszka@siemens.com>
9  *  Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
10  *
11  * This work is licensed under the terms of the GNU GPL, version 2.  See
12  * the COPYING file in the top-level directory.
13  */
14
15 #include <jailhouse/mmio.h>
16 #include <jailhouse/paging.h>
17 #include <jailhouse/printk.h>
18 #include <asm/ioapic.h>
19 #include <asm/iommu.h>
20 #include <asm/vcpu.h>
21
22 #define X86_MAX_INST_LEN        15
23
24 union opcode {
25         u8 raw;
26         struct { /* REX */
27                 u8 b:1, x:1, r:1, w:1;
28                 u8 code:4;
29         } __attribute__((packed)) rex;
30         struct {
31                 u8 rm:3;
32                 u8 reg:3;
33                 u8 mod:2;
34         } __attribute__((packed)) modrm;
35         struct {
36                 u8 base:3;
37                 u8 index:3;
38                 u8 ss:2;
39         } __attribute__((packed)) sib;
40 };
41
42 struct parse_context {
43         unsigned int remaining;
44         unsigned int size;
45         const u8 *inst;
46 };
47
48 static void ctx_move_next_byte(struct parse_context *ctx)
49 {
50         ctx->inst++;
51         ctx->size--;
52 }
53
54 static bool ctx_maybe_get_bytes(struct parse_context *ctx,
55                                 unsigned long *pc,
56                                 const struct guest_paging_structures *pg)
57 {
58         if (!ctx->size) {
59                 ctx->size = ctx->remaining;
60                 ctx->inst = vcpu_get_inst_bytes(pg, *pc, &ctx->size);
61                 if (!ctx->inst)
62                         return false;
63                 ctx->remaining -= ctx->size;
64                 *pc += ctx->size;
65         }
66         return true;
67 }
68
69 static bool ctx_advance(struct parse_context *ctx,
70                         unsigned long *pc,
71                         const struct guest_paging_structures *pg)
72 {
73         ctx_move_next_byte(ctx);
74         return ctx_maybe_get_bytes(ctx, pc, pg);
75 }
76
77 struct mmio_instruction x86_mmio_parse(unsigned long pc,
78         const struct guest_paging_structures *pg_structs, bool is_write)
79 {
80         struct parse_context ctx = { .remaining = X86_MAX_INST_LEN };
81         struct mmio_instruction inst = { .inst_len = 0 };
82         union opcode op[3] = { };
83         bool has_rex_r = false;
84         bool does_write;
85
86 restart:
87         if (!ctx_maybe_get_bytes(&ctx, &pc, pg_structs))
88                 goto error_noinst;
89
90         op[0].raw = *(ctx.inst);
91         if (op[0].rex.code == X86_REX_CODE) {
92                 /* REX.W is simply over-read since it is only affects the
93                  * memory address in our supported modes which we get from the
94                  * virtualization support. */
95                 if (op[0].rex.r)
96                         has_rex_r = true;
97                 if (op[0].rex.x)
98                         goto error_unsupported;
99
100                 ctx_move_next_byte(&ctx);
101                 inst.inst_len++;
102                 goto restart;
103         }
104         switch (op[0].raw) {
105         case X86_OP_MOV_TO_MEM:
106                 inst.inst_len += 2;
107                 inst.access_size = 4;
108                 does_write = true;
109                 break;
110         case X86_OP_MOV_FROM_MEM:
111                 inst.inst_len += 2;
112                 inst.access_size = 4;
113                 does_write = false;
114                 break;
115         default:
116                 goto error_unsupported;
117         }
118
119         if (!ctx_advance(&ctx, &pc, pg_structs))
120                 goto error_noinst;
121
122         op[1].raw = *(ctx.inst);
123         switch (op[1].modrm.mod) {
124         case 0:
125                 if (op[1].modrm.rm == 5) /* 32-bit displacement */
126                         goto error_unsupported;
127                 else if (op[1].modrm.rm != 4) /* no SIB */
128                         break;
129                 inst.inst_len++;
130
131                 if (!ctx_advance(&ctx, &pc, pg_structs))
132                         goto error_noinst;
133
134                 op[2].raw = *(ctx.inst);
135                 if (op[2].sib.base == 5)
136                         inst.inst_len += 4;
137                 break;
138         case 1:
139         case 2:
140                 if (op[1].modrm.rm == 4) /* SIB */
141                         goto error_unsupported;
142                 inst.inst_len += op[1].modrm.mod == 1 ? 1 : 4;
143                 break;
144         default:
145                 goto error_unsupported;
146         }
147         if (has_rex_r)
148                 inst.reg_num = 7 - op[1].modrm.reg;
149         else if (op[1].modrm.reg == 4)
150                 goto error_unsupported;
151         else
152                 inst.reg_num = 15 - op[1].modrm.reg;
153
154         if (does_write != is_write)
155                 goto error_inconsitent;
156
157         return inst;
158
159 error_noinst:
160         panic_printk("FATAL: unable to get MMIO instruction\n");
161         goto error;
162
163 error_unsupported:
164         panic_printk("FATAL: unsupported instruction (0x%02x 0x%02x 0x%02x)\n",
165                      op[0].raw, op[1].raw, op[2].raw);
166         goto error;
167
168 error_inconsitent:
169         panic_printk("FATAL: inconsistent access, expected %s instruction\n",
170                      is_write ? "write" : "read");
171 error:
172         inst.inst_len = 0;
173         return inst;
174 }
175
176 unsigned int arch_mmio_count_regions(struct cell *cell)
177 {
178         return ioapic_mmio_count_regions(cell) + iommu_mmio_count_regions(cell);
179 }