From patchwork Fri Jun 3 15:04:22 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoffer Dall X-Patchwork-Id: 847472 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p53F4aoZ029941 for ; Fri, 3 Jun 2011 15:04:36 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755422Ab1FCPEe (ORCPT ); Fri, 3 Jun 2011 11:04:34 -0400 Received: from mail-wy0-f174.google.com ([74.125.82.174]:37774 "EHLO mail-wy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755387Ab1FCPEd (ORCPT ); Fri, 3 Jun 2011 11:04:33 -0400 Received: by mail-wy0-f174.google.com with SMTP id 21so1457214wya.19 for ; Fri, 03 Jun 2011 08:04:32 -0700 (PDT) Received: by 10.216.231.198 with SMTP id l48mr2169099weq.54.1307113472208; Fri, 03 Jun 2011 08:04:32 -0700 (PDT) Received: from [127.0.0.1] (host198-39-dynamic.245-95-r.retail.telecomitalia.it [95.245.39.198]) by mx.google.com with ESMTPS id u64sm521407weq.28.2011.06.03.08.04.30 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 03 Jun 2011 08:04:31 -0700 (PDT) Subject: [PATCH v3 8/8] ARM: KVM: Handle I/O aborts To: catalin.marinas@arm.com, android-virt@lists.cs.columbia.edu From: Christoffer Dall Cc: s.raho@virtualopensystems.com, a.motakis@virtualopensystems.com, c.dall@virtualopensystems.com, kvm@vger.kernel.org, a.costa@virtualopensystems.com Date: Fri, 03 Jun 2011 17:04:22 +0200 Message-ID: <20110603150422.17011.44777.stgit@ubuntu> In-Reply-To: <20110603150318.17011.82777.stgit@ubuntu> References: <20110603150318.17011.82777.stgit@ubuntu> User-Agent: StGit/0.15 MIME-Version: 1.0 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Fri, 03 Jun 2011 15:04:37 +0000 (UTC) When the guest accesses I/O memory this will create data abort exceptions and they are handled by decoding the HSR information (physical address, read/write, length, register) and forwarding reads and writes to QEMU which performs the device emulation. This requires changing the general flow somewhat since new calls to run the VCPU must check if there's a pending MMIO load and perform the write after QEMU has made the data available. --- arch/arm/include/asm/kvm_host.h | 1 arch/arm/include/asm/kvm_mmu.h | 1 arch/arm/kvm/arm.c | 11 ++++ arch/arm/kvm/arm_mmu.c | 106 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 115 insertions(+), 4 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 7f96974..5393e25 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -86,6 +86,7 @@ struct kvm_vcpu_arch { u32 hpfar; /* Hyp IPA Fault Address Register */ /* IO related fields */ + bool mmio_sign_extend; /* for byte/halfword loads */ u32 mmio_rd; /* Misc. fields */ diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h index a64ab2d..f06f42d 100644 --- a/arch/arm/include/asm/kvm_mmu.h +++ b/arch/arm/include/asm/kvm_mmu.h @@ -40,6 +40,7 @@ void free_hyp_pmds(pgd_t *hyp_pgd); int kvm_alloc_stage2_pgd(struct kvm *kvm); void kvm_free_stage2_pgd(struct kvm *kvm); +int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run); int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run); #endif /* __ARM_KVM_MMU_H__ */ diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index abed683..d01f234 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -349,6 +349,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) int ret; for (;;) { + if (run->exit_reason == KVM_EXIT_MMIO) { + ret = kvm_handle_mmio_return(vcpu, vcpu->run); + if (ret) + break; + } + local_irq_save(flags); ret = __kvm_vcpu_run(vcpu); local_irq_restore(flags); @@ -367,8 +373,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) kvm_err(ret, "Error in handle_exit"); break; } + + if (run->exit_reason == KVM_EXIT_MMIO) + break; } + if (ret < 0) + run->exit_reason = KVM_EXIT_EXCEPTION; return ret; } diff --git a/arch/arm/kvm/arm_mmu.c b/arch/arm/kvm/arm_mmu.c index fe27e59..b04a211 100644 --- a/arch/arm/kvm/arm_mmu.c +++ b/arch/arm/kvm/arm_mmu.c @@ -16,9 +16,10 @@ #include #include +#include #include #include -#include +#include #include "../mm/mm.h" #include "trace.h" @@ -297,6 +298,105 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, return 0; } +/** + * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation + * @vcpu: The VCPU pointer + * @run: The VCPU run struct containing the mmio data + * + * This should only be called after returning to QEMU for MMIO load emulation. + */ +int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + int *dest; + unsigned int len; + int mask; + + if (!run->mmio.is_write) { + dest = &vcpu_reg(vcpu, vcpu->arch.mmio_rd); + memset(dest, 0, sizeof(int)); + + if (run->mmio.len > 4) { + kvm_err(-EINVAL, "Incorrect mmio length"); + return -EINVAL; + } + + len = run->mmio.len; + memcpy(dest, run->mmio.data, len); + + if (vcpu->arch.mmio_sign_extend && len < 4) { + mask = 1U << ((len * 8) - 1); + *dest = (*dest ^ mask) - mask; + } + } + + return 0; +} + +static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, + phys_addr_t fault_ipa, struct kvm_memory_slot *memslot) +{ + unsigned long rd, len, instr_len; + bool is_write, sign_extend; + + if (!((vcpu->arch.hsr >> 24) & 1) || ((vcpu->arch.hsr >> 8) & 1)) { + kvm_err(-EFAULT, "Invalid I/O abort"); + return -EFAULT; + } + + if ((vcpu->arch.hsr >> 7) & 1) { + kvm_err(-EFAULT, "Translation table accesses I/O memory"); + return -EFAULT; + } + + switch ((vcpu->arch.hsr >> 22) & 0x3) { + case 0: len = 1; break; + case 1: len = 2; break; + case 2: len = 4; break; + default: + kvm_err(-EFAULT, "Invalid I/O abort"); + return -EFAULT; + } + + is_write = ((vcpu->arch.hsr >> 6) & 1); + sign_extend = ((vcpu->arch.hsr >> 21) & 1); + rd = (vcpu->arch.hsr >> 16) & 0xf; + BUG_ON(rd > 15); + + if (rd == 15) { + kvm_err(-EFAULT, "I/O memory trying to read/write pc"); + return -EFAULT; + } + + /* Get instruction length in bytes */ + instr_len = ((vcpu->arch.hsr >> 25) & 1) ? 4 : 2; + + if (!memslot) { + /* QEMU hack for missing devices - simply return 0 */ + if (!is_write) + vcpu_reg(vcpu, rd) = 0; + vcpu_reg(vcpu, 15) += instr_len; + return 0; + } + + /* Export MMIO operations to user space */ + vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->mmio.is_write = is_write; + vcpu->run->mmio.phys_addr = fault_ipa; + vcpu->run->mmio.len = len; + vcpu->arch.mmio_sign_extend = sign_extend; + vcpu->arch.mmio_rd = rd; + + if (is_write) + memcpy(run->mmio.data, &vcpu_reg(vcpu, rd), len); + + /* + * The MMIO instruction is emulated and should not be re-executed + * in the guest. + */ + vcpu_reg(vcpu, 15) += instr_len; + return 0; +} + #define HSR_ABT_FS (0x3f) #define HPFAR_MASK (~0xf) int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) @@ -335,7 +435,5 @@ io_mem_abort: return -EFAULT; } - kvm_msg("I/O address abort..."); - KVMARM_NOT_IMPLEMENTED(); - return -EINVAL; + return io_mem_abort(vcpu, run, fault_ipa, memslot); }