diff options
| -rw-r--r-- | arch/arm/include/asm/kvm_arm.h | 3 | ||||
| -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/include/asm/kvm_mmio.h | 56 | ||||
| -rw-r--r-- | arch/arm/kvm/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/kvm/arm.c | 6 | ||||
| -rw-r--r-- | arch/arm/kvm/mmio.c | 153 | ||||
| -rw-r--r-- | arch/arm/kvm/mmu.c | 7 | ||||
| -rw-r--r-- | arch/arm/kvm/trace.h | 21 |
9 files changed, 255 insertions, 3 deletions
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 9a34c20d41ec..7c3d813e15df 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h | |||
| @@ -173,8 +173,11 @@ | |||
| 173 | #define HSR_ISS (HSR_IL - 1) | 173 | #define HSR_ISS (HSR_IL - 1) |
| 174 | #define HSR_ISV_SHIFT (24) | 174 | #define HSR_ISV_SHIFT (24) |
| 175 | #define HSR_ISV (1U << HSR_ISV_SHIFT) | 175 | #define HSR_ISV (1U << HSR_ISV_SHIFT) |
| 176 | #define HSR_SRT_SHIFT (16) | ||
| 177 | #define HSR_SRT_MASK (0xf << HSR_SRT_SHIFT) | ||
| 176 | #define HSR_FSC (0x3f) | 178 | #define HSR_FSC (0x3f) |
| 177 | #define HSR_FSC_TYPE (0x3c) | 179 | #define HSR_FSC_TYPE (0x3c) |
| 180 | #define HSR_SSE (1 << 21) | ||
| 178 | #define HSR_WNR (1 << 6) | 181 | #define HSR_WNR (1 << 6) |
| 179 | #define HSR_CV_SHIFT (24) | 182 | #define HSR_CV_SHIFT (24) |
| 180 | #define HSR_CV (1U << HSR_CV_SHIFT) | 183 | #define HSR_CV (1U << HSR_CV_SHIFT) |
diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h index 01a755b80632..4c1a073280be 100644 --- a/arch/arm/include/asm/kvm_emulate.h +++ b/arch/arm/include/asm/kvm_emulate.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | #include <linux/kvm_host.h> | 22 | #include <linux/kvm_host.h> |
| 23 | #include <asm/kvm_asm.h> | 23 | #include <asm/kvm_asm.h> |
| 24 | #include <asm/kvm_mmio.h> | ||
| 24 | 25 | ||
| 25 | u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); | 26 | u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); |
| 26 | u32 *vcpu_spsr(struct kvm_vcpu *vcpu); | 27 | u32 *vcpu_spsr(struct kvm_vcpu *vcpu); |
| @@ -53,4 +54,9 @@ static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu) | |||
| 53 | return cpsr_mode > USR_MODE;; | 54 | return cpsr_mode > USR_MODE;; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 57 | static inline bool kvm_vcpu_reg_is_pc(struct kvm_vcpu *vcpu, int reg) | ||
| 58 | { | ||
| 59 | return reg == 15; | ||
| 60 | } | ||
| 61 | |||
| 56 | #endif /* __ARM_KVM_EMULATE_H__ */ | 62 | #endif /* __ARM_KVM_EMULATE_H__ */ |
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index ed79043d7921..e65fc967a71d 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | #include <asm/kvm.h> | 22 | #include <asm/kvm.h> |
| 23 | #include <asm/kvm_asm.h> | 23 | #include <asm/kvm_asm.h> |
| 24 | #include <asm/kvm_mmio.h> | ||
| 24 | #include <asm/fpstate.h> | 25 | #include <asm/fpstate.h> |
| 25 | 26 | ||
| 26 | #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS | 27 | #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS |
| @@ -99,6 +100,9 @@ struct kvm_vcpu_arch { | |||
| 99 | int last_pcpu; | 100 | int last_pcpu; |
| 100 | cpumask_t require_dcache_flush; | 101 | cpumask_t require_dcache_flush; |
| 101 | 102 | ||
| 103 | /* IO related fields */ | ||
| 104 | struct kvm_decode mmio_decode; | ||
| 105 | |||
| 102 | /* Interrupt related fields */ | 106 | /* Interrupt related fields */ |
| 103 | u32 irq_lines; /* IRQ and FIQ levels */ | 107 | u32 irq_lines; /* IRQ and FIQ levels */ |
| 104 | 108 | ||
diff --git a/arch/arm/include/asm/kvm_mmio.h b/arch/arm/include/asm/kvm_mmio.h new file mode 100644 index 000000000000..adcc0d7d3175 --- /dev/null +++ b/arch/arm/include/asm/kvm_mmio.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | ||
| 3 | * Author: 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_MMIO_H__ | ||
| 20 | #define __ARM_KVM_MMIO_H__ | ||
| 21 | |||
| 22 | #include <linux/kvm_host.h> | ||
| 23 | #include <asm/kvm_asm.h> | ||
| 24 | #include <asm/kvm_arm.h> | ||
| 25 | |||
| 26 | struct kvm_decode { | ||
| 27 | unsigned long rt; | ||
| 28 | bool sign_extend; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /* | ||
| 32 | * The in-kernel MMIO emulation code wants to use a copy of run->mmio, | ||
| 33 | * which is an anonymous type. Use our own type instead. | ||
| 34 | */ | ||
| 35 | struct kvm_exit_mmio { | ||
| 36 | phys_addr_t phys_addr; | ||
| 37 | u8 data[8]; | ||
| 38 | u32 len; | ||
| 39 | bool is_write; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static inline void kvm_prepare_mmio(struct kvm_run *run, | ||
| 43 | struct kvm_exit_mmio *mmio) | ||
| 44 | { | ||
| 45 | run->mmio.phys_addr = mmio->phys_addr; | ||
| 46 | run->mmio.len = mmio->len; | ||
| 47 | run->mmio.is_write = mmio->is_write; | ||
| 48 | memcpy(run->mmio.data, mmio->data, mmio->len); | ||
| 49 | run->exit_reason = KVM_EXIT_MMIO; | ||
| 50 | } | ||
| 51 | |||
| 52 | int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run); | ||
| 53 | int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, | ||
| 54 | phys_addr_t fault_ipa); | ||
| 55 | |||
| 56 | #endif /* __ARM_KVM_MMIO_H__ */ | ||
diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index 88edce6c97d4..1e45cd97a7fc 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 coproc_a15.o | 21 | obj-y += coproc.o coproc_a15.o mmio.o |
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index be06c5de51e3..8680b9ffd2ae 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c | |||
| @@ -616,6 +616,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
| 616 | if (ret) | 616 | if (ret) |
| 617 | return ret; | 617 | return ret; |
| 618 | 618 | ||
| 619 | if (run->exit_reason == KVM_EXIT_MMIO) { | ||
| 620 | ret = kvm_handle_mmio_return(vcpu, vcpu->run); | ||
| 621 | if (ret) | ||
| 622 | return ret; | ||
| 623 | } | ||
| 624 | |||
| 619 | if (vcpu->sigset_active) | 625 | if (vcpu->sigset_active) |
| 620 | sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); | 626 | sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); |
| 621 | 627 | ||
diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c new file mode 100644 index 000000000000..0144baf82904 --- /dev/null +++ b/arch/arm/kvm/mmio.c | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | ||
| 3 | * Author: 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 | #include <linux/kvm_host.h> | ||
| 20 | #include <asm/kvm_mmio.h> | ||
| 21 | #include <asm/kvm_emulate.h> | ||
| 22 | #include <trace/events/kvm.h> | ||
| 23 | |||
| 24 | #include "trace.h" | ||
| 25 | |||
| 26 | /** | ||
| 27 | * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation | ||
| 28 | * @vcpu: The VCPU pointer | ||
| 29 | * @run: The VCPU run struct containing the mmio data | ||
| 30 | * | ||
| 31 | * This should only be called after returning from userspace for MMIO load | ||
| 32 | * emulation. | ||
| 33 | */ | ||
| 34 | int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
| 35 | { | ||
| 36 | __u32 *dest; | ||
| 37 | unsigned int len; | ||
| 38 | int mask; | ||
| 39 | |||
| 40 | if (!run->mmio.is_write) { | ||
| 41 | dest = vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt); | ||
| 42 | memset(dest, 0, sizeof(int)); | ||
| 43 | |||
| 44 | len = run->mmio.len; | ||
| 45 | if (len > 4) | ||
| 46 | return -EINVAL; | ||
| 47 | |||
| 48 | memcpy(dest, run->mmio.data, len); | ||
| 49 | |||
| 50 | trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, | ||
| 51 | *((u64 *)run->mmio.data)); | ||
| 52 | |||
| 53 | if (vcpu->arch.mmio_decode.sign_extend && len < 4) { | ||
| 54 | mask = 1U << ((len * 8) - 1); | ||
| 55 | *dest = (*dest ^ mask) - mask; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, | ||
| 63 | struct kvm_exit_mmio *mmio) | ||
| 64 | { | ||
| 65 | unsigned long rt, len; | ||
| 66 | bool is_write, sign_extend; | ||
| 67 | |||
| 68 | if ((vcpu->arch.hsr >> 8) & 1) { | ||
| 69 | /* cache operation on I/O addr, tell guest unsupported */ | ||
| 70 | kvm_inject_dabt(vcpu, vcpu->arch.hxfar); | ||
| 71 | return 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | if ((vcpu->arch.hsr >> 7) & 1) { | ||
| 75 | /* page table accesses IO mem: tell guest to fix its TTBR */ | ||
| 76 | kvm_inject_dabt(vcpu, vcpu->arch.hxfar); | ||
| 77 | return 1; | ||
| 78 | } | ||
| 79 | |||
| 80 | switch ((vcpu->arch.hsr >> 22) & 0x3) { | ||
| 81 | case 0: | ||
| 82 | len = 1; | ||
| 83 | break; | ||
| 84 | case 1: | ||
| 85 | len = 2; | ||
| 86 | break; | ||
| 87 | case 2: | ||
| 88 | len = 4; | ||
| 89 | break; | ||
| 90 | default: | ||
| 91 | kvm_err("Hardware is weird: SAS 0b11 is reserved\n"); | ||
| 92 | return -EFAULT; | ||
| 93 | } | ||
| 94 | |||
| 95 | is_write = vcpu->arch.hsr & HSR_WNR; | ||
| 96 | sign_extend = vcpu->arch.hsr & HSR_SSE; | ||
| 97 | rt = (vcpu->arch.hsr & HSR_SRT_MASK) >> HSR_SRT_SHIFT; | ||
| 98 | |||
| 99 | if (kvm_vcpu_reg_is_pc(vcpu, rt)) { | ||
| 100 | /* IO memory trying to read/write pc */ | ||
| 101 | kvm_inject_pabt(vcpu, vcpu->arch.hxfar); | ||
| 102 | return 1; | ||
| 103 | } | ||
| 104 | |||
| 105 | mmio->is_write = is_write; | ||
| 106 | mmio->phys_addr = fault_ipa; | ||
| 107 | mmio->len = len; | ||
| 108 | vcpu->arch.mmio_decode.sign_extend = sign_extend; | ||
| 109 | vcpu->arch.mmio_decode.rt = rt; | ||
| 110 | |||
| 111 | /* | ||
| 112 | * The MMIO instruction is emulated and should not be re-executed | ||
| 113 | * in the guest. | ||
| 114 | */ | ||
| 115 | kvm_skip_instr(vcpu, (vcpu->arch.hsr >> 25) & 1); | ||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, | ||
| 120 | phys_addr_t fault_ipa) | ||
| 121 | { | ||
| 122 | struct kvm_exit_mmio mmio; | ||
| 123 | unsigned long rt; | ||
| 124 | int ret; | ||
| 125 | |||
| 126 | /* | ||
| 127 | * Prepare MMIO operation. First stash it in a private | ||
| 128 | * structure that we can use for in-kernel emulation. If the | ||
| 129 | * kernel can't handle it, copy it into run->mmio and let user | ||
| 130 | * space do its magic. | ||
| 131 | */ | ||
| 132 | |||
| 133 | if (vcpu->arch.hsr & HSR_ISV) { | ||
| 134 | ret = decode_hsr(vcpu, fault_ipa, &mmio); | ||
| 135 | if (ret) | ||
| 136 | return ret; | ||
| 137 | } else { | ||
| 138 | kvm_err("load/store instruction decoding not implemented\n"); | ||
| 139 | return -ENOSYS; | ||
| 140 | } | ||
| 141 | |||
| 142 | rt = vcpu->arch.mmio_decode.rt; | ||
| 143 | trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE : | ||
| 144 | KVM_TRACE_MMIO_READ_UNSATISFIED, | ||
| 145 | mmio.len, fault_ipa, | ||
| 146 | (mmio.is_write) ? *vcpu_reg(vcpu, rt) : 0); | ||
| 147 | |||
| 148 | if (mmio.is_write) | ||
| 149 | memcpy(mmio.data, vcpu_reg(vcpu, rt), mmio.len); | ||
| 150 | |||
| 151 | kvm_prepare_mmio(run, &mmio); | ||
| 152 | return 0; | ||
| 153 | } | ||
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index a4b7b0f900e5..f30e13163a96 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
| @@ -19,11 +19,13 @@ | |||
| 19 | #include <linux/mman.h> | 19 | #include <linux/mman.h> |
| 20 | #include <linux/kvm_host.h> | 20 | #include <linux/kvm_host.h> |
| 21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
| 22 | #include <trace/events/kvm.h> | ||
| 22 | #include <asm/idmap.h> | 23 | #include <asm/idmap.h> |
| 23 | #include <asm/pgalloc.h> | 24 | #include <asm/pgalloc.h> |
| 24 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
| 25 | #include <asm/kvm_arm.h> | 26 | #include <asm/kvm_arm.h> |
| 26 | #include <asm/kvm_mmu.h> | 27 | #include <asm/kvm_mmu.h> |
| 28 | #include <asm/kvm_mmio.h> | ||
| 27 | #include <asm/kvm_asm.h> | 29 | #include <asm/kvm_asm.h> |
| 28 | #include <asm/kvm_emulate.h> | 30 | #include <asm/kvm_emulate.h> |
| 29 | #include <asm/mach/map.h> | 31 | #include <asm/mach/map.h> |
| @@ -624,8 +626,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
| 624 | goto out_unlock; | 626 | goto out_unlock; |
| 625 | } | 627 | } |
| 626 | 628 | ||
| 627 | kvm_pr_unimpl("I/O address abort..."); | 629 | /* Adjust page offset */ |
| 628 | ret = 0; | 630 | fault_ipa |= vcpu->arch.hxfar & ~PAGE_MASK; |
| 631 | ret = io_mem_abort(vcpu, run, fault_ipa); | ||
| 629 | goto out_unlock; | 632 | goto out_unlock; |
| 630 | } | 633 | } |
| 631 | 634 | ||
diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h index 624b5a4e8fad..a8e73ed5ad5b 100644 --- a/arch/arm/kvm/trace.h +++ b/arch/arm/kvm/trace.h | |||
| @@ -90,6 +90,27 @@ TRACE_EVENT(kvm_irq_line, | |||
| 90 | __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level) | 90 | __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level) |
| 91 | ); | 91 | ); |
| 92 | 92 | ||
| 93 | TRACE_EVENT(kvm_mmio_emulate, | ||
| 94 | TP_PROTO(unsigned long vcpu_pc, unsigned long instr, | ||
| 95 | unsigned long cpsr), | ||
| 96 | TP_ARGS(vcpu_pc, instr, cpsr), | ||
| 97 | |||
| 98 | TP_STRUCT__entry( | ||
| 99 | __field( unsigned long, vcpu_pc ) | ||
| 100 | __field( unsigned long, instr ) | ||
| 101 | __field( unsigned long, cpsr ) | ||
| 102 | ), | ||
| 103 | |||
| 104 | TP_fast_assign( | ||
| 105 | __entry->vcpu_pc = vcpu_pc; | ||
| 106 | __entry->instr = instr; | ||
| 107 | __entry->cpsr = cpsr; | ||
| 108 | ), | ||
| 109 | |||
| 110 | TP_printk("Emulate MMIO at: 0x%08lx (instr: %08lx, cpsr: %08lx)", | ||
| 111 | __entry->vcpu_pc, __entry->instr, __entry->cpsr) | ||
| 112 | ); | ||
| 113 | |||
| 93 | /* Architecturally implementation defined CP15 register access */ | 114 | /* Architecturally implementation defined CP15 register access */ |
| 94 | TRACE_EVENT(kvm_emulate_cp15_imp, | 115 | TRACE_EVENT(kvm_emulate_cp15_imp, |
| 95 | TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn, | 116 | TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn, |
