]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/arch/x86/vcpu.c
x86: Prepare generic MMIO dispatching
[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 = ioapic_access_handler(this_cell(), mmio.is_write,
224                                                intercept.phys_addr, &val);
225                 mmio.value = val;
226         }
227         if (result == MMIO_UNHANDLED) {
228                 result = pci_mmio_access_handler(this_cell(), mmio.is_write,
229                                                  intercept.phys_addr, &val);
230                 mmio.value = val;
231         }
232         if (result == MMIO_UNHANDLED) {
233                 result = iommu_mmio_access_handler(mmio.is_write,
234                                                    intercept.phys_addr, &val);
235                 mmio.value = val;
236         }
237
238         if (result == MMIO_HANDLED) {
239                 if (!mmio.is_write)
240                         guest_regs->by_index[inst.reg_num] = mmio.value;
241                 vcpu_skip_emulated_instruction(inst.inst_len);
242                 return true;
243         }
244
245 invalid_access:
246         /* report only unhandled access failures */
247         if (result == MMIO_UNHANDLED)
248                 panic_printk("FATAL: Invalid MMIO/RAM %s, addr: %p\n",
249                              intercept.is_write ? "write" : "read",
250                              intercept.phys_addr);
251         return false;
252 }
253
254 bool vcpu_handle_msr_read(void)
255 {
256         struct per_cpu *cpu_data = this_cpu_data();
257
258         switch (cpu_data->guest_regs.rcx) {
259         case MSR_X2APIC_BASE ... MSR_X2APIC_END:
260                 x2apic_handle_read();
261                 break;
262         case MSR_IA32_PAT:
263                 set_rdmsr_value(&cpu_data->guest_regs, cpu_data->pat);
264                 break;
265         case MSR_IA32_MTRR_DEF_TYPE:
266                 set_rdmsr_value(&cpu_data->guest_regs,
267                                 cpu_data->mtrr_def_type);
268                 break;
269         default:
270                 panic_printk("FATAL: Unhandled MSR read: %x\n",
271                              cpu_data->guest_regs.rcx);
272                 return false;
273         }
274
275         vcpu_skip_emulated_instruction(X86_INST_LEN_WRMSR);
276         return true;
277 }
278
279 bool vcpu_handle_msr_write(void)
280 {
281         struct per_cpu *cpu_data = this_cpu_data();
282         unsigned int bit_pos, pa;
283         unsigned long val;
284
285         switch (cpu_data->guest_regs.rcx) {
286         case MSR_X2APIC_BASE ... MSR_X2APIC_END:
287                 if (!x2apic_handle_write())
288                         return false;
289                 break;
290         case MSR_IA32_PAT:
291                 val = get_wrmsr_value(&cpu_data->guest_regs);
292                 for (bit_pos = 0; bit_pos < 64; bit_pos += 8) {
293                         pa = (val >> bit_pos) & 0xff;
294                         /* filter out reserved memory types */
295                         if (pa == 2 || pa == 3 || pa > 7) {
296                                 printk("FATAL: Invalid PAT value: %x\n", val);
297                                 return false;
298                         }
299                 }
300                 cpu_data->pat = val;
301                 if (cpu_data->mtrr_def_type & MTRR_ENABLE)
302                         vcpu_vendor_set_guest_pat(val);
303                 break;
304         case MSR_IA32_MTRR_DEF_TYPE:
305                 /*
306                  * This only emulates the difference between MTRRs enabled
307                  * and disabled. When disabled, we turn off all caching by
308                  * setting the guest PAT to 0. When enabled, guest PAT +
309                  * host-controlled MTRRs define the guest's memory types.
310                  */
311                 val = get_wrmsr_value(&cpu_data->guest_regs);
312                 cpu_data->mtrr_def_type = val;
313                 vcpu_vendor_set_guest_pat((val & MTRR_ENABLE) ?
314                                           cpu_data->pat : 0);
315                 break;
316         default:
317                 panic_printk("FATAL: Unhandled MSR write: %x\n",
318                              cpu_data->guest_regs.rcx);
319                 return false;
320         }
321
322         vcpu_skip_emulated_instruction(X86_INST_LEN_WRMSR);
323         return true;
324 }
325
326 void vcpu_handle_cpuid(void)
327 {
328         static const char signature[12] = "Jailhouse";
329         union registers *guest_regs = &this_cpu_data()->guest_regs;
330         u32 function = guest_regs->rax;
331
332         this_cpu_data()->stats[JAILHOUSE_CPU_STAT_VMEXITS_CPUID]++;
333
334         switch (function) {
335         case JAILHOUSE_CPUID_SIGNATURE:
336                 guest_regs->rax = JAILHOUSE_CPUID_FEATURES;
337                 guest_regs->rbx = *(u32 *)signature;
338                 guest_regs->rcx = *(u32 *)(signature + 4);
339                 guest_regs->rdx = *(u32 *)(signature + 8);
340                 break;
341         case JAILHOUSE_CPUID_FEATURES:
342                 guest_regs->rax = 0;
343                 guest_regs->rbx = 0;
344                 guest_regs->rcx = 0;
345                 guest_regs->rdx = 0;
346                 break;
347         default:
348                 /* clear upper 32 bits of the involved registers */
349                 guest_regs->rax &= 0xffffffff;
350                 guest_regs->rbx &= 0xffffffff;
351                 guest_regs->rcx &= 0xffffffff;
352                 guest_regs->rdx &= 0xffffffff;
353
354                 cpuid((u32 *)&guest_regs->rax, (u32 *)&guest_regs->rbx,
355                       (u32 *)&guest_regs->rcx, (u32 *)&guest_regs->rdx);
356                 if (function == 0x01)
357                         guest_regs->rcx |= X86_FEATURE_HYPERVISOR;
358                 break;
359         }
360
361         vcpu_skip_emulated_instruction(X86_INST_LEN_CPUID);
362 }
363
364 bool vcpu_handle_xsetbv(void)
365 {
366         union registers *guest_regs = &this_cpu_data()->guest_regs;
367
368         this_cpu_data()->stats[JAILHOUSE_CPU_STAT_VMEXITS_XSETBV]++;
369
370         if (cpuid_ecx(1) & X86_FEATURE_XSAVE &&
371             guest_regs->rax & X86_XCR0_FP &&
372             (guest_regs->rax & ~cpuid_eax(0x0d)) == 0 &&
373              guest_regs->rcx == 0 && guest_regs->rdx == 0) {
374                 vcpu_skip_emulated_instruction(X86_INST_LEN_XSETBV);
375                 asm volatile(
376                         "xsetbv"
377                         : /* no output */
378                         : "a" (guest_regs->rax), "c" (0), "d" (0));
379                 return true;
380         }
381         panic_printk("FATAL: Invalid xsetbv parameters: xcr[%d] = %08x:%08x\n",
382                      guest_regs->rcx, guest_regs->rdx, guest_regs->rax);
383         return false;
384 }
385
386 void vcpu_reset(bool hard_reset)
387 {
388         struct per_cpu *cpu_data = this_cpu_data();
389
390         memset(&cpu_data->guest_regs, 0, sizeof(cpu_data->guest_regs));
391
392         if (hard_reset) {
393                 cpu_data->pat = PAT_RESET_VALUE;
394                 cpu_data->mtrr_def_type = 0;
395                 vcpu_vendor_set_guest_pat(0);
396         }
397 }