diff options
-rw-r--r-- | arch/arm/include/asm/kvm_arm.h | 11 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_coproc.h | 14 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_emulate.h | 6 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_host.h | 4 | ||||
-rw-r--r-- | arch/arm/kvm/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kvm/arm.c | 169 | ||||
-rw-r--r-- | arch/arm/kvm/coproc.c | 360 | ||||
-rw-r--r-- | arch/arm/kvm/coproc.h | 153 | ||||
-rw-r--r-- | arch/arm/kvm/coproc_a15.c | 162 | ||||
-rw-r--r-- | arch/arm/kvm/emulate.c | 218 | ||||
-rw-r--r-- | arch/arm/kvm/trace.h | 65 |
11 files changed, 1160 insertions, 4 deletions
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index c69936b1fc53..9a34c20d41ec 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h | |||
@@ -70,6 +70,11 @@ | |||
70 | HCR_SWIO | HCR_TIDCP) | 70 | HCR_SWIO | HCR_TIDCP) |
71 | #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF) | 71 | #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF) |
72 | 72 | ||
73 | /* System Control Register (SCTLR) bits */ | ||
74 | #define SCTLR_TE (1 << 30) | ||
75 | #define SCTLR_EE (1 << 25) | ||
76 | #define SCTLR_V (1 << 13) | ||
77 | |||
73 | /* Hyp System Control Register (HSCTLR) bits */ | 78 | /* Hyp System Control Register (HSCTLR) bits */ |
74 | #define HSCTLR_TE (1 << 30) | 79 | #define HSCTLR_TE (1 << 30) |
75 | #define HSCTLR_EE (1 << 25) | 80 | #define HSCTLR_EE (1 << 25) |
@@ -171,6 +176,10 @@ | |||
171 | #define HSR_FSC (0x3f) | 176 | #define HSR_FSC (0x3f) |
172 | #define HSR_FSC_TYPE (0x3c) | 177 | #define HSR_FSC_TYPE (0x3c) |
173 | #define HSR_WNR (1 << 6) | 178 | #define HSR_WNR (1 << 6) |
179 | #define HSR_CV_SHIFT (24) | ||
180 | #define HSR_CV (1U << HSR_CV_SHIFT) | ||
181 | #define HSR_COND_SHIFT (20) | ||
182 | #define HSR_COND (0xfU << HSR_COND_SHIFT) | ||
174 | 183 | ||
175 | #define FSC_FAULT (0x04) | 184 | #define FSC_FAULT (0x04) |
176 | #define FSC_PERM (0x0c) | 185 | #define FSC_PERM (0x0c) |
@@ -197,4 +206,6 @@ | |||
197 | #define HSR_EC_DABT (0x24) | 206 | #define HSR_EC_DABT (0x24) |
198 | #define HSR_EC_DABT_HYP (0x25) | 207 | #define HSR_EC_DABT_HYP (0x25) |
199 | 208 | ||
209 | #define HSR_HVC_IMM_MASK ((1UL << 16) - 1) | ||
210 | |||
200 | #endif /* __ARM_KVM_ARM_H__ */ | 211 | #endif /* __ARM_KVM_ARM_H__ */ |
diff --git a/arch/arm/include/asm/kvm_coproc.h b/arch/arm/include/asm/kvm_coproc.h index b6d023deb426..bd1ace030495 100644 --- a/arch/arm/include/asm/kvm_coproc.h +++ b/arch/arm/include/asm/kvm_coproc.h | |||
@@ -21,4 +21,18 @@ | |||
21 | 21 | ||
22 | void kvm_reset_coprocs(struct kvm_vcpu *vcpu); | 22 | void kvm_reset_coprocs(struct kvm_vcpu *vcpu); |
23 | 23 | ||
24 | struct kvm_coproc_target_table { | ||
25 | unsigned target; | ||
26 | const struct coproc_reg *table; | ||
27 | size_t num; | ||
28 | }; | ||
29 | void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table); | ||
30 | |||
31 | int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
32 | int kvm_handle_cp_0_13_access(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
33 | int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
34 | int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
35 | int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
36 | int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
37 | void kvm_coproc_table_init(void); | ||
24 | #endif /* __ARM_KVM_COPROC_H__ */ | 38 | #endif /* __ARM_KVM_COPROC_H__ */ |
diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h index 17dad674b90f..01a755b80632 100644 --- a/arch/arm/include/asm/kvm_emulate.h +++ b/arch/arm/include/asm/kvm_emulate.h | |||
@@ -25,6 +25,12 @@ | |||
25 | u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); | 25 | u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); |
26 | u32 *vcpu_spsr(struct kvm_vcpu *vcpu); | 26 | u32 *vcpu_spsr(struct kvm_vcpu *vcpu); |
27 | 27 | ||
28 | int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
29 | void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr); | ||
30 | void kvm_inject_undefined(struct kvm_vcpu *vcpu); | ||
31 | void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); | ||
32 | void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); | ||
33 | |||
28 | static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu) | 34 | static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu) |
29 | { | 35 | { |
30 | return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc; | 36 | return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc; |
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 7a121089c733..e1d4168d4f19 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h | |||
@@ -94,6 +94,10 @@ struct kvm_vcpu_arch { | |||
94 | * Anything that is not used directly from assembly code goes | 94 | * Anything that is not used directly from assembly code goes |
95 | * here. | 95 | * here. |
96 | */ | 96 | */ |
97 | /* dcache set/way operation pending */ | ||
98 | int last_pcpu; | ||
99 | cpumask_t require_dcache_flush; | ||
100 | |||
97 | /* Interrupt related fields */ | 101 | /* Interrupt related fields */ |
98 | u32 irq_lines; /* IRQ and FIQ levels */ | 102 | u32 irq_lines; /* IRQ and FIQ levels */ |
99 | 103 | ||
diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index dfc293f277b3..88edce6c97d4 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile | |||
@@ -18,4 +18,4 @@ kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) | |||
18 | 18 | ||
19 | obj-y += kvm-arm.o init.o interrupts.o | 19 | obj-y += kvm-arm.o init.o interrupts.o |
20 | obj-y += arm.o guest.o mmu.o emulate.o reset.o | 20 | obj-y += arm.o guest.o mmu.o emulate.o reset.o |
21 | obj-y += coproc.o | 21 | obj-y += coproc.o coproc_a15.o |
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 9e9fa4477884..be06c5de51e3 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c | |||
@@ -36,11 +36,14 @@ | |||
36 | #include <asm/mman.h> | 36 | #include <asm/mman.h> |
37 | #include <asm/cputype.h> | 37 | #include <asm/cputype.h> |
38 | #include <asm/tlbflush.h> | 38 | #include <asm/tlbflush.h> |
39 | #include <asm/cacheflush.h> | ||
39 | #include <asm/virt.h> | 40 | #include <asm/virt.h> |
40 | #include <asm/kvm_arm.h> | 41 | #include <asm/kvm_arm.h> |
41 | #include <asm/kvm_asm.h> | 42 | #include <asm/kvm_asm.h> |
42 | #include <asm/kvm_mmu.h> | 43 | #include <asm/kvm_mmu.h> |
43 | #include <asm/kvm_emulate.h> | 44 | #include <asm/kvm_emulate.h> |
45 | #include <asm/kvm_coproc.h> | ||
46 | #include <asm/opcodes.h> | ||
44 | 47 | ||
45 | #ifdef REQUIRES_VIRT | 48 | #ifdef REQUIRES_VIRT |
46 | __asm__(".arch_extension virt"); | 49 | __asm__(".arch_extension virt"); |
@@ -294,6 +297,15 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) | |||
294 | { | 297 | { |
295 | vcpu->cpu = cpu; | 298 | vcpu->cpu = cpu; |
296 | vcpu->arch.vfp_host = this_cpu_ptr(kvm_host_vfp_state); | 299 | vcpu->arch.vfp_host = this_cpu_ptr(kvm_host_vfp_state); |
300 | |||
301 | /* | ||
302 | * Check whether this vcpu requires the cache to be flushed on | ||
303 | * this physical CPU. This is a consequence of doing dcache | ||
304 | * operations by set/way on this vcpu. We do it here to be in | ||
305 | * a non-preemptible section. | ||
306 | */ | ||
307 | if (cpumask_test_and_clear_cpu(cpu, &vcpu->arch.require_dcache_flush)) | ||
308 | flush_cache_all(); /* We'd really want v7_flush_dcache_all() */ | ||
297 | } | 309 | } |
298 | 310 | ||
299 | void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) | 311 | void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) |
@@ -319,9 +331,16 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, | |||
319 | return -EINVAL; | 331 | return -EINVAL; |
320 | } | 332 | } |
321 | 333 | ||
334 | /** | ||
335 | * kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled | ||
336 | * @v: The VCPU pointer | ||
337 | * | ||
338 | * If the guest CPU is not waiting for interrupts or an interrupt line is | ||
339 | * asserted, the CPU is by definition runnable. | ||
340 | */ | ||
322 | int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) | 341 | int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) |
323 | { | 342 | { |
324 | return 0; | 343 | return !!v->arch.irq_lines; |
325 | } | 344 | } |
326 | 345 | ||
327 | /* Just ensure a guest exit from a particular CPU */ | 346 | /* Just ensure a guest exit from a particular CPU */ |
@@ -411,6 +430,110 @@ static void update_vttbr(struct kvm *kvm) | |||
411 | spin_unlock(&kvm_vmid_lock); | 430 | spin_unlock(&kvm_vmid_lock); |
412 | } | 431 | } |
413 | 432 | ||
433 | static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
434 | { | ||
435 | /* SVC called from Hyp mode should never get here */ | ||
436 | kvm_debug("SVC called from Hyp mode shouldn't go here\n"); | ||
437 | BUG(); | ||
438 | return -EINVAL; /* Squash warning */ | ||
439 | } | ||
440 | |||
441 | static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
442 | { | ||
443 | trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0), | ||
444 | vcpu->arch.hsr & HSR_HVC_IMM_MASK); | ||
445 | |||
446 | kvm_inject_undefined(vcpu); | ||
447 | return 1; | ||
448 | } | ||
449 | |||
450 | static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
451 | { | ||
452 | /* We don't support SMC; don't do that. */ | ||
453 | kvm_debug("smc: at %08x", *vcpu_pc(vcpu)); | ||
454 | kvm_inject_undefined(vcpu); | ||
455 | return 1; | ||
456 | } | ||
457 | |||
458 | static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
459 | { | ||
460 | /* The hypervisor should never cause aborts */ | ||
461 | kvm_err("Prefetch Abort taken from Hyp mode at %#08x (HSR: %#08x)\n", | ||
462 | vcpu->arch.hxfar, vcpu->arch.hsr); | ||
463 | return -EFAULT; | ||
464 | } | ||
465 | |||
466 | static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
467 | { | ||
468 | /* This is either an error in the ws. code or an external abort */ | ||
469 | kvm_err("Data Abort taken from Hyp mode at %#08x (HSR: %#08x)\n", | ||
470 | vcpu->arch.hxfar, vcpu->arch.hsr); | ||
471 | return -EFAULT; | ||
472 | } | ||
473 | |||
474 | typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); | ||
475 | static exit_handle_fn arm_exit_handlers[] = { | ||
476 | [HSR_EC_WFI] = kvm_handle_wfi, | ||
477 | [HSR_EC_CP15_32] = kvm_handle_cp15_32, | ||
478 | [HSR_EC_CP15_64] = kvm_handle_cp15_64, | ||
479 | [HSR_EC_CP14_MR] = kvm_handle_cp14_access, | ||
480 | [HSR_EC_CP14_LS] = kvm_handle_cp14_load_store, | ||
481 | [HSR_EC_CP14_64] = kvm_handle_cp14_access, | ||
482 | [HSR_EC_CP_0_13] = kvm_handle_cp_0_13_access, | ||
483 | [HSR_EC_CP10_ID] = kvm_handle_cp10_id, | ||
484 | [HSR_EC_SVC_HYP] = handle_svc_hyp, | ||
485 | [HSR_EC_HVC] = handle_hvc, | ||
486 | [HSR_EC_SMC] = handle_smc, | ||
487 | [HSR_EC_IABT] = kvm_handle_guest_abort, | ||
488 | [HSR_EC_IABT_HYP] = handle_pabt_hyp, | ||
489 | [HSR_EC_DABT] = kvm_handle_guest_abort, | ||
490 | [HSR_EC_DABT_HYP] = handle_dabt_hyp, | ||
491 | }; | ||
492 | |||
493 | /* | ||
494 | * A conditional instruction is allowed to trap, even though it | ||
495 | * wouldn't be executed. So let's re-implement the hardware, in | ||
496 | * software! | ||
497 | */ | ||
498 | static bool kvm_condition_valid(struct kvm_vcpu *vcpu) | ||
499 | { | ||
500 | unsigned long cpsr, cond, insn; | ||
501 | |||
502 | /* | ||
503 | * Exception Code 0 can only happen if we set HCR.TGE to 1, to | ||
504 | * catch undefined instructions, and then we won't get past | ||
505 | * the arm_exit_handlers test anyway. | ||
506 | */ | ||
507 | BUG_ON(((vcpu->arch.hsr & HSR_EC) >> HSR_EC_SHIFT) == 0); | ||
508 | |||
509 | /* Top two bits non-zero? Unconditional. */ | ||
510 | if (vcpu->arch.hsr >> 30) | ||
511 | return true; | ||
512 | |||
513 | cpsr = *vcpu_cpsr(vcpu); | ||
514 | |||
515 | /* Is condition field valid? */ | ||
516 | if ((vcpu->arch.hsr & HSR_CV) >> HSR_CV_SHIFT) | ||
517 | cond = (vcpu->arch.hsr & HSR_COND) >> HSR_COND_SHIFT; | ||
518 | else { | ||
519 | /* This can happen in Thumb mode: examine IT state. */ | ||
520 | unsigned long it; | ||
521 | |||
522 | it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3); | ||
523 | |||
524 | /* it == 0 => unconditional. */ | ||
525 | if (it == 0) | ||
526 | return true; | ||
527 | |||
528 | /* The cond for this insn works out as the top 4 bits. */ | ||
529 | cond = (it >> 4); | ||
530 | } | ||
531 | |||
532 | /* Shift makes it look like an ARM-mode instruction */ | ||
533 | insn = cond << 28; | ||
534 | return arm_check_condition(insn, cpsr) != ARM_OPCODE_CONDTEST_FAIL; | ||
535 | } | ||
536 | |||
414 | /* | 537 | /* |
415 | * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on | 538 | * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on |
416 | * proper exit to QEMU. | 539 | * proper exit to QEMU. |
@@ -418,8 +541,46 @@ static void update_vttbr(struct kvm *kvm) | |||
418 | static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, | 541 | static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, |
419 | int exception_index) | 542 | int exception_index) |
420 | { | 543 | { |
421 | run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | 544 | unsigned long hsr_ec; |
422 | return 0; | 545 | |
546 | switch (exception_index) { | ||
547 | case ARM_EXCEPTION_IRQ: | ||
548 | return 1; | ||
549 | case ARM_EXCEPTION_UNDEFINED: | ||
550 | kvm_err("Undefined exception in Hyp mode at: %#08x\n", | ||
551 | vcpu->arch.hyp_pc); | ||
552 | BUG(); | ||
553 | panic("KVM: Hypervisor undefined exception!\n"); | ||
554 | case ARM_EXCEPTION_DATA_ABORT: | ||
555 | case ARM_EXCEPTION_PREF_ABORT: | ||
556 | case ARM_EXCEPTION_HVC: | ||
557 | hsr_ec = (vcpu->arch.hsr & HSR_EC) >> HSR_EC_SHIFT; | ||
558 | |||
559 | if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) | ||
560 | || !arm_exit_handlers[hsr_ec]) { | ||
561 | kvm_err("Unkown exception class: %#08lx, " | ||
562 | "hsr: %#08x\n", hsr_ec, | ||
563 | (unsigned int)vcpu->arch.hsr); | ||
564 | BUG(); | ||
565 | } | ||
566 | |||
567 | /* | ||
568 | * See ARM ARM B1.14.1: "Hyp traps on instructions | ||
569 | * that fail their condition code check" | ||
570 | */ | ||
571 | if (!kvm_condition_valid(vcpu)) { | ||
572 | bool is_wide = vcpu->arch.hsr & HSR_IL; | ||
573 | kvm_skip_instr(vcpu, is_wide); | ||
574 | return 1; | ||
575 | } | ||
576 | |||
577 | return arm_exit_handlers[hsr_ec](vcpu, run); | ||
578 | default: | ||
579 | kvm_pr_unimpl("Unsupported exception type: %d", | ||
580 | exception_index); | ||
581 | run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | ||
582 | return 0; | ||
583 | } | ||
423 | } | 584 | } |
424 | 585 | ||
425 | static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) | 586 | static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) |
@@ -493,6 +654,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
493 | ret = kvm_call_hyp(__kvm_vcpu_run, vcpu); | 654 | ret = kvm_call_hyp(__kvm_vcpu_run, vcpu); |
494 | 655 | ||
495 | vcpu->mode = OUTSIDE_GUEST_MODE; | 656 | vcpu->mode = OUTSIDE_GUEST_MODE; |
657 | vcpu->arch.last_pcpu = smp_processor_id(); | ||
496 | kvm_guest_exit(); | 658 | kvm_guest_exit(); |
497 | trace_kvm_exit(*vcpu_pc(vcpu)); | 659 | trace_kvm_exit(*vcpu_pc(vcpu)); |
498 | /* | 660 | /* |
@@ -801,6 +963,7 @@ int kvm_arch_init(void *opaque) | |||
801 | if (err) | 963 | if (err) |
802 | goto out_err; | 964 | goto out_err; |
803 | 965 | ||
966 | kvm_coproc_table_init(); | ||
804 | return 0; | 967 | return 0; |
805 | out_err: | 968 | out_err: |
806 | return err; | 969 | return err; |
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index 0c433558591c..722efe3b1675 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c | |||
@@ -16,8 +16,368 @@ | |||
16 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 17 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | */ | 18 | */ |
19 | #include <linux/mm.h> | ||
19 | #include <linux/kvm_host.h> | 20 | #include <linux/kvm_host.h> |
21 | #include <asm/kvm_arm.h> | ||
22 | #include <asm/kvm_host.h> | ||
23 | #include <asm/kvm_emulate.h> | ||
24 | #include <asm/kvm_coproc.h> | ||
25 | #include <asm/cacheflush.h> | ||
26 | #include <asm/cputype.h> | ||
27 | #include <trace/events/kvm.h> | ||
20 | 28 | ||
29 | #include "trace.h" | ||
30 | #include "coproc.h" | ||
31 | |||
32 | |||
33 | /****************************************************************************** | ||
34 | * Co-processor emulation | ||
35 | *****************************************************************************/ | ||
36 | |||
37 | int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
38 | { | ||
39 | kvm_inject_undefined(vcpu); | ||
40 | return 1; | ||
41 | } | ||
42 | |||
43 | int kvm_handle_cp_0_13_access(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
44 | { | ||
45 | /* | ||
46 | * We can get here, if the host has been built without VFPv3 support, | ||
47 | * but the guest attempted a floating point operation. | ||
48 | */ | ||
49 | kvm_inject_undefined(vcpu); | ||
50 | return 1; | ||
51 | } | ||
52 | |||
53 | int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
54 | { | ||
55 | kvm_inject_undefined(vcpu); | ||
56 | return 1; | ||
57 | } | ||
58 | |||
59 | int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
60 | { | ||
61 | kvm_inject_undefined(vcpu); | ||
62 | return 1; | ||
63 | } | ||
64 | |||
65 | /* See note at ARM ARM B1.14.4 */ | ||
66 | static bool access_dcsw(struct kvm_vcpu *vcpu, | ||
67 | const struct coproc_params *p, | ||
68 | const struct coproc_reg *r) | ||
69 | { | ||
70 | u32 val; | ||
71 | int cpu; | ||
72 | |||
73 | cpu = get_cpu(); | ||
74 | |||
75 | if (!p->is_write) | ||
76 | return read_from_write_only(vcpu, p); | ||
77 | |||
78 | cpumask_setall(&vcpu->arch.require_dcache_flush); | ||
79 | cpumask_clear_cpu(cpu, &vcpu->arch.require_dcache_flush); | ||
80 | |||
81 | /* If we were already preempted, take the long way around */ | ||
82 | if (cpu != vcpu->arch.last_pcpu) { | ||
83 | flush_cache_all(); | ||
84 | goto done; | ||
85 | } | ||
86 | |||
87 | val = *vcpu_reg(vcpu, p->Rt1); | ||
88 | |||
89 | switch (p->CRm) { | ||
90 | case 6: /* Upgrade DCISW to DCCISW, as per HCR.SWIO */ | ||
91 | case 14: /* DCCISW */ | ||
92 | asm volatile("mcr p15, 0, %0, c7, c14, 2" : : "r" (val)); | ||
93 | break; | ||
94 | |||
95 | case 10: /* DCCSW */ | ||
96 | asm volatile("mcr p15, 0, %0, c7, c10, 2" : : "r" (val)); | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | done: | ||
101 | put_cpu(); | ||
102 | |||
103 | return true; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * We could trap ID_DFR0 and tell the guest we don't support performance | ||
108 | * monitoring. Unfortunately the patch to make the kernel check ID_DFR0 was | ||
109 | * NAKed, so it will read the PMCR anyway. | ||
110 | * | ||
111 | * Therefore we tell the guest we have 0 counters. Unfortunately, we | ||
112 | * must always support PMCCNTR (the cycle counter): we just RAZ/WI for | ||
113 | * all PM registers, which doesn't crash the guest kernel at least. | ||
114 | */ | ||
115 | static bool pm_fake(struct kvm_vcpu *vcpu, | ||
116 | const struct coproc_params *p, | ||
117 | const struct coproc_reg *r) | ||
118 | { | ||
119 | if (p->is_write) | ||
120 | return ignore_write(vcpu, p); | ||
121 | else | ||
122 | return read_zero(vcpu, p); | ||
123 | } | ||
124 | |||
125 | #define access_pmcr pm_fake | ||
126 | #define access_pmcntenset pm_fake | ||
127 | #define access_pmcntenclr pm_fake | ||
128 | #define access_pmovsr pm_fake | ||
129 | #define access_pmselr pm_fake | ||
130 | #define access_pmceid0 pm_fake | ||
131 | #define access_pmceid1 pm_fake | ||
132 | #define access_pmccntr pm_fake | ||
133 | #define access_pmxevtyper pm_fake | ||
134 | #define access_pmxevcntr pm_fake | ||
135 | #define access_pmuserenr pm_fake | ||
136 | #define access_pmintenset pm_fake | ||
137 | #define access_pmintenclr pm_fake | ||
138 | |||
139 | /* Architected CP15 registers. | ||
140 | * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 | ||
141 | */ | ||
142 | static const struct coproc_reg cp15_regs[] = { | ||
143 | /* CSSELR: swapped by interrupt.S. */ | ||
144 | { CRn( 0), CRm( 0), Op1( 2), Op2( 0), is32, | ||
145 | NULL, reset_unknown, c0_CSSELR }, | ||
146 | |||
147 | /* TTBR0/TTBR1: swapped by interrupt.S. */ | ||
148 | { CRm( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, | ||
149 | { CRm( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, | ||
150 | |||
151 | /* TTBCR: swapped by interrupt.S. */ | ||
152 | { CRn( 2), CRm( 0), Op1( 0), Op2( 2), is32, | ||
153 | NULL, reset_val, c2_TTBCR, 0x00000000 }, | ||
154 | |||
155 | /* DACR: swapped by interrupt.S. */ | ||
156 | { CRn( 3), CRm( 0), Op1( 0), Op2( 0), is32, | ||
157 | NULL, reset_unknown, c3_DACR }, | ||
158 | |||
159 | /* DFSR/IFSR/ADFSR/AIFSR: swapped by interrupt.S. */ | ||
160 | { CRn( 5), CRm( 0), Op1( 0), Op2( 0), is32, | ||
161 | NULL, reset_unknown, c5_DFSR }, | ||
162 | { CRn( 5), CRm( 0), Op1( 0), Op2( 1), is32, | ||
163 | NULL, reset_unknown, c5_IFSR }, | ||
164 | { CRn( 5), CRm( 1), Op1( 0), Op2( 0), is32, | ||
165 | NULL, reset_unknown, c5_ADFSR }, | ||
166 | { CRn( 5), CRm( 1), Op1( 0), Op2( 1), is32, | ||
167 | NULL, reset_unknown, c5_AIFSR }, | ||
168 | |||
169 | /* DFAR/IFAR: swapped by interrupt.S. */ | ||
170 | { CRn( 6), CRm( 0), Op1( 0), Op2( 0), is32, | ||
171 | NULL, reset_unknown, c6_DFAR }, | ||
172 | { CRn( 6), CRm( 0), Op1( 0), Op2( 2), is32, | ||
173 | NULL, reset_unknown, c6_IFAR }, | ||
174 | /* | ||
175 | * DC{C,I,CI}SW operations: | ||
176 | */ | ||
177 | { CRn( 7), CRm( 6), Op1( 0), Op2( 2), is32, access_dcsw}, | ||
178 | { CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, access_dcsw}, | ||
179 | { CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, access_dcsw}, | ||
180 | /* | ||
181 | * Dummy performance monitor implementation. | ||
182 | */ | ||
183 | { CRn( 9), CRm(12), Op1( 0), Op2( 0), is32, access_pmcr}, | ||
184 | { CRn( 9), CRm(12), Op1( 0), Op2( 1), is32, access_pmcntenset}, | ||
185 | { CRn( 9), CRm(12), Op1( 0), Op2( 2), is32, access_pmcntenclr}, | ||
186 | { CRn( 9), CRm(12), Op1( 0), Op2( 3), is32, access_pmovsr}, | ||
187 | { CRn( 9), CRm(12), Op1( 0), Op2( 5), is32, access_pmselr}, | ||
188 | { CRn( 9), CRm(12), Op1( 0), Op2( 6), is32, access_pmceid0}, | ||
189 | { CRn( 9), CRm(12), Op1( 0), Op2( 7), is32, access_pmceid1}, | ||
190 | { CRn( 9), CRm(13), Op1( 0), Op2( 0), is32, access_pmccntr}, | ||
191 | { CRn( 9), CRm(13), Op1( 0), Op2( 1), is32, access_pmxevtyper}, | ||
192 | { CRn( 9), CRm(13), Op1( 0), Op2( 2), is32, access_pmxevcntr}, | ||
193 | { CRn( 9), CRm(14), Op1( 0), Op2( 0), is32, access_pmuserenr}, | ||
194 | { CRn( 9), CRm(14), Op1( 0), Op2( 1), is32, access_pmintenset}, | ||
195 | { CRn( 9), CRm(14), Op1( 0), Op2( 2), is32, access_pmintenclr}, | ||
196 | |||
197 | /* PRRR/NMRR (aka MAIR0/MAIR1): swapped by interrupt.S. */ | ||
198 | { CRn(10), CRm( 2), Op1( 0), Op2( 0), is32, | ||
199 | NULL, reset_unknown, c10_PRRR}, | ||
200 | { CRn(10), CRm( 2), Op1( 0), Op2( 1), is32, | ||
201 | NULL, reset_unknown, c10_NMRR}, | ||
202 | |||
203 | /* VBAR: swapped by interrupt.S. */ | ||
204 | { CRn(12), CRm( 0), Op1( 0), Op2( 0), is32, | ||
205 | NULL, reset_val, c12_VBAR, 0x00000000 }, | ||
206 | |||
207 | /* CONTEXTIDR/TPIDRURW/TPIDRURO/TPIDRPRW: swapped by interrupt.S. */ | ||
208 | { CRn(13), CRm( 0), Op1( 0), Op2( 1), is32, | ||
209 | NULL, reset_val, c13_CID, 0x00000000 }, | ||
210 | { CRn(13), CRm( 0), Op1( 0), Op2( 2), is32, | ||
211 | NULL, reset_unknown, c13_TID_URW }, | ||
212 | { CRn(13), CRm( 0), Op1( 0), Op2( 3), is32, | ||
213 | NULL, reset_unknown, c13_TID_URO }, | ||
214 | { CRn(13), CRm( 0), Op1( 0), Op2( 4), is32, | ||
215 | NULL, reset_unknown, c13_TID_PRIV }, | ||
216 | }; | ||
217 | |||
218 | /* Target specific emulation tables */ | ||
219 | static struct kvm_coproc_target_table *target_tables[KVM_ARM_NUM_TARGETS]; | ||
220 | |||
221 | void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table) | ||
222 | { | ||
223 | target_tables[table->target] = table; | ||
224 | } | ||
225 | |||
226 | /* Get specific register table for this target. */ | ||
227 | static const struct coproc_reg *get_target_table(unsigned target, size_t *num) | ||
228 | { | ||
229 | struct kvm_coproc_target_table *table; | ||
230 | |||
231 | table = target_tables[target]; | ||
232 | *num = table->num; | ||
233 | return table->table; | ||
234 | } | ||
235 | |||
236 | static const struct coproc_reg *find_reg(const struct coproc_params *params, | ||
237 | const struct coproc_reg table[], | ||
238 | unsigned int num) | ||
239 | { | ||
240 | unsigned int i; | ||
241 | |||
242 | for (i = 0; i < num; i++) { | ||
243 | const struct coproc_reg *r = &table[i]; | ||
244 | |||
245 | if (params->is_64bit != r->is_64) | ||
246 | continue; | ||
247 | if (params->CRn != r->CRn) | ||
248 | continue; | ||
249 | if (params->CRm != r->CRm) | ||
250 | continue; | ||
251 | if (params->Op1 != r->Op1) | ||
252 | continue; | ||
253 | if (params->Op2 != r->Op2) | ||
254 | continue; | ||
255 | |||
256 | return r; | ||
257 | } | ||
258 | return NULL; | ||
259 | } | ||
260 | |||
261 | static int emulate_cp15(struct kvm_vcpu *vcpu, | ||
262 | const struct coproc_params *params) | ||
263 | { | ||
264 | size_t num; | ||
265 | const struct coproc_reg *table, *r; | ||
266 | |||
267 | trace_kvm_emulate_cp15_imp(params->Op1, params->Rt1, params->CRn, | ||
268 | params->CRm, params->Op2, params->is_write); | ||
269 | |||
270 | table = get_target_table(vcpu->arch.target, &num); | ||
271 | |||
272 | /* Search target-specific then generic table. */ | ||
273 | r = find_reg(params, table, num); | ||
274 | if (!r) | ||
275 | r = find_reg(params, cp15_regs, ARRAY_SIZE(cp15_regs)); | ||
276 | |||
277 | if (likely(r)) { | ||
278 | /* If we don't have an accessor, we should never get here! */ | ||
279 | BUG_ON(!r->access); | ||
280 | |||
281 | if (likely(r->access(vcpu, params, r))) { | ||
282 | /* Skip instruction, since it was emulated */ | ||
283 | kvm_skip_instr(vcpu, (vcpu->arch.hsr >> 25) & 1); | ||
284 | return 1; | ||
285 | } | ||
286 | /* If access function fails, it should complain. */ | ||
287 | } else { | ||
288 | kvm_err("Unsupported guest CP15 access at: %08x\n", | ||
289 | *vcpu_pc(vcpu)); | ||
290 | print_cp_instr(params); | ||
291 | } | ||
292 | kvm_inject_undefined(vcpu); | ||
293 | return 1; | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * kvm_handle_cp15_64 -- handles a mrrc/mcrr trap on a guest CP15 access | ||
298 | * @vcpu: The VCPU pointer | ||
299 | * @run: The kvm_run struct | ||
300 | */ | ||
301 | int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
302 | { | ||
303 | struct coproc_params params; | ||
304 | |||
305 | params.CRm = (vcpu->arch.hsr >> 1) & 0xf; | ||
306 | params.Rt1 = (vcpu->arch.hsr >> 5) & 0xf; | ||
307 | params.is_write = ((vcpu->arch.hsr & 1) == 0); | ||
308 | params.is_64bit = true; | ||
309 | |||
310 | params.Op1 = (vcpu->arch.hsr >> 16) & 0xf; | ||
311 | params.Op2 = 0; | ||
312 | params.Rt2 = (vcpu->arch.hsr >> 10) & 0xf; | ||
313 | params.CRn = 0; | ||
314 | |||
315 | return emulate_cp15(vcpu, ¶ms); | ||
316 | } | ||
317 | |||
318 | static void reset_coproc_regs(struct kvm_vcpu *vcpu, | ||
319 | const struct coproc_reg *table, size_t num) | ||
320 | { | ||
321 | unsigned long i; | ||
322 | |||
323 | for (i = 0; i < num; i++) | ||
324 | if (table[i].reset) | ||
325 | table[i].reset(vcpu, &table[i]); | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * kvm_handle_cp15_32 -- handles a mrc/mcr trap on a guest CP15 access | ||
330 | * @vcpu: The VCPU pointer | ||
331 | * @run: The kvm_run struct | ||
332 | */ | ||
333 | int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
334 | { | ||
335 | struct coproc_params params; | ||
336 | |||
337 | params.CRm = (vcpu->arch.hsr >> 1) & 0xf; | ||
338 | params.Rt1 = (vcpu->arch.hsr >> 5) & 0xf; | ||
339 | params.is_write = ((vcpu->arch.hsr & 1) == 0); | ||
340 | params.is_64bit = false; | ||
341 | |||
342 | params.CRn = (vcpu->arch.hsr >> 10) & 0xf; | ||
343 | params.Op1 = (vcpu->arch.hsr >> 14) & 0x7; | ||
344 | params.Op2 = (vcpu->arch.hsr >> 17) & 0x7; | ||
345 | params.Rt2 = 0; | ||
346 | |||
347 | return emulate_cp15(vcpu, ¶ms); | ||
348 | } | ||
349 | |||
350 | void kvm_coproc_table_init(void) | ||
351 | { | ||
352 | unsigned int i; | ||
353 | |||
354 | /* Make sure tables are unique and in order. */ | ||
355 | for (i = 1; i < ARRAY_SIZE(cp15_regs); i++) | ||
356 | BUG_ON(cmp_reg(&cp15_regs[i-1], &cp15_regs[i]) >= 0); | ||
357 | } | ||
358 | |||
359 | /** | ||
360 | * kvm_reset_coprocs - sets cp15 registers to reset value | ||
361 | * @vcpu: The VCPU pointer | ||
362 | * | ||
363 | * This function finds the right table above and sets the registers on the | ||
364 | * virtual CPU struct to their architecturally defined reset values. | ||
365 | */ | ||
21 | void kvm_reset_coprocs(struct kvm_vcpu *vcpu) | 366 | void kvm_reset_coprocs(struct kvm_vcpu *vcpu) |
22 | { | 367 | { |
368 | size_t num; | ||
369 | const struct coproc_reg *table; | ||
370 | |||
371 | /* Catch someone adding a register without putting in reset entry. */ | ||
372 | memset(vcpu->arch.cp15, 0x42, sizeof(vcpu->arch.cp15)); | ||
373 | |||
374 | /* Generic chip reset first (so target could override). */ | ||
375 | reset_coproc_regs(vcpu, cp15_regs, ARRAY_SIZE(cp15_regs)); | ||
376 | |||
377 | table = get_target_table(vcpu->arch.target, &num); | ||
378 | reset_coproc_regs(vcpu, table, num); | ||
379 | |||
380 | for (num = 1; num < NR_CP15_REGS; num++) | ||
381 | if (vcpu->arch.cp15[num] == 0x42424242) | ||
382 | panic("Didn't reset vcpu->arch.cp15[%zi]", num); | ||
23 | } | 383 | } |
diff --git a/arch/arm/kvm/coproc.h b/arch/arm/kvm/coproc.h new file mode 100644 index 000000000000..992adfafa2ff --- /dev/null +++ b/arch/arm/kvm/coproc.h | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | ||
3 | * Authors: Christoffer Dall <c.dall@virtualopensystems.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License, version 2, as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | ||
18 | |||
19 | #ifndef __ARM_KVM_COPROC_LOCAL_H__ | ||
20 | #define __ARM_KVM_COPROC_LOCAL_H__ | ||
21 | |||
22 | struct coproc_params { | ||
23 | unsigned long CRn; | ||
24 | unsigned long CRm; | ||
25 | unsigned long Op1; | ||
26 | unsigned long Op2; | ||
27 | unsigned long Rt1; | ||
28 | unsigned long Rt2; | ||
29 | bool is_64bit; | ||
30 | bool is_write; | ||
31 | }; | ||
32 | |||
33 | struct coproc_reg { | ||
34 | /* MRC/MCR/MRRC/MCRR instruction which accesses it. */ | ||
35 | unsigned long CRn; | ||
36 | unsigned long CRm; | ||
37 | unsigned long Op1; | ||
38 | unsigned long Op2; | ||
39 | |||
40 | bool is_64; | ||
41 | |||
42 | /* Trapped access from guest, if non-NULL. */ | ||
43 | bool (*access)(struct kvm_vcpu *, | ||
44 | const struct coproc_params *, | ||
45 | const struct coproc_reg *); | ||
46 | |||
47 | /* Initialization for vcpu. */ | ||
48 | void (*reset)(struct kvm_vcpu *, const struct coproc_reg *); | ||
49 | |||
50 | /* Index into vcpu->arch.cp15[], or 0 if we don't need to save it. */ | ||
51 | unsigned long reg; | ||
52 | |||
53 | /* Value (usually reset value) */ | ||
54 | u64 val; | ||
55 | }; | ||
56 | |||
57 | static inline void print_cp_instr(const struct coproc_params *p) | ||
58 | { | ||
59 | /* Look, we even formatted it for you to paste into the table! */ | ||
60 | if (p->is_64bit) { | ||
61 | kvm_pr_unimpl(" { CRm(%2lu), Op1(%2lu), is64, func_%s },\n", | ||
62 | p->CRm, p->Op1, p->is_write ? "write" : "read"); | ||
63 | } else { | ||
64 | kvm_pr_unimpl(" { CRn(%2lu), CRm(%2lu), Op1(%2lu), Op2(%2lu), is32," | ||
65 | " func_%s },\n", | ||
66 | p->CRn, p->CRm, p->Op1, p->Op2, | ||
67 | p->is_write ? "write" : "read"); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static inline bool ignore_write(struct kvm_vcpu *vcpu, | ||
72 | const struct coproc_params *p) | ||
73 | { | ||
74 | return true; | ||
75 | } | ||
76 | |||
77 | static inline bool read_zero(struct kvm_vcpu *vcpu, | ||
78 | const struct coproc_params *p) | ||
79 | { | ||
80 | *vcpu_reg(vcpu, p->Rt1) = 0; | ||
81 | return true; | ||
82 | } | ||
83 | |||
84 | static inline bool write_to_read_only(struct kvm_vcpu *vcpu, | ||
85 | const struct coproc_params *params) | ||
86 | { | ||
87 | kvm_debug("CP15 write to read-only register at: %08x\n", | ||
88 | *vcpu_pc(vcpu)); | ||
89 | print_cp_instr(params); | ||
90 | return false; | ||
91 | } | ||
92 | |||
93 | static inline bool read_from_write_only(struct kvm_vcpu *vcpu, | ||
94 | const struct coproc_params *params) | ||
95 | { | ||
96 | kvm_debug("CP15 read to write-only register at: %08x\n", | ||
97 | *vcpu_pc(vcpu)); | ||
98 | print_cp_instr(params); | ||
99 | return false; | ||
100 | } | ||
101 | |||
102 | /* Reset functions */ | ||
103 | static inline void reset_unknown(struct kvm_vcpu *vcpu, | ||
104 | const struct coproc_reg *r) | ||
105 | { | ||
106 | BUG_ON(!r->reg); | ||
107 | BUG_ON(r->reg >= ARRAY_SIZE(vcpu->arch.cp15)); | ||
108 | vcpu->arch.cp15[r->reg] = 0xdecafbad; | ||
109 | } | ||
110 | |||
111 | static inline void reset_val(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||
112 | { | ||
113 | BUG_ON(!r->reg); | ||
114 | BUG_ON(r->reg >= ARRAY_SIZE(vcpu->arch.cp15)); | ||
115 | vcpu->arch.cp15[r->reg] = r->val; | ||
116 | } | ||
117 | |||
118 | static inline void reset_unknown64(struct kvm_vcpu *vcpu, | ||
119 | const struct coproc_reg *r) | ||
120 | { | ||
121 | BUG_ON(!r->reg); | ||
122 | BUG_ON(r->reg + 1 >= ARRAY_SIZE(vcpu->arch.cp15)); | ||
123 | |||
124 | vcpu->arch.cp15[r->reg] = 0xdecafbad; | ||
125 | vcpu->arch.cp15[r->reg+1] = 0xd0c0ffee; | ||
126 | } | ||
127 | |||
128 | static inline int cmp_reg(const struct coproc_reg *i1, | ||
129 | const struct coproc_reg *i2) | ||
130 | { | ||
131 | BUG_ON(i1 == i2); | ||
132 | if (!i1) | ||
133 | return 1; | ||
134 | else if (!i2) | ||
135 | return -1; | ||
136 | if (i1->CRn != i2->CRn) | ||
137 | return i1->CRn - i2->CRn; | ||
138 | if (i1->CRm != i2->CRm) | ||
139 | return i1->CRm - i2->CRm; | ||
140 | if (i1->Op1 != i2->Op1) | ||
141 | return i1->Op1 - i2->Op1; | ||
142 | return i1->Op2 - i2->Op2; | ||
143 | } | ||
144 | |||
145 | |||
146 | #define CRn(_x) .CRn = _x | ||
147 | #define CRm(_x) .CRm = _x | ||
148 | #define Op1(_x) .Op1 = _x | ||
149 | #define Op2(_x) .Op2 = _x | ||
150 | #define is64 .is_64 = true | ||
151 | #define is32 .is_64 = false | ||
152 | |||
153 | #endif /* __ARM_KVM_COPROC_LOCAL_H__ */ | ||
diff --git a/arch/arm/kvm/coproc_a15.c b/arch/arm/kvm/coproc_a15.c new file mode 100644 index 000000000000..685063a6d0cf --- /dev/null +++ b/arch/arm/kvm/coproc_a15.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | ||
3 | * Authors: Rusty Russell <rusty@rustcorp.au> | ||
4 | * Christoffer Dall <c.dall@virtualopensystems.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License, version 2, as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
18 | */ | ||
19 | #include <linux/kvm_host.h> | ||
20 | #include <asm/cputype.h> | ||
21 | #include <asm/kvm_arm.h> | ||
22 | #include <asm/kvm_host.h> | ||
23 | #include <asm/kvm_emulate.h> | ||
24 | #include <asm/kvm_coproc.h> | ||
25 | #include <linux/init.h> | ||
26 | |||
27 | static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||
28 | { | ||
29 | /* | ||
30 | * Compute guest MPIDR: | ||
31 | * (Even if we present only one VCPU to the guest on an SMP | ||
32 | * host we don't set the U bit in the MPIDR, or vice versa, as | ||
33 | * revealing the underlying hardware properties is likely to | ||
34 | * be the best choice). | ||
35 | */ | ||
36 | vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_LEVEL_MASK) | ||
37 | | (vcpu->vcpu_id & MPIDR_LEVEL_MASK); | ||
38 | } | ||
39 | |||
40 | #include "coproc.h" | ||
41 | |||
42 | /* A15 TRM 4.3.28: RO WI */ | ||
43 | static bool access_actlr(struct kvm_vcpu *vcpu, | ||
44 | const struct coproc_params *p, | ||
45 | const struct coproc_reg *r) | ||
46 | { | ||
47 | if (p->is_write) | ||
48 | return ignore_write(vcpu, p); | ||
49 | |||
50 | *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR]; | ||
51 | return true; | ||
52 | } | ||
53 | |||
54 | /* A15 TRM 4.3.60: R/O. */ | ||
55 | static bool access_cbar(struct kvm_vcpu *vcpu, | ||
56 | const struct coproc_params *p, | ||
57 | const struct coproc_reg *r) | ||
58 | { | ||
59 | if (p->is_write) | ||
60 | return write_to_read_only(vcpu, p); | ||
61 | return read_zero(vcpu, p); | ||
62 | } | ||
63 | |||
64 | /* A15 TRM 4.3.48: R/O WI. */ | ||
65 | static bool access_l2ctlr(struct kvm_vcpu *vcpu, | ||
66 | const struct coproc_params *p, | ||
67 | const struct coproc_reg *r) | ||
68 | { | ||
69 | if (p->is_write) | ||
70 | return ignore_write(vcpu, p); | ||
71 | |||
72 | *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR]; | ||
73 | return true; | ||
74 | } | ||
75 | |||
76 | static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||
77 | { | ||
78 | u32 l2ctlr, ncores; | ||
79 | |||
80 | asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); | ||
81 | l2ctlr &= ~(3 << 24); | ||
82 | ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1; | ||
83 | l2ctlr |= (ncores & 3) << 24; | ||
84 | |||
85 | vcpu->arch.cp15[c9_L2CTLR] = l2ctlr; | ||
86 | } | ||
87 | |||
88 | static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||
89 | { | ||
90 | u32 actlr; | ||
91 | |||
92 | /* ACTLR contains SMP bit: make sure you create all cpus first! */ | ||
93 | asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr)); | ||
94 | /* Make the SMP bit consistent with the guest configuration */ | ||
95 | if (atomic_read(&vcpu->kvm->online_vcpus) > 1) | ||
96 | actlr |= 1U << 6; | ||
97 | else | ||
98 | actlr &= ~(1U << 6); | ||
99 | |||
100 | vcpu->arch.cp15[c1_ACTLR] = actlr; | ||
101 | } | ||
102 | |||
103 | /* A15 TRM 4.3.49: R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored). */ | ||
104 | static bool access_l2ectlr(struct kvm_vcpu *vcpu, | ||
105 | const struct coproc_params *p, | ||
106 | const struct coproc_reg *r) | ||
107 | { | ||
108 | if (p->is_write) | ||
109 | return ignore_write(vcpu, p); | ||
110 | |||
111 | *vcpu_reg(vcpu, p->Rt1) = 0; | ||
112 | return true; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * A15-specific CP15 registers. | ||
117 | * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 | ||
118 | */ | ||
119 | static const struct coproc_reg a15_regs[] = { | ||
120 | /* MPIDR: we use VMPIDR for guest access. */ | ||
121 | { CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32, | ||
122 | NULL, reset_mpidr, c0_MPIDR }, | ||
123 | |||
124 | /* SCTLR: swapped by interrupt.S. */ | ||
125 | { CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32, | ||
126 | NULL, reset_val, c1_SCTLR, 0x00C50078 }, | ||
127 | /* ACTLR: trapped by HCR.TAC bit. */ | ||
128 | { CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32, | ||
129 | access_actlr, reset_actlr, c1_ACTLR }, | ||
130 | /* CPACR: swapped by interrupt.S. */ | ||
131 | { CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32, | ||
132 | NULL, reset_val, c1_CPACR, 0x00000000 }, | ||
133 | |||
134 | /* | ||
135 | * L2CTLR access (guest wants to know #CPUs). | ||
136 | */ | ||
137 | { CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32, | ||
138 | access_l2ctlr, reset_l2ctlr, c9_L2CTLR }, | ||
139 | { CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr}, | ||
140 | |||
141 | /* The Configuration Base Address Register. */ | ||
142 | { CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar}, | ||
143 | }; | ||
144 | |||
145 | static struct kvm_coproc_target_table a15_target_table = { | ||
146 | .target = KVM_ARM_TARGET_CORTEX_A15, | ||
147 | .table = a15_regs, | ||
148 | .num = ARRAY_SIZE(a15_regs), | ||
149 | }; | ||
150 | |||
151 | static int __init coproc_a15_init(void) | ||
152 | { | ||
153 | unsigned int i; | ||
154 | |||
155 | for (i = 1; i < ARRAY_SIZE(a15_regs); i++) | ||
156 | BUG_ON(cmp_reg(&a15_regs[i-1], | ||
157 | &a15_regs[i]) >= 0); | ||
158 | |||
159 | kvm_register_target_coproc_table(&a15_target_table); | ||
160 | return 0; | ||
161 | } | ||
162 | late_initcall(coproc_a15_init); | ||
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c index 3eadc25e95de..d61450ac6665 100644 --- a/arch/arm/kvm/emulate.c +++ b/arch/arm/kvm/emulate.c | |||
@@ -16,7 +16,13 @@ | |||
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/kvm_host.h> | ||
21 | #include <asm/kvm_arm.h> | ||
19 | #include <asm/kvm_emulate.h> | 22 | #include <asm/kvm_emulate.h> |
23 | #include <trace/events/kvm.h> | ||
24 | |||
25 | #include "trace.h" | ||
20 | 26 | ||
21 | #define VCPU_NR_MODES 6 | 27 | #define VCPU_NR_MODES 6 |
22 | #define VCPU_REG_OFFSET_USR 0 | 28 | #define VCPU_REG_OFFSET_USR 0 |
@@ -153,3 +159,215 @@ u32 *vcpu_spsr(struct kvm_vcpu *vcpu) | |||
153 | BUG(); | 159 | BUG(); |
154 | } | 160 | } |
155 | } | 161 | } |
162 | |||
163 | /** | ||
164 | * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest | ||
165 | * @vcpu: the vcpu pointer | ||
166 | * @run: the kvm_run structure pointer | ||
167 | * | ||
168 | * Simply sets the wait_for_interrupts flag on the vcpu structure, which will | ||
169 | * halt execution of world-switches and schedule other host processes until | ||
170 | * there is an incoming IRQ or FIQ to the VM. | ||
171 | */ | ||
172 | int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
173 | { | ||
174 | trace_kvm_wfi(*vcpu_pc(vcpu)); | ||
175 | kvm_vcpu_block(vcpu); | ||
176 | return 1; | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block | ||
181 | * @vcpu: The VCPU pointer | ||
182 | * | ||
183 | * When exceptions occur while instructions are executed in Thumb IF-THEN | ||
184 | * blocks, the ITSTATE field of the CPSR is not advanved (updated), so we have | ||
185 | * to do this little bit of work manually. The fields map like this: | ||
186 | * | ||
187 | * IT[7:0] -> CPSR[26:25],CPSR[15:10] | ||
188 | */ | ||
189 | static void kvm_adjust_itstate(struct kvm_vcpu *vcpu) | ||
190 | { | ||
191 | unsigned long itbits, cond; | ||
192 | unsigned long cpsr = *vcpu_cpsr(vcpu); | ||
193 | bool is_arm = !(cpsr & PSR_T_BIT); | ||
194 | |||
195 | BUG_ON(is_arm && (cpsr & PSR_IT_MASK)); | ||
196 | |||
197 | if (!(cpsr & PSR_IT_MASK)) | ||
198 | return; | ||
199 | |||
200 | cond = (cpsr & 0xe000) >> 13; | ||
201 | itbits = (cpsr & 0x1c00) >> (10 - 2); | ||
202 | itbits |= (cpsr & (0x3 << 25)) >> 25; | ||
203 | |||
204 | /* Perform ITAdvance (see page A-52 in ARM DDI 0406C) */ | ||
205 | if ((itbits & 0x7) == 0) | ||
206 | itbits = cond = 0; | ||
207 | else | ||
208 | itbits = (itbits << 1) & 0x1f; | ||
209 | |||
210 | cpsr &= ~PSR_IT_MASK; | ||
211 | cpsr |= cond << 13; | ||
212 | cpsr |= (itbits & 0x1c) << (10 - 2); | ||
213 | cpsr |= (itbits & 0x3) << 25; | ||
214 | *vcpu_cpsr(vcpu) = cpsr; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * kvm_skip_instr - skip a trapped instruction and proceed to the next | ||
219 | * @vcpu: The vcpu pointer | ||
220 | */ | ||
221 | void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) | ||
222 | { | ||
223 | bool is_thumb; | ||
224 | |||
225 | is_thumb = !!(*vcpu_cpsr(vcpu) & PSR_T_BIT); | ||
226 | if (is_thumb && !is_wide_instr) | ||
227 | *vcpu_pc(vcpu) += 2; | ||
228 | else | ||
229 | *vcpu_pc(vcpu) += 4; | ||
230 | kvm_adjust_itstate(vcpu); | ||
231 | } | ||
232 | |||
233 | |||
234 | /****************************************************************************** | ||
235 | * Inject exceptions into the guest | ||
236 | */ | ||
237 | |||
238 | static u32 exc_vector_base(struct kvm_vcpu *vcpu) | ||
239 | { | ||
240 | u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; | ||
241 | u32 vbar = vcpu->arch.cp15[c12_VBAR]; | ||
242 | |||
243 | if (sctlr & SCTLR_V) | ||
244 | return 0xffff0000; | ||
245 | else /* always have security exceptions */ | ||
246 | return vbar; | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * kvm_inject_undefined - inject an undefined exception into the guest | ||
251 | * @vcpu: The VCPU to receive the undefined exception | ||
252 | * | ||
253 | * It is assumed that this code is called from the VCPU thread and that the | ||
254 | * VCPU therefore is not currently executing guest code. | ||
255 | * | ||
256 | * Modelled after TakeUndefInstrException() pseudocode. | ||
257 | */ | ||
258 | void kvm_inject_undefined(struct kvm_vcpu *vcpu) | ||
259 | { | ||
260 | u32 new_lr_value; | ||
261 | u32 new_spsr_value; | ||
262 | u32 cpsr = *vcpu_cpsr(vcpu); | ||
263 | u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; | ||
264 | bool is_thumb = (cpsr & PSR_T_BIT); | ||
265 | u32 vect_offset = 4; | ||
266 | u32 return_offset = (is_thumb) ? 2 : 4; | ||
267 | |||
268 | new_spsr_value = cpsr; | ||
269 | new_lr_value = *vcpu_pc(vcpu) - return_offset; | ||
270 | |||
271 | *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | UND_MODE; | ||
272 | *vcpu_cpsr(vcpu) |= PSR_I_BIT; | ||
273 | *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT); | ||
274 | |||
275 | if (sctlr & SCTLR_TE) | ||
276 | *vcpu_cpsr(vcpu) |= PSR_T_BIT; | ||
277 | if (sctlr & SCTLR_EE) | ||
278 | *vcpu_cpsr(vcpu) |= PSR_E_BIT; | ||
279 | |||
280 | /* Note: These now point to UND banked copies */ | ||
281 | *vcpu_spsr(vcpu) = cpsr; | ||
282 | *vcpu_reg(vcpu, 14) = new_lr_value; | ||
283 | |||
284 | /* Branch to exception vector */ | ||
285 | *vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset; | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * Modelled after TakeDataAbortException() and TakePrefetchAbortException | ||
290 | * pseudocode. | ||
291 | */ | ||
292 | static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr) | ||
293 | { | ||
294 | u32 new_lr_value; | ||
295 | u32 new_spsr_value; | ||
296 | u32 cpsr = *vcpu_cpsr(vcpu); | ||
297 | u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; | ||
298 | bool is_thumb = (cpsr & PSR_T_BIT); | ||
299 | u32 vect_offset; | ||
300 | u32 return_offset = (is_thumb) ? 4 : 0; | ||
301 | bool is_lpae; | ||
302 | |||
303 | new_spsr_value = cpsr; | ||
304 | new_lr_value = *vcpu_pc(vcpu) + return_offset; | ||
305 | |||
306 | *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | ABT_MODE; | ||
307 | *vcpu_cpsr(vcpu) |= PSR_I_BIT | PSR_A_BIT; | ||
308 | *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT); | ||
309 | |||
310 | if (sctlr & SCTLR_TE) | ||
311 | *vcpu_cpsr(vcpu) |= PSR_T_BIT; | ||
312 | if (sctlr & SCTLR_EE) | ||
313 | *vcpu_cpsr(vcpu) |= PSR_E_BIT; | ||
314 | |||
315 | /* Note: These now point to ABT banked copies */ | ||
316 | *vcpu_spsr(vcpu) = cpsr; | ||
317 | *vcpu_reg(vcpu, 14) = new_lr_value; | ||
318 | |||
319 | if (is_pabt) | ||
320 | vect_offset = 12; | ||
321 | else | ||
322 | vect_offset = 16; | ||
323 | |||
324 | /* Branch to exception vector */ | ||
325 | *vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset; | ||
326 | |||
327 | if (is_pabt) { | ||
328 | /* Set DFAR and DFSR */ | ||
329 | vcpu->arch.cp15[c6_IFAR] = addr; | ||
330 | is_lpae = (vcpu->arch.cp15[c2_TTBCR] >> 31); | ||
331 | /* Always give debug fault for now - should give guest a clue */ | ||
332 | if (is_lpae) | ||
333 | vcpu->arch.cp15[c5_IFSR] = 1 << 9 | 0x22; | ||
334 | else | ||
335 | vcpu->arch.cp15[c5_IFSR] = 2; | ||
336 | } else { /* !iabt */ | ||
337 | /* Set DFAR and DFSR */ | ||
338 | vcpu->arch.cp15[c6_DFAR] = addr; | ||
339 | is_lpae = (vcpu->arch.cp15[c2_TTBCR] >> 31); | ||
340 | /* Always give debug fault for now - should give guest a clue */ | ||
341 | if (is_lpae) | ||
342 | vcpu->arch.cp15[c5_DFSR] = 1 << 9 | 0x22; | ||
343 | else | ||
344 | vcpu->arch.cp15[c5_DFSR] = 2; | ||
345 | } | ||
346 | |||
347 | } | ||
348 | |||
349 | /** | ||
350 | * kvm_inject_dabt - inject a data abort into the guest | ||
351 | * @vcpu: The VCPU to receive the undefined exception | ||
352 | * @addr: The address to report in the DFAR | ||
353 | * | ||
354 | * It is assumed that this code is called from the VCPU thread and that the | ||
355 | * VCPU therefore is not currently executing guest code. | ||
356 | */ | ||
357 | void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) | ||
358 | { | ||
359 | inject_abt(vcpu, false, addr); | ||
360 | } | ||
361 | |||
362 | /** | ||
363 | * kvm_inject_pabt - inject a prefetch abort into the guest | ||
364 | * @vcpu: The VCPU to receive the undefined exception | ||
365 | * @addr: The address to report in the DFAR | ||
366 | * | ||
367 | * It is assumed that this code is called from the VCPU thread and that the | ||
368 | * VCPU therefore is not currently executing guest code. | ||
369 | */ | ||
370 | void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) | ||
371 | { | ||
372 | inject_abt(vcpu, true, addr); | ||
373 | } | ||
diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h index 105d1f79909a..022305b38c27 100644 --- a/arch/arm/kvm/trace.h +++ b/arch/arm/kvm/trace.h | |||
@@ -64,6 +64,51 @@ TRACE_EVENT(kvm_irq_line, | |||
64 | __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level) | 64 | __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level) |
65 | ); | 65 | ); |
66 | 66 | ||
67 | /* Architecturally implementation defined CP15 register access */ | ||
68 | TRACE_EVENT(kvm_emulate_cp15_imp, | ||
69 | TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn, | ||
70 | unsigned long CRm, unsigned long Op2, bool is_write), | ||
71 | TP_ARGS(Op1, Rt1, CRn, CRm, Op2, is_write), | ||
72 | |||
73 | TP_STRUCT__entry( | ||
74 | __field( unsigned int, Op1 ) | ||
75 | __field( unsigned int, Rt1 ) | ||
76 | __field( unsigned int, CRn ) | ||
77 | __field( unsigned int, CRm ) | ||
78 | __field( unsigned int, Op2 ) | ||
79 | __field( bool, is_write ) | ||
80 | ), | ||
81 | |||
82 | TP_fast_assign( | ||
83 | __entry->is_write = is_write; | ||
84 | __entry->Op1 = Op1; | ||
85 | __entry->Rt1 = Rt1; | ||
86 | __entry->CRn = CRn; | ||
87 | __entry->CRm = CRm; | ||
88 | __entry->Op2 = Op2; | ||
89 | ), | ||
90 | |||
91 | TP_printk("Implementation defined CP15: %s\tp15, %u, r%u, c%u, c%u, %u", | ||
92 | (__entry->is_write) ? "mcr" : "mrc", | ||
93 | __entry->Op1, __entry->Rt1, __entry->CRn, | ||
94 | __entry->CRm, __entry->Op2) | ||
95 | ); | ||
96 | |||
97 | TRACE_EVENT(kvm_wfi, | ||
98 | TP_PROTO(unsigned long vcpu_pc), | ||
99 | TP_ARGS(vcpu_pc), | ||
100 | |||
101 | TP_STRUCT__entry( | ||
102 | __field( unsigned long, vcpu_pc ) | ||
103 | ), | ||
104 | |||
105 | TP_fast_assign( | ||
106 | __entry->vcpu_pc = vcpu_pc; | ||
107 | ), | ||
108 | |||
109 | TP_printk("guest executed wfi at: 0x%08lx", __entry->vcpu_pc) | ||
110 | ); | ||
111 | |||
67 | TRACE_EVENT(kvm_unmap_hva, | 112 | TRACE_EVENT(kvm_unmap_hva, |
68 | TP_PROTO(unsigned long hva), | 113 | TP_PROTO(unsigned long hva), |
69 | TP_ARGS(hva), | 114 | TP_ARGS(hva), |
@@ -112,6 +157,26 @@ TRACE_EVENT(kvm_set_spte_hva, | |||
112 | TP_printk("mmu notifier set pte hva: %#08lx", __entry->hva) | 157 | TP_printk("mmu notifier set pte hva: %#08lx", __entry->hva) |
113 | ); | 158 | ); |
114 | 159 | ||
160 | TRACE_EVENT(kvm_hvc, | ||
161 | TP_PROTO(unsigned long vcpu_pc, unsigned long r0, unsigned long imm), | ||
162 | TP_ARGS(vcpu_pc, r0, imm), | ||
163 | |||
164 | TP_STRUCT__entry( | ||
165 | __field( unsigned long, vcpu_pc ) | ||
166 | __field( unsigned long, r0 ) | ||
167 | __field( unsigned long, imm ) | ||
168 | ), | ||
169 | |||
170 | TP_fast_assign( | ||
171 | __entry->vcpu_pc = vcpu_pc; | ||
172 | __entry->r0 = r0; | ||
173 | __entry->imm = imm; | ||
174 | ), | ||
175 | |||
176 | TP_printk("HVC at 0x%08lx (r0: 0x%08lx, imm: 0x%lx", | ||
177 | __entry->vcpu_pc, __entry->r0, __entry->imm) | ||
178 | ); | ||
179 | |||
115 | #endif /* _TRACE_KVM_H */ | 180 | #endif /* _TRACE_KVM_H */ |
116 | 181 | ||
117 | #undef TRACE_INCLUDE_PATH | 182 | #undef TRACE_INCLUDE_PATH |