ARM: 7882/1: mm: fix __phys_to_virt to work with 64 bit phys_addr_t in BE case

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Fri Nov 22 17:59:05 EST 2013


Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=139cc2ba7400dab80228a2bfa683e2f49cf5d3ff
Commit:     139cc2ba7400dab80228a2bfa683e2f49cf5d3ff
Parent:     10593b2e49327f7cd193fc2ba30fa3da322bda6a
Author:     Victor Kamensky <victor.kamensky at linaro.org>
AuthorDate: Thu Nov 7 08:42:41 2013 +0100
Committer:  Russell King <rmk+kernel at arm.linux.org.uk>
CommitDate: Thu Nov 14 11:13:08 2013 +0000

    ARM: 7882/1: mm: fix __phys_to_virt to work with 64 bit phys_addr_t in BE case
    
    Make sure that inline assembler that expects 'r' operand
    receives 32 bit value.
    
    Before this fix in case of CONFIG_ARCH_PHYS_ADDR_T_64BIT and
    CONFIG_ARM_PATCH_PHYS_VIRT __phys_to_virt function passed 64 bit
    value to __pv_stub inline assembler where 'r' operand is
    expected. Compiler behavior in such case is not well specified.
    It worked in little endian case, but in big endian case
    incorrect code was generated, where compiler confused which
    part of 64 bit value it needed to modify. For example BE
    snippet looked like this:
    
    N:0x80904E08 : MOV      r2,#0
    N:0x80904E0C : SUB      r2,r2,#0x81000000
    
    when LE similar code looked like this
    
    N:0x808FCE2C : MOV      r2,r0
    N:0x808FCE30 : SUB      r2,r2,#0xc0, 8 ; #0xc0000000
    
    Note 'r0' register is va that have to be translated into phys
    
    To avoid this situation use explicit cast to 'unsigned long',
    which explicitly discard upper part of phys address and convert
    value to 32 bit. Also add comment so such cast will not be
    removed in the future.
    
    Signed-off-by: Victor Kamensky <victor.kamensky at linaro.org>
    Acked-by: Santosh Shilimkar <santosh.shilimkar at ti.com>
    Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 arch/arm/include/asm/memory.h | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 4dd2145..9ecccc8 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -226,7 +226,14 @@ static inline phys_addr_t __virt_to_phys(unsigned long x)
 static inline unsigned long __phys_to_virt(phys_addr_t x)
 {
 	unsigned long t;
-	__pv_stub(x, t, "sub", __PV_BITS_31_24);
+
+	/*
+	 * 'unsigned long' cast discard upper word when
+	 * phys_addr_t is 64 bit, and makes sure that inline
+	 * assembler expression receives 32 bit argument
+	 * in place where 'r' 32 bit operand is expected.
+	 */
+	__pv_stub((unsigned long) x, t, "sub", __PV_BITS_31_24);
 	return t;
 }
 



More information about the linux-mtd-cvs mailing list