ARM: 6516/1: Allow SMP_ON_UP to work with Thumb-2 kernels.
Dave Martin [Wed, 1 Dec 2010 14:39:23 +0000 (15:39 +0100)]
  * __fixup_smp_on_up has been modified with support for the
    THUMB2_KERNEL case.  For THUMB2_KERNEL only, fixups are split
    into halfwords in case of misalignment, since we can't rely on
    unaligned accesses working before turning the MMU on.

    No attempt is made to optimise the aligned case, since the
    number of fixups is typically small, and it seems best to keep
    the code as simple as possible.

  * Add a rotate in the fixup_smp code in order to support
    CPU_BIG_ENDIAN, as suggested by Nicolas Pitre.

  * Add an assembly-time sanity-check to ALT_UP() to ensure that
    the content really is the right size (4 bytes).

    (No check is done for ALT_SMP().  Possibly, this could be fixed
    by splitting the two uses ot ALT_SMP() (ALT_SMP...SMP_UP versus
    ALT_SMP...SMP_UP_B) into two macros.  In the first case,
    ALT_SMP needs to expand to >= 4 bytes, not == 4.)

  * smp_mpidr.h (which implements ALT_SMP()/ALT_UP() manually due
    to macro limitations) has not been modified: the affected
    instruction (mov) has no 16-bit encoding, so the correct
    instruction size is satisfied in this case.

  * A "mode" parameter has been added to smp_dmb:

    smp_dmb arm @ assumes 4-byte instructions (for ARM code, e.g. kuser)
    smp_dmb     @ uses W() to ensure 4-byte instructions for ALT_SMP()

    This avoids assembly failures due to use of W() inside smp_dmb,
    when assembling pure-ARM code in the vectors page.

    There might be a better way to achieve this.

  * Kconfig: make SMP_ON_UP depend on
    (!THUMB2_KERNEL || !BIG_ENDIAN) i.e., THUMB2_KERNEL is now
    supported, but only if !BIG_ENDIAN (The fixup code for Thumb-2
    currently assumes little-endian order.)

Tested using a single generic realview kernel on:
ARM RealView PB-A8 (CONFIG_THUMB2_KERNEL={n,y})
ARM RealView PBX-A9 (SMP)

Signed-off-by: Dave Martin <dave.martin@linaro.org>
Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

arch/arm/Kconfig
arch/arm/include/asm/assembler.h
arch/arm/kernel/entry-armv.S
arch/arm/kernel/head.S

index db524e7..290a4b5 100644 (file)
@@ -1229,7 +1229,7 @@ config SMP
 config SMP_ON_UP
        bool "Allow booting SMP kernel on uniprocessor systems (EXPERIMENTAL)"
        depends on EXPERIMENTAL
-       depends on SMP && !XIP && !THUMB2_KERNEL
+       depends on SMP && !XIP
        default y
        help
          SMP kernels contain instructions which fail on non-SMP processors.
index 749bb66..72d3389 100644 (file)
 #ifdef CONFIG_SMP
 #define ALT_SMP(instr...)                                      \
 9998:  instr
+/*
+ * Note: if you get assembler errors from ALT_UP() when building with
+ * CONFIG_THUMB2_KERNEL, you almost certainly need to use
+ * ALT_SMP( W(instr) ... )
+ */
 #define ALT_UP(instr...)                                       \
        .pushsection ".alt.smp.init", "a"                       ;\
        .long   9998b                                           ;\
-       instr                                                   ;\
+9997:  instr                                                   ;\
+       .if . - 9997b != 4                                      ;\
+               .error "ALT_UP() content must assemble to exactly 4 bytes";\
+       .endif                                                  ;\
        .popsection
 #define ALT_UP_B(label)                                        \
        .equ    up_b_offset, label - 9998b                      ;\
        .pushsection ".alt.smp.init", "a"                       ;\
        .long   9998b                                           ;\
-       b       . + up_b_offset                                 ;\
+       W(b)    . + up_b_offset                                 ;\
        .popsection
 #else
 #define ALT_SMP(instr...)
 /*
  * SMP data memory barrier
  */
-       .macro  smp_dmb
+       .macro  smp_dmb mode
 #ifdef CONFIG_SMP
 #if __LINUX_ARM_ARCH__ >= 7
+       .ifeqs "\mode","arm"
        ALT_SMP(dmb)
+       .else
+       ALT_SMP(W(dmb))
+       .endif
 #elif __LINUX_ARM_ARCH__ == 6
        ALT_SMP(mcr     p15, 0, r0, c7, c10, 5) @ dmb
 #else
 #error Incompatible SMP platform
 #endif
+       .ifeqs "\mode","arm"
        ALT_UP(nop)
+       .else
+       ALT_UP(W(nop))
+       .endif
 #endif
        .endm
 
index 955cf5f..7f22a11 100644 (file)
@@ -842,7 +842,7 @@ __kuser_helper_start:
  */
 
 __kuser_memory_barrier:                                @ 0xffff0fa0
-       smp_dmb
+       smp_dmb arm
        usr_ret lr
 
        .align  5
@@ -959,7 +959,7 @@ kuser_cmpxchg_fixup:
 
 #else
 
-       smp_dmb
+       smp_dmb arm
 1:     ldrex   r3, [r2]
        subs    r3, r3, r0
        strexeq r3, r1, [r2]
index fd94e4e..359e54e 100644 (file)
@@ -412,10 +412,17 @@ __fixup_smp_on_up:
        add     r4, r4, r3
        add     r5, r5, r3
 2:     cmp     r4, r5
+       movhs   pc, lr
        ldmia   r4!, {r0, r6}
-       strlo   r6, [r0, r3]
-       blo     2b
-       mov     pc, lr
+ ARM(  str     r6, [r0, r3]    )
+ THUMB(        add     r0, r0, r3      )
+#ifdef __ARMEB__
+ THUMB(        mov     r6, r6, ror #16 )       @ Convert word order for big-endian.
+#endif
+ THUMB(        strh    r6, [r0], #2    )       @ For Thumb-2, store as two halfwords
+ THUMB(        mov     r6, r6, lsr #16 )       @ to be robust against misaligned r3.
+ THUMB(        strh    r6, [r0]        )
+       b       2b
 ENDPROC(__fixup_smp)
 
 1:     .word   .