From patchwork Tue May 13 16:13:59 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: vkamensky X-Patchwork-Id: 30101 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-pa0-f72.google.com (mail-pa0-f72.google.com [209.85.220.72]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id DC67020446 for ; Tue, 13 May 2014 16:18:06 +0000 (UTC) Received: by mail-pa0-f72.google.com with SMTP id rd3sf2572879pab.11 for ; Tue, 13 May 2014 09:18:06 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:subject:date:message-id :in-reply-to:references:cc:precedence:list-id:list-unsubscribe :list-archive:list-post:list-help:list-subscribe:mime-version:sender :errors-to:x-original-sender:x-original-authentication-results :mailing-list:content-type:content-transfer-encoding; bh=2GjUBQg2U1T3WgUF4bHYL+FwlBvTF+e6MUd4Iy7EDxQ=; b=HDnPJdBvqn7QUaryl2cTPE/hke8vyHtQBhC7bqQ5+B/y+WkWxEAsGYNewqcDO9qq3E BZtxJJepSBa8GrqqCa1CmxU9z43zLHh+vRZ4vP+19r/k58w2U4NVNpwD+HN+REARwmpe GZw+f3/qKY4tp7S67LNcT8XEKjOWMPbWpd/BFkA/UEfxzJtsUo8shw1hIDZria1OOIjA x5j04Latueq2uF/t+PYg/dIb5RoO2IPaMRpbH4f7kFFlt6tiRfEBRYleTdZzBQxb9Q9/ s/ONQfn4IQcBQJF/w6hVAklQP/4IP1+8xnNjLjzn27nQ/hgANJAZw8aEbUMmGgC4XJLG 5KcQ== X-Gm-Message-State: ALoCoQky3LkiLc3HBdZcz68fvD0bTnMfktqyy2R5vdHczINAL2kaHf+T4ERJPHY9r0xP48toJDY6 X-Received: by 10.66.190.202 with SMTP id gs10mr18031760pac.0.1399997886219; Tue, 13 May 2014 09:18:06 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.107.97 with SMTP id g88ls1890920qgf.40.gmail; Tue, 13 May 2014 09:18:06 -0700 (PDT) X-Received: by 10.52.137.174 with SMTP id qj14mr7872459vdb.32.1399997886054; Tue, 13 May 2014 09:18:06 -0700 (PDT) Received: from mail-ve0-f182.google.com (mail-ve0-f182.google.com [209.85.128.182]) by mx.google.com with ESMTPS id ko12si2727226veb.189.2014.05.13.09.18.06 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 13 May 2014 09:18:06 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.128.182 as permitted sender) client-ip=209.85.128.182; Received: by mail-ve0-f182.google.com with SMTP id sa20so742385veb.13 for ; Tue, 13 May 2014 09:18:05 -0700 (PDT) X-Received: by 10.52.2.229 with SMTP id 5mr24700545vdx.24.1399997885825; Tue, 13 May 2014 09:18:05 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.221.72 with SMTP id ib8csp164181vcb; Tue, 13 May 2014 09:18:05 -0700 (PDT) X-Received: by 10.180.84.226 with SMTP id c2mr22135510wiz.50.1399997884275; Tue, 13 May 2014 09:18:04 -0700 (PDT) Received: from bombadil.infradead.org (bombadil.infradead.org. [2001:1868:205::9]) by mx.google.com with ESMTPS id bl10si821859wib.98.2014.05.13.09.18.03 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 13 May 2014 09:18:04 -0700 (PDT) Received-SPF: none (google.com: linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org does not designate permitted sender hosts) client-ip=2001:1868:205::9; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WkFNR-0003Af-Ol; Tue, 13 May 2014 16:16:25 +0000 Received: from mail-pa0-f46.google.com ([209.85.220.46]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WkFMR-0001of-SQ for linux-arm-kernel@lists.infradead.org; Tue, 13 May 2014 16:15:24 +0000 Received: by mail-pa0-f46.google.com with SMTP id kq14so458758pab.5 for ; Tue, 13 May 2014 09:15:03 -0700 (PDT) X-Received: by 10.68.241.68 with SMTP id wg4mr6276317pbc.66.1399997703013; Tue, 13 May 2014 09:15:03 -0700 (PDT) Received: from kamensky-w530.hsd1.ca.comcast.net (c-24-6-79-41.hsd1.ca.comcast.net. [24.6.79.41]) by mx.google.com with ESMTPSA id dd5sm28958360pbc.85.2014.05.13.09.15.01 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 13 May 2014 09:15:02 -0700 (PDT) From: Victor Kamensky To: kvmarm@lists.cs.columbia.edu, christoffer.dall@linaro.org, marc.zyngier@arm.com, agraf@suse.de Subject: [PATCH v3 07/14] ARM: KVM: one_reg coproc set and get BE fixes Date: Tue, 13 May 2014 09:13:59 -0700 Message-Id: <1399997646-4716-8-git-send-email-victor.kamensky@linaro.org> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1399997646-4716-1-git-send-email-victor.kamensky@linaro.org> References: <1399997646-4716-1-git-send-email-victor.kamensky@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140513_091523_965606_4E34BEC0 X-CRM114-Status: GOOD ( 20.87 ) X-Spam-Score: -0.7 (/) X-Spam-Report: SpamAssassin version 3.3.2 on bombadil.infradead.org summary: Content analysis details: (-0.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.220.46 listed in list.dnswl.org] Cc: taras.kondratiuk@linaro.org, linaro-kernel@lists.linaro.org, linux-arm-kernel@lists.infradead.org, Victor Kamensky X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: victor.kamensky@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.128.182 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 Fix code that handles KVM_SET_ONE_REG, KVM_GET_ONE_REG ioctls to work in BE image. Before this fix get/set_one_reg functions worked correctly only in LE case - reg_from_user was taking 'void *' kernel address that actually could be target/source memory of either 4 bytes size or 8 bytes size, and code copied from/to user memory that could hold either 4 bytes register, 8 byte register or pair of 4 bytes registers. For example note that there was a case when 4 bytes register was read from user-land to kernel target address of 8 bytes value. Because it was working in LE, least significant word was memcpy(ied) and it just worked. In BE code with 'void *' as target/source 'val' type it is impossible to tell whether 4 bytes register from user-land should be copied to 'val' address itself (4 bytes target) or it should be copied to 'val' + 4 (least significant word of 8 bytes value). So first change was to introduce strongly typed functions, where type of target/source 'val' is strongly defined: reg_from_user_to_u64 - reads register from user-land to kernel 'u64 *val' address; register size could be 4 or 8 bytes reg_from_user_to_u32 - reads register(s) from user-land to kernel 'u32 *val' address; note it could be one or two 4 bytes registers reg_to_user_from_u64 - writes register from kernel 'u64 *val' address to user-land register memory; register size could be 4 or 8 bytes ret_to_user_from_u32 - writes register(s) from kernel 'u32 *val' address to user-land register(s) memory; note it could be one or two 4 bytes registers All places where reg_from_user, reg_to_user functions were used, were changed to use either corresponding u64 or u32 variants of functions depending on type of source/target kernel memory variable. In case of 'u64 *val' and register size equals 4 bytes, reg_from_user_to_u64 and reg_to_user_from_u64 work only with least siginificant word of target/source kernel value. It would be nice to deal only with u32 kernel values in _u32 functions and with u64 kernel values in _u64 functions, however it is not always possible because functions like set_invariant_cp15 get_invariant_cp15 store values in u64 types but registers are 32bit. Signed-off-by: Victor Kamensky --- arch/arm/kvm/coproc.c | 118 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 26 deletions(-) diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index c58a351..5ca6582 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c @@ -682,17 +682,83 @@ static struct coproc_reg invariant_cp15[] = { { CRn( 0), CRm( 0), Op1( 1), Op2( 7), is32, NULL, get_AIDR }, }; -static int reg_from_user(void *val, const void __user *uaddr, u64 id) +/* + * Reads register value from user-land uaddr address into kernel u64 value + * given by val address. Note register size could be 4 bytes, so user-land + * 4 bytes value will be copied into least significant word. Or register + * size could be 8 bytes, so function works as regular copy. + */ +static int reg_from_user_to_u64(u64 *val, const void __user *uaddr, u64 id) +{ + unsigned long regsize = KVM_REG_SIZE(id); + union { + u32 word; + u64 dword; + } tmp = {0}; + + if (copy_from_user(&tmp, uaddr, regsize) != 0) + return -EFAULT; + + switch (regsize) { + case 4: + *val = tmp.word; + break; + case 8: + *val = tmp.dword; + break; + } + return 0; +} + +/* + * Reads register value from user-land uaddr address to kernel u32 value + * or array of two u32 values. I.e. it may really copy two u32 registers + * when used with register which size is 8 bytes (for example V7 64 + * bit registers like TTBR0/TTBR1). + */ +static int reg_from_user_to_u32(u32 *val, const void __user *uaddr, u64 id) { - /* This Just Works because we are little endian. */ if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0) return -EFAULT; return 0; } -static int reg_to_user(void __user *uaddr, const void *val, u64 id) +/* + * Writes register value to user-land uaddr address from kernel u64 value + * given by val address. Note register size could be 4 bytes, so only 4 + * bytes from least significant word will by copied into uaddr address. + * In case of 8 bytes sized register it works as regular copy. + */ +static int reg_to_user_from_u64(void __user *uaddr, const u64 *val, u64 id) +{ + unsigned long regsize = KVM_REG_SIZE(id); + union { + u32 word; + u64 dword; + } tmp; + + switch (regsize) { + case 4: + tmp.word = *val; + break; + case 8: + tmp.dword = *val; + break; + } + + if (copy_to_user(uaddr, &tmp, regsize) != 0) + return -EFAULT; + return 0; +} + +/* + * Writes register value to user-land uaddr address from kernel u32 value + * or arra of two u32 values. I.e it may really copy two u32 registers + * when used with register which size is 8 bytes (for example V7 64 + * bit registers like TTBR0/TTBR1). + */ +static int reg_to_user_from_u32(void __user *uaddr, const u32 *val, u64 id) { - /* This Just Works because we are little endian. */ if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0) return -EFAULT; return 0; @@ -710,7 +776,7 @@ static int get_invariant_cp15(u64 id, void __user *uaddr) if (!r) return -ENOENT; - return reg_to_user(uaddr, &r->val, id); + return reg_to_user_from_u64(uaddr, &r->val, id); } static int set_invariant_cp15(u64 id, void __user *uaddr) @@ -726,7 +792,7 @@ static int set_invariant_cp15(u64 id, void __user *uaddr) if (!r) return -ENOENT; - err = reg_from_user(&val, uaddr, id); + err = reg_from_user_to_u64(&val, uaddr, id); if (err) return err; @@ -894,8 +960,8 @@ static int vfp_get_reg(const struct kvm_vcpu *vcpu, u64 id, void __user *uaddr) if (vfpid < num_fp_regs()) { if (KVM_REG_SIZE(id) != 8) return -ENOENT; - return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpregs[vfpid], - id); + return reg_to_user_from_u64(uaddr, &vcpu->arch.vfp_guest.fpregs[vfpid], + id); } /* FP control registers are all 32 bit. */ @@ -904,22 +970,22 @@ static int vfp_get_reg(const struct kvm_vcpu *vcpu, u64 id, void __user *uaddr) switch (vfpid) { case KVM_REG_ARM_VFP_FPEXC: - return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpexc, id); + return reg_to_user_from_u32(uaddr, &vcpu->arch.vfp_guest.fpexc, id); case KVM_REG_ARM_VFP_FPSCR: - return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpscr, id); + return reg_to_user_from_u32(uaddr, &vcpu->arch.vfp_guest.fpscr, id); case KVM_REG_ARM_VFP_FPINST: - return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpinst, id); + return reg_to_user_from_u32(uaddr, &vcpu->arch.vfp_guest.fpinst, id); case KVM_REG_ARM_VFP_FPINST2: - return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpinst2, id); + return reg_to_user_from_u32(uaddr, &vcpu->arch.vfp_guest.fpinst2, id); case KVM_REG_ARM_VFP_MVFR0: val = fmrx(MVFR0); - return reg_to_user(uaddr, &val, id); + return reg_to_user_from_u32(uaddr, &val, id); case KVM_REG_ARM_VFP_MVFR1: val = fmrx(MVFR1); - return reg_to_user(uaddr, &val, id); + return reg_to_user_from_u32(uaddr, &val, id); case KVM_REG_ARM_VFP_FPSID: val = fmrx(FPSID); - return reg_to_user(uaddr, &val, id); + return reg_to_user_from_u32(uaddr, &val, id); default: return -ENOENT; } @@ -938,8 +1004,8 @@ static int vfp_set_reg(struct kvm_vcpu *vcpu, u64 id, const void __user *uaddr) if (vfpid < num_fp_regs()) { if (KVM_REG_SIZE(id) != 8) return -ENOENT; - return reg_from_user(&vcpu->arch.vfp_guest.fpregs[vfpid], - uaddr, id); + return reg_from_user_to_u64(&vcpu->arch.vfp_guest.fpregs[vfpid], + uaddr, id); } /* FP control registers are all 32 bit. */ @@ -948,28 +1014,28 @@ static int vfp_set_reg(struct kvm_vcpu *vcpu, u64 id, const void __user *uaddr) switch (vfpid) { case KVM_REG_ARM_VFP_FPEXC: - return reg_from_user(&vcpu->arch.vfp_guest.fpexc, uaddr, id); + return reg_from_user_to_u32(&vcpu->arch.vfp_guest.fpexc, uaddr, id); case KVM_REG_ARM_VFP_FPSCR: - return reg_from_user(&vcpu->arch.vfp_guest.fpscr, uaddr, id); + return reg_from_user_to_u32(&vcpu->arch.vfp_guest.fpscr, uaddr, id); case KVM_REG_ARM_VFP_FPINST: - return reg_from_user(&vcpu->arch.vfp_guest.fpinst, uaddr, id); + return reg_from_user_to_u32(&vcpu->arch.vfp_guest.fpinst, uaddr, id); case KVM_REG_ARM_VFP_FPINST2: - return reg_from_user(&vcpu->arch.vfp_guest.fpinst2, uaddr, id); + return reg_from_user_to_u32(&vcpu->arch.vfp_guest.fpinst2, uaddr, id); /* These are invariant. */ case KVM_REG_ARM_VFP_MVFR0: - if (reg_from_user(&val, uaddr, id)) + if (reg_from_user_to_u32(&val, uaddr, id)) return -EFAULT; if (val != fmrx(MVFR0)) return -EINVAL; return 0; case KVM_REG_ARM_VFP_MVFR1: - if (reg_from_user(&val, uaddr, id)) + if (reg_from_user_to_u32(&val, uaddr, id)) return -EFAULT; if (val != fmrx(MVFR1)) return -EINVAL; return 0; case KVM_REG_ARM_VFP_FPSID: - if (reg_from_user(&val, uaddr, id)) + if (reg_from_user_to_u32(&val, uaddr, id)) return -EFAULT; if (val != fmrx(FPSID)) return -EINVAL; @@ -1016,7 +1082,7 @@ int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) return get_invariant_cp15(reg->id, uaddr); /* Note: copies two regs if size is 64 bit. */ - return reg_to_user(uaddr, &vcpu->arch.cp15[r->reg], reg->id); + return reg_to_user_from_u32(uaddr, &vcpu->arch.cp15[r->reg], reg->id); } int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) @@ -1035,7 +1101,7 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) return set_invariant_cp15(reg->id, uaddr); /* Note: copies two regs if size is 64 bit */ - return reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id); + return reg_from_user_to_u32(&vcpu->arch.cp15[r->reg], uaddr, reg->id); } static unsigned int num_demux_regs(void)