From patchwork Tue Oct 8 15:31:12 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?C=C3=A9dric_Le_Goater?= X-Patchwork-Id: 281500 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 0CB162C00D0 for ; Wed, 9 Oct 2013 02:31:36 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753758Ab3JHPbf (ORCPT ); Tue, 8 Oct 2013 11:31:35 -0400 Received: from e06smtp13.uk.ibm.com ([195.75.94.109]:47584 "EHLO e06smtp13.uk.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751657Ab3JHPbe (ORCPT ); Tue, 8 Oct 2013 11:31:34 -0400 Received: from /spool/local by e06smtp13.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 8 Oct 2013 16:31:32 +0100 Received: from d06dlp02.portsmouth.uk.ibm.com (9.149.20.14) by e06smtp13.uk.ibm.com (192.168.101.143) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 8 Oct 2013 16:31:29 +0100 Received: from b06cxnps4076.portsmouth.uk.ibm.com (d06relay13.portsmouth.uk.ibm.com [9.149.109.198]) by d06dlp02.portsmouth.uk.ibm.com (Postfix) with ESMTP id 29DFE2190094; Tue, 8 Oct 2013 16:31:28 +0100 (BST) Received: from d06av02.portsmouth.uk.ibm.com (d06av02.portsmouth.uk.ibm.com [9.149.37.228]) by b06cxnps4076.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id r98FVFZj66912312; Tue, 8 Oct 2013 15:31:15 GMT Received: from d06av02.portsmouth.uk.ibm.com (localhost [127.0.0.1]) by d06av02.portsmouth.uk.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id r98FVQl4014903; Tue, 8 Oct 2013 09:31:27 -0600 Received: from smtp.lab.toulouse-stg.fr.ibm.com (srv01.lab.toulouse-stg.fr.ibm.com [9.101.4.1]) by d06av02.portsmouth.uk.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id r98FVP07014849; Tue, 8 Oct 2013 09:31:25 -0600 Received: from hermes.lab.toulouse-stg.fr.ibm.com (hermes.lab.toulouse-stg.fr.ibm.com [9.101.4.42]) by smtp.lab.toulouse-stg.fr.ibm.com (Postfix) with ESMTP id 05BE3210FF5; Tue, 8 Oct 2013 17:31:25 +0200 (CEST) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: agraf@suse.de, paulus@samba.org Cc: kvm-ppc@vger.kernel.org, kvm@vger.kernel.org, =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Subject: [PATCH v3 3/3] KVM: PPC: Book3S: MMIO emulation support for little endian guests Date: Tue, 8 Oct 2013 17:31:12 +0200 Message-Id: <1381246272-29284-1-git-send-email-clg@fr.ibm.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <52541F96.6030304@fr.ibm.com> References: <52541F96.6030304@fr.ibm.com> MIME-Version: 1.0 X-TM-AS-MML: No X-Content-Scanned: Fidelis XPS MAILER x-cbid: 13100815-2966-0000-0000-000008FAC872 Sender: kvm-ppc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm-ppc@vger.kernel.org MMIO emulation reads the last instruction executed by the guest and then emulates. If the guest is running in Little Endian mode, the instruction needs to be byte-swapped before being emulated. This patch stores the last instruction in the endian order of the host, primarily doing a byte-swap if needed. The common code which fetches 'last_inst' uses a helper routine kvmppc_need_byteswap(). and the exit paths for the Book3S PV and HR guests use their own version in assembly. Finally, the meaning of the 'is_bigendian' argument of the routines kvmppc_handle_load() of kvmppc_handle_store() is slightly changed to represent an eventual reverse operation. This is used in conjunction with kvmppc_is_bigendian() to determine if the instruction being emulated should be byte-swapped. Signed-off-by: Cédric Le Goater --- Changes in v3: - moved kvmppc_need_byteswap() in kvmppc_ld32. It previously was in kvmppc_ld_inst(). (Alexander Graf) Changes in v2: - replaced rldicl. by andi. to test the MSR_LE bit in the guest exit paths. (Paul Mackerras) - moved the byte swapping logic to kvmppc_handle_load() and kvmppc_handle_load() by changing the is_bigendian parameter meaning. (Paul Mackerras) arch/powerpc/include/asm/kvm_book3s.h | 9 ++++++++- arch/powerpc/include/asm/kvm_ppc.h | 10 +++++----- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 11 +++++++++++ arch/powerpc/kvm/book3s_segment.S | 10 ++++++++++ arch/powerpc/kvm/emulate.c | 1 - arch/powerpc/kvm/powerpc.c | 16 ++++++++++++---- 6 files changed, 46 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index 4ee6c66..9403042 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -283,7 +283,14 @@ static inline bool kvmppc_is_bigendian(struct kvm_vcpu *vcpu) static inline int kvmppc_ld32(struct kvm_vcpu *vcpu, ulong *eaddr, u32 *ptr, bool data) { - return kvmppc_ld(vcpu, eaddr, sizeof(u32), ptr, data); + int ret; + + ret = kvmppc_ld(vcpu, eaddr, sizeof(u32), ptr, data); + + if (kvmppc_need_byteswap(vcpu)) + *ptr = swab32(*ptr); + + return ret; } static inline int kvmppc_ld_inst(struct kvm_vcpu *vcpu, ulong *eaddr, diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index b15554a..3769a13 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -53,13 +53,13 @@ extern void kvmppc_handler_highmem(void); extern void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu); extern int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu, - unsigned int rt, unsigned int bytes, - int is_bigendian); + unsigned int rt, unsigned int bytes, + int not_reverse); extern int kvmppc_handle_loads(struct kvm_run *run, struct kvm_vcpu *vcpu, - unsigned int rt, unsigned int bytes, - int is_bigendian); + unsigned int rt, unsigned int bytes, + int not_reverse); extern int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu, - u64 val, unsigned int bytes, int is_bigendian); + u64 val, unsigned int bytes, int not_reverse); extern int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu); diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 77f1baa..ff7da8b 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -1404,10 +1404,21 @@ fast_interrupt_c_return: lwz r8, 0(r10) mtmsrd r3 + ld r0, VCPU_MSR(r9) + + andi. r10, r0, MSR_LE + /* Store the result */ stw r8, VCPU_LAST_INST(r9) + beq after_inst_store + + /* Swap and store the result */ + addi r11, r9, VCPU_LAST_INST + stwbrx r8, 0, r11 + /* Unset guest mode. */ +after_inst_store: li r0, KVM_GUEST_MODE_HOST_HV stb r0, HSTATE_IN_GUEST(r13) b guest_exit_cont diff --git a/arch/powerpc/kvm/book3s_segment.S b/arch/powerpc/kvm/book3s_segment.S index 1abe478..677ef7a 100644 --- a/arch/powerpc/kvm/book3s_segment.S +++ b/arch/powerpc/kvm/book3s_segment.S @@ -287,8 +287,18 @@ ld_last_inst: sync #endif + ld r8, SVCPU_SHADOW_SRR1(r13) + + andi. r10, r8, MSR_LE + stw r0, SVCPU_LAST_INST(r13) + beq no_ld_last_inst + + /* swap and store the result */ + addi r11, r13, SVCPU_LAST_INST + stwbrx r0, 0, r11 + no_ld_last_inst: /* Unset guest mode */ diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index 751cd45..5e38004 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -219,7 +219,6 @@ static int kvmppc_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt) * lmw * stmw * - * XXX is_bigendian should depend on MMU mapping or MSR[LE] */ /* XXX Should probably auto-generate instruction decoding for a particular core * from opcode tables in the future. */ diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 07c0106..6950f2b 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -625,9 +625,13 @@ static void kvmppc_complete_mmio_load(struct kvm_vcpu *vcpu, } int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu, - unsigned int rt, unsigned int bytes, int is_bigendian) + unsigned int rt, unsigned int bytes, int not_reverse) { int idx, ret; + int is_bigendian = not_reverse; + + if (!kvmppc_is_bigendian(vcpu)) + is_bigendian = !not_reverse; if (bytes > sizeof(run->mmio.data)) { printk(KERN_ERR "%s: bad MMIO length: %d\n", __func__, @@ -662,21 +666,25 @@ int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu, /* Same as above, but sign extends */ int kvmppc_handle_loads(struct kvm_run *run, struct kvm_vcpu *vcpu, - unsigned int rt, unsigned int bytes, int is_bigendian) + unsigned int rt, unsigned int bytes, int not_reverse) { int r; vcpu->arch.mmio_sign_extend = 1; - r = kvmppc_handle_load(run, vcpu, rt, bytes, is_bigendian); + r = kvmppc_handle_load(run, vcpu, rt, bytes, not_reverse); return r; } int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu, - u64 val, unsigned int bytes, int is_bigendian) + u64 val, unsigned int bytes, int not_reverse) { void *data = run->mmio.data; int idx, ret; + int is_bigendian = not_reverse; + + if (!kvmppc_is_bigendian(vcpu)) + is_bigendian = !not_reverse; if (bytes > sizeof(run->mmio.data)) { printk(KERN_ERR "%s: bad MMIO length: %d\n", __func__,