From patchwork Thu Aug 21 05:29:09 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victor Kamensky X-Patchwork-Id: 4755611 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 42044C0338 for ; Thu, 21 Aug 2014 05:32:30 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4B8D120108 for ; Thu, 21 Aug 2014 05:32:29 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5A7EE20107 for ; Thu, 21 Aug 2014 05:32:28 +0000 (UTC) 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 1XKKwo-00048P-4v; Thu, 21 Aug 2014 05:30:06 +0000 Received: from mail-pd0-f177.google.com ([209.85.192.177]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XKKwk-00038w-TO for linux-arm-kernel@lists.infradead.org; Thu, 21 Aug 2014 05:30:03 +0000 Received: by mail-pd0-f177.google.com with SMTP id p10so12748948pdj.8 for ; Wed, 20 Aug 2014 22:29:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=8TnTgkWbS33CPuABFHqGLNTCZYno8b4xgRrYhuBC/qE=; b=mD9A4iFMlBgcrl7Whn1JzM4C2OX6O/9BZaCKo7oyA5I7sVHruytN5Jbv9x8eMSXOe2 n9J8vMs5EocMW0OHgcYm536NlL0fVlzqXtE37A02SvzGPO9vbgv9395Kz2Tr0diZYaxz Ck1X+IBqfphTmCwpsvgqn6lUT/Vm4Ngn5iZs/jpraLKu4xzZ6QnAgZrGflgDR93Apufk ckJc2qzMPYE2nbWIp9e8ZEqDXK/lRXsGleMNRw1NHYyycq1syW/0y3s2Sp1puCfbrf7/ wEl+7sNtIQ4lOGuctAHK4XSd29oFbjRYY/XL2tW8VGT4SoWQ0WarBz/2SmvRRONWfBcA rsvw== X-Gm-Message-State: ALoCoQmHSGc9yDY1aLHIzhg/+jKM0Ts48Mgr7glFoOpR5f1/h8DAhzXQL/IA8YvlvAoTc8mPKt3/ X-Received: by 10.68.186.33 with SMTP id fh1mr58371454pbc.105.1408598980547; Wed, 20 Aug 2014 22:29:40 -0700 (PDT) Received: from kamensky-w530.cisco.com (128-107-239-233.cisco.com. [128.107.239.233]) by mx.google.com with ESMTPSA id oz7sm36872569pdb.77.2014.08.20.22.29.38 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 20 Aug 2014 22:29:39 -0700 (PDT) From: Victor Kamensky To: linux-arm-kernel@lists.infradead.org, daniel.thompson@linaro.org, rmk@arm.linux.org.uk Subject: [RFC PATCH] arm: fix get_user BE behavior for target variable with size of 8 bytes Date: Wed, 20 Aug 2014 22:29:09 -0700 Message-Id: <1408598949-23050-1-git-send-email-victor.kamensky@linaro.org> X-Mailer: git-send-email 1.8.1.4 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140820_223002_979412_5F26C1BC X-CRM114-Status: GOOD ( 14.72 ) X-Spam-Score: -1.4 (-) Cc: nicolas.pitre@linaro.org, Victor Kamensky , marc.zyngier@arm.com, will.deacon@arm.com, arnd.bergmann@linaro.org, christoffer.dall@linaro.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 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+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP e38361d 'ARM: 8091/2: add get_user() support for 8 byte types' commit broke V7 BE get_user call when target var size is 64 bit, but '*ptr' size is 32 bit or smaller. e38361d changed type of __r2 from 'register unsigned long' to 'register typeof(x) __r2 asm("r2")' i.e before the change even when target variable size was 64 bit, __r2 was still 32 bit. But after e38361d commit, for target var of 64 bit size, __r2 became 64 bit and now it should occupy 2 registers r2, and r3. The issue in BE case that r3 register is least significant word of __r2 and r2 register is most significant word of __r2. But __get_user_4 still copies result into r2 (most significant word of __r2). Subsequent code copies from __r2 into x, but for situation described it will pick up only garbage from r3 register. It was discovered during 3.17-rc1 V7 BE KVM testing. Simple test case below. Note it works in LE case because r2 in LE case is still least significant word. Proposed fix uninspiringly restores previous code but now in individual branches of switch statement for '*(__p)' byte sizes 1, 2, 4 and have newer code only for sizeof(*(__p)) == 8. Looking for may be better ideas how to fix the issue. Small test case C code char gut_lower_v64_p32 (int *ptr) { long long value = 0; get_user(value, ptr); return 0xff & value; } the following code in BE V7 image will be generated. Note uxtb access to r3 register, but __get_user_4 retrieves data into r2. (gdb) disassemble gut_lower_v64_p32 Dump of assembler code for function gut_lower_v64_p32: 0xc0022ec8 <+0>: push {lr} ; (str lr, [sp, #-4]!) 0xc0022ecc <+4>: mov r2, sp 0xc0022ed0 <+8>: bic r3, r2, #8128 ; 0x1fc0 0xc0022ed4 <+12>: bic r3, r3, #63 ; 0x3f 0xc0022ed8 <+16>: ldr r1, [r3, #8] 0xc0022edc <+20>: sub r1, r1, #1 0xc0022ee0 <+24>: bl 0xc03792ac <__get_user_4> 0xc0022ee4 <+28>: uxtb r0, r3 0xc0022ee8 <+32>: pop {pc} ; (ldr pc, [sp], #4) End of assembler dump. (gdb) disassemble __get_user_4 Dump of assembler code for function __get_user_4: 0xc03792ac <+0>: adds r2, r0, #3 0xc03792b0 <+4>: sbcscc r2, r2, r1 0xc03792b4 <+8>: bcs 0xc03792fc <__get_user_bad> 0xc03792b8 <+12>: ldr r2, [r0] 0xc03792bc <+16>: mov r0, #0 0xc03792c0 <+20>: bx lr End of assembler dump. Signed-off-by: Victor Kamensky --- arch/arm/include/asm/uaccess.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index a4cd7af..69b9292 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -141,28 +141,42 @@ extern int __get_user_8(void *); ({ \ unsigned long __limit = current_thread_info()->addr_limit - 1; \ register const typeof(*(p)) __user *__p asm("r0") = (p);\ - register typeof(x) __r2 asm("r2"); \ register unsigned long __l asm("r1") = __limit; \ register int __e asm("r0"); \ switch (sizeof(*(__p))) { \ case 1: \ + { \ + register unsigned long __r2 asm("r2"); \ __get_user_x(__r2, __p, __e, __l, 1); \ + x = (typeof(*(p))) __r2; \ break; \ + } \ case 2: \ + { \ + register unsigned long __r2 asm("r2"); \ __get_user_x(__r2, __p, __e, __l, 2); \ + x = (typeof(*(p))) __r2; \ break; \ + } \ case 4: \ + { \ + register unsigned long __r2 asm("r2"); \ __get_user_x(__r2, __p, __e, __l, 4); \ + x = (typeof(*(p))) __r2; \ break; \ + } \ case 8: \ + { \ + register typeof(x) __r2 asm("r2"); \ if (sizeof((x)) < 8) \ __get_user_xb(__r2, __p, __e, __l, 4); \ else \ __get_user_x(__r2, __p, __e, __l, 8); \ + x = (typeof(*(p))) __r2; \ break; \ + } \ default: __e = __get_user_bad(); break; \ } \ - x = (typeof(*(p))) __r2; \ __e; \ })