]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/arch/x86/vcpu.c
x86: Migrate VT-d interrupt remapping emulation to generic MMIO dispatcher
[jailhouse.git] / hypervisor / arch / x86 / vcpu.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/control.h>
16 #include <jailhouse/mmio.h>
17 #include <jailhouse/paging.h>
18 #include <jailhouse/pci.h>
19 #include <jailhouse/printk.h>
20 #include <jailhouse/string.h>
21 #include <jailhouse/types.h>
22 #include <asm/i8042.h>
23 #include <asm/ioapic.h>
24 #include <asm/iommu.h>
25 #include <asm/pci.h>
26 #include <asm/percpu.h>
27 #include <asm/vcpu.h>
28
29 /* Can be overriden in vendor-specific code if needed */
30 const u8 *vcpu_get_inst_bytes(const struct guest_paging_structures *pg_structs,
31                               unsigned long pc, unsigned int *size)
32         __attribute__((weak, alias("vcpu_map_inst")));
33
34 const u8 *vcpu_map_inst(const struct guest_paging_structures *pg_structs,
35                         unsigned long pc, unsigned int *size)
36 {
37         unsigned short bytes_avail;
38         u8 *page = NULL;
39
40         if (!*size)
41                 goto out_err;
42         page = paging_get_guest_pages(pg_structs, pc,
43                         1, PAGE_READONLY_FLAGS);
44         if (!page)
45                 goto out_err;
46
47         /* Number of bytes available before page boundary */
48         bytes_avail = PAGE_SIZE - (pc & PAGE_OFFS_MASK);
49         if (*size > bytes_avail)
50                 *size = bytes_avail;
51
52         return &page[pc & PAGE_OFFS_MASK];
53
54 out_err:
55         return NULL;
56 }
57
58 int vcpu_cell_init(struct cell *cell)
59 {
60         const u8 *pio_bitmap = jailhouse_cell_pio_bitmap(cell->config);
61         u32 pio_bitmap_size = cell->config->pio_bitmap_size;
62         struct vcpu_io_bitmap cell_iobm, root_cell_iobm;
63         unsigned int n, pm_timer_addr;
64         u32 size;
65         int err;
66         u8 *b;
67
68         /* PM timer has to be provided */
69         if (system_config->platform_info.x86.pm_timer_address == 0)
70                 return trace_error(-EINVAL);
71
72         err = vcpu_vendor_cell_init(cell);
73         if (err)
74                 return err;
75
76         vcpu_vendor_get_cell_io_bitmap(cell, &cell_iobm);
77         memset(cell_iobm.data, -1, cell_iobm.size);
78
79         for (n = 0; n < 2; n++) {
80                 size = pio_bitmap_size <= PAGE_SIZE ?
81                         pio_bitmap_size : PAGE_SIZE;
82                 memcpy(cell_iobm.data + n * PAGE_SIZE, pio_bitmap, size);
83                 pio_bitmap += size;
84                 pio_bitmap_size -= size;
85         }
86
87         /* moderate access to i8042 command register */
88         cell_iobm.data[I8042_CMD_REG / 8] |= 1 << (I8042_CMD_REG % 8);
89
90         if (cell != &root_cell) {
91                 /*
92                  * Shrink PIO access of root cell corresponding to new cell's
93                  * access rights.
94                  */
95                 vcpu_vendor_get_cell_io_bitmap(&root_cell, &root_cell_iobm);
96                 pio_bitmap = jailhouse_cell_pio_bitmap(cell->config);
97                 pio_bitmap_size = cell->config->pio_bitmap_size;
98                 for (b = root_cell_iobm.data; pio_bitmap_size > 0;
99                      b++, pio_bitmap++, pio_bitmap_size--)
100                         *b |= ~*pio_bitmap;
101         }
102
103         /* permit access to the PM timer */
104         pm_timer_addr = system_config->platform_info.x86.pm_timer_address;
105         for (n = 0; n < 4; n++, pm_timer_addr++) {
106                 b = cell_iobm.data;
107                 b[pm_timer_addr / 8] &= ~(1 << (pm_timer_addr % 8));
108         }
109
110         return 0;
111 }
112
113 void vcpu_cell_exit(struct cell *cell)
114 {
115         const u8 *root_pio_bitmap =
116                 jailhouse_cell_pio_bitmap(root_cell.config);
117         const u8 *pio_bitmap = jailhouse_cell_pio_bitmap(cell->config);
118         u32 pio_bitmap_size = cell->config->pio_bitmap_size;
119         struct vcpu_io_bitmap root_cell_iobm;
120         u8 *b;
121
122         vcpu_vendor_get_cell_io_bitmap(&root_cell, &root_cell_iobm);
123
124         if (root_cell.config->pio_bitmap_size < pio_bitmap_size)
125                 pio_bitmap_size = root_cell.config->pio_bitmap_size;
126
127         for (b = root_cell_iobm.data; pio_bitmap_size > 0;
128              b++, pio_bitmap++, root_pio_bitmap++, pio_bitmap_size--)
129                 *b &= *pio_bitmap | *root_pio_bitmap;
130
131         vcpu_vendor_cell_exit(cell);
132 }
133
134 void vcpu_handle_hypercall(void)
135 {
136         union registers *guest_regs = &this_cpu_data()->guest_regs;
137         unsigned long code = guest_regs->rax;
138         struct vcpu_execution_state x_state;
139         unsigned long arg_mask;
140         bool long_mode;
141
142         vcpu_skip_emulated_instruction(X86_INST_LEN_HYPERCALL);
143
144         vcpu_vendor_get_execution_state(&x_state);
145
146         long_mode = !!(x_state.efer & EFER_LMA);
147         arg_mask = long_mode ? (u64)-1 : (u32)-1;
148
149         if ((!long_mode && (x_state.rflags & X86_RFLAGS_VM)) ||
150             (x_state.cs & 3) != 0) {
151                 guest_regs->rax = -EPERM;
152                 return;
153         }
154
155         guest_regs->rax = hypercall(code, guest_regs->rdi & arg_mask,
156                                     guest_regs->rsi & arg_mask);
157         if (guest_regs->rax == -ENOSYS)
158                 printk("CPU %d: Unknown hypercall %d, RIP: %p\n",
159                        this_cpu_id(), code,
160                        x_state.rip - X86_INST_LEN_HYPERCALL);
161
162         if (code == JAILHOUSE_HC_DISABLE && guest_regs->rax == 0)
163                 vcpu_deactivate_vmm();
164 }
165
166 bool vcpu_handle_io_access(void)
167 {
168         struct vcpu_io_intercept io;
169         int result = 0;
170
171         vcpu_vendor_get_io_intercept(&io);
172
173         /* string and REP-prefixed instructions are not supported */
174         if (io.rep_or_str)
175                 goto invalid_access;
176
177         result = x86_pci_config_handler(io.port, io.in, io.size);
178         if (result == 0)
179                 result = i8042_access_handler(io.port, io.in, io.size);
180
181         if (result == 1) {
182                 vcpu_skip_emulated_instruction(io.inst_len);
183                 return true;
184         }
185
186 invalid_access:
187         panic_printk("FATAL: Invalid PIO %s, port: %x size: %d\n",
188                      io.in ? "read" : "write", io.port, io.size);
189         return false;
190 }
191
192 bool vcpu_handle_mmio_access(void)
193 {
194         union registers *guest_regs = &this_cpu_data()->guest_regs;
195         enum mmio_result result = MMIO_UNHANDLED;
196         struct mmio_access mmio = {.size = 4};
197         struct guest_paging_structures pg_structs;
198         struct vcpu_mmio_intercept intercept;
199         struct vcpu_execution_state x_state;
200         struct mmio_instruction inst;
201         u32 val;
202
203         vcpu_vendor_get_execution_state(&x_state);
204         vcpu_vendor_get_mmio_intercept(&intercept);
205
206         if (!vcpu_get_guest_paging_structs(&pg_structs))
207                 goto invalid_access;
208
209         inst = x86_mmio_parse(x_state.rip, &pg_structs, intercept.is_write);
210         if (!inst.inst_len || inst.access_size != 4)
211                 goto invalid_access;
212
213         mmio.is_write = intercept.is_write;
214         if (mmio.is_write) {
215                 mmio.value = guest_regs->by_index[inst.reg_num];
216                 val = mmio.value;
217         }
218
219         mmio.address = intercept.phys_addr;
220         result = mmio_handle_access(&mmio);
221
222         if (result == MMIO_UNHANDLED) {
223                 result = pci_mmio_access_handler(this_cell(), mmio.is_write,
224                                                  intercept.phys_addr, &val);
225                 mmio.value = val;
226         }
227
228         if (result == MMIO_HANDLED) {
229                 if (!mmio.is_write)
230                         guest_regs->by_index[inst.reg_num] = mmio.value;
231                 vcpu_skip_emulated_instruction(inst.inst_len);
232                 return true;
233         }
234
235 invalid_access:
236         /* report only unhandled access failures */
237         if (result == MMIO_UNHANDLED)
238                 panic_printk("FATAL: Invalid MMIO/RAM %s, addr: %p\n",
239                              intercept.is_write ? "write" : "read",
240                              intercept.phys_addr);
241         return false;
242 }
243
244 bool vcpu_handle_msr_read(void)
245 {
246         struct per_cpu *cpu_data = this_cpu_data();
247
248         switch (cpu_data->guest_regs.rcx) {
249         case MSR_X2APIC_BASE ... MSR_X2APIC_END:
250                 x2apic_handle_read();
251                 break;
252         case MSR_IA32_PAT:
253                 set_rdmsr_value(&cpu_data->guest_regs, cpu_data->pat);
254                 break;
255         case MSR_IA32_MTRR_DEF_TYPE:
256                 set_rdmsr_value(&cpu_data->guest_regs,
257                                 cpu_data->mtrr_def_type);
258                 break;
259         default:
260                 panic_printk("FATAL: Unhandled MSR read: %x\n",
261                              cpu_data->guest_regs.rcx);
262                 return false;
263         }
264
265         vcpu_skip_emulated_instruction(X86_INST_LEN_WRMSR);
266         return true;
267 }
268
269 bool vcpu_handle_msr_write(void)
270 {
271         struct per_cpu *cpu_data = this_cpu_data();
272         unsigned int bit_pos, pa;
273         unsigned long val;
274
275         switch (cpu_data->guest_regs.rcx) {
276         case MSR_X2APIC_BASE ... MSR_X2APIC_END:
277                 if (!x2apic_handle_write())
278                         return false;
279                 break;
280         case MSR_IA32_PAT:
281                 val = get_wrmsr_value(&cpu_data->guest_regs);
282                 for (bit_pos = 0; bit_pos < 64; bit_pos += 8) {
283                         pa = (val >> bit_pos) & 0xff;
284                         /* filter out reserved memory types */
285                         if (pa == 2 || pa == 3 || pa > 7) {
286                                 printk("FATAL: Invalid PAT value: %x\n", val);
287                                 return false;
288                         }
289                 }
290                 cpu_data->pat = val;
291                 if (cpu_data->mtrr_def_type & MTRR_ENABLE)
292                         vcpu_vendor_set_guest_pat(val);
293                 break;
294         case MSR_IA32_MTRR_DEF_TYPE:
295                 /*
296                  * This only emulates the difference between MTRRs enabled
297                  * and disabled. When disabled, we turn off all caching by
298                  * setting the guest PAT to 0. When enabled, guest PAT +
299                  * host-controlled MTRRs define the guest's memory types.
300                  */
301                 val = get_wrmsr_value(&cpu_data->guest_regs);
302                 cpu_data->mtrr_def_type = val;
303                 vcpu_vendor_set_guest_pat((val & MTRR_ENABLE) ?
304                                           cpu_data->pat : 0);
305                 break;
306         default:
307                 panic_printk("FATAL: Unhandled MSR write: %x\n",
308                              cpu_data->guest_regs.rcx);
309                 return false;
310         }
311
312         vcpu_skip_emulated_instruction(X86_INST_LEN_WRMSR);
313         return true;
314 }
315
316 void vcpu_handle_cpuid(void)
317 {
318         static const char signature[12] = "Jailhouse";
319         union registers *guest_regs = &this_cpu_data()->guest_regs;
320         u32 function = guest_regs->rax;
321
322         this_cpu_data()->stats[JAILHOUSE_CPU_STAT_VMEXITS_CPUID]++;
323
324         switch (function) {
325         case JAILHOUSE_CPUID_SIGNATURE:
326                 guest_regs->rax = JAILHOUSE_CPUID_FEATURES;
327                 guest_regs->rbx = *(u32 *)signature;
328                 guest_regs->rcx = *(u32 *)(signature + 4);
329                 guest_regs->rdx = *(u32 *)(signature + 8);
330                 break;
331         case JAILHOUSE_CPUID_FEATURES:
332                 guest_regs->rax = 0;
333                 guest_regs->rbx = 0;
334                 guest_regs->rcx = 0;
335                 guest_regs->rdx = 0;
336                 break;
337         default:
338                 /* clear upper 32 bits of the involved registers */
339                 guest_regs->rax &= 0xffffffff;
340                 guest_regs->rbx &= 0xffffffff;
341                 guest_regs->rcx &= 0xffffffff;
342                 guest_regs->rdx &= 0xffffffff;
343
344                 cpuid((u32 *)&guest_regs->rax, (u32 *)&guest_regs->rbx,
345                       (u32 *)&guest_regs->rcx, (u32 *)&guest_regs->rdx);
346                 if (function == 0x01)
347                         guest_regs->rcx |= X86_FEATURE_HYPERVISOR;
348                 break;
349         }
350
351         vcpu_skip_emulated_instruction(X86_INST_LEN_CPUID);
352 }
353
354 bool vcpu_handle_xsetbv(void)
355 {
356         union registers *guest_regs = &this_cpu_data()->guest_regs;
357
358         this_cpu_data()->stats[JAILHOUSE_CPU_STAT_VMEXITS_XSETBV]++;
359
360         if (cpuid_ecx(1) & X86_FEATURE_XSAVE &&
361             guest_regs->rax & X86_XCR0_FP &&
362             (guest_regs->rax & ~cpuid_eax(0x0d)) == 0 &&
363              guest_regs->rcx == 0 && guest_regs->rdx == 0) {
364                 vcpu_skip_emulated_instruction(X86_INST_LEN_XSETBV);
365                 asm volatile(
366                         "xsetbv"
367                         : /* no output */
368                         : "a" (guest_regs->rax), "c" (0), "d" (0));
369                 return true;
370         }
371         panic_printk("FATAL: Invalid xsetbv parameters: xcr[%d] = %08x:%08x\n",
372                      guest_regs->rcx, guest_regs->rdx, guest_regs->rax);
373         return false;
374 }
375
376 void vcpu_reset(bool hard_reset)
377 {
378         struct per_cpu *cpu_data = this_cpu_data();
379
380         memset(&cpu_data->guest_regs, 0, sizeof(cpu_data->guest_regs));
381
382         if (hard_reset) {
383                 cpu_data->pat = PAT_RESET_VALUE;
384                 cpu_data->mtrr_def_type = 0;
385                 vcpu_vendor_set_guest_pat(0);
386         }
387 }