Skip to content

Commit

Permalink
ARM: Allow SMP kernels to boot on UP systems
Browse files Browse the repository at this point in the history
UP systems do not implement all the instructions that SMP systems have,
so in order to boot a SMP kernel on a UP system, we need to rewrite
parts of the kernel.

Do this using an 'alternatives' scheme, where the kernel code and data
is modified prior to initialization to replace the SMP instructions,
thereby rendering the problematical code ineffectual.  We use the linker
to generate a list of 32-bit word locations and their replacement values,
and run through these replacements when we detect a UP system.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Russell King committed Oct 4, 2010
1 parent 0671735 commit f00ec48
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 102 deletions.
13 changes: 13 additions & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,19 @@ config SMP

If you don't know what to do here, say N.

config SMP_ON_UP
bool "Allow booting SMP kernel on uniprocessor systems (EXPERIMENTAL)"
depends on EXPERIMENTAL
depends on SMP && !XIP && !THUMB2_KERNEL
default y
help
SMP kernels contain instructions which fail on non-SMP processors.
Enabling this option allows the kernel to modify itself to make
these instructions safe. Disabling it allows about 1K of space
savings.

If you don't know what to do here, say Y.

config HAVE_ARM_SCU
bool
depends on SMP
Expand Down
27 changes: 25 additions & 2 deletions arch/arm/include/asm/assembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,39 @@
.long 9999b,9001f; \
.popsection

#ifdef CONFIG_SMP
#define ALT_SMP(instr...) \
9998: instr
#define ALT_UP(instr...) \
.pushsection ".alt.smp.init", "a" ;\
.long 9998b ;\
instr ;\
.popsection
#define ALT_UP_B(label) \
.equ up_b_offset, label - 9998b ;\
.pushsection ".alt.smp.init", "a" ;\
.long 9998b ;\
b . + up_b_offset ;\
.popsection
#else
#define ALT_SMP(instr...)
#define ALT_UP(instr...) instr
#define ALT_UP_B(label) b label
#endif

/*
* SMP data memory barrier
*/
.macro smp_dmb
#ifdef CONFIG_SMP
#if __LINUX_ARM_ARCH__ >= 7
dmb
ALT_SMP(dmb)
#elif __LINUX_ARM_ARCH__ == 6
mcr p15, 0, r0, c7, c10, 5 @ dmb
ALT_SMP(mcr p15, 0, r0, c7, c10, 5) @ dmb
#else
#error Incompatible SMP platform
#endif
ALT_UP(nop)
#endif
.endm

Expand Down
7 changes: 6 additions & 1 deletion arch/arm/include/asm/smp_mpidr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
#define hard_smp_processor_id() \
({ \
unsigned int cpunum; \
__asm__("mrc p15, 0, %0, c0, c0, 5\n" \
__asm__("\n" \
"1: mrc p15, 0, %0, c0, c0, 5\n" \
" .pushsection \".alt.smp.init\", \"a\"\n"\
" .long 1b\n" \
" mov %0, #0\n" \
" .popsection" \
: "=r" (cpunum)); \
cpunum &= 0x0F; \
})
Expand Down
15 changes: 15 additions & 0 deletions arch/arm/include/asm/smp_plat.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,19 @@ static inline int cache_ops_need_broadcast(void)
return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 1;
}

/*
* Return true if we are running on a SMP platform
*/
static inline bool is_smp(void)
{
#ifndef CONFIG_SMP
return false;
#elif defined(CONFIG_SMP_ON_UP)
extern unsigned int smp_on_up;
return !!smp_on_up;
#else
return true;
#endif
}

#endif
24 changes: 17 additions & 7 deletions arch/arm/include/asm/tlbflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
#undef _TLB
#undef MULTI_TLB

#ifdef CONFIG_SMP_ON_UP
#define MULTI_TLB 1
#endif

#define v3_tlb_flags (TLB_V3_FULL | TLB_V3_PAGE)

#ifdef CONFIG_CPU_TLB_V3
Expand Down Expand Up @@ -185,17 +189,23 @@
# define v6wbi_always_flags (-1UL)
#endif

#ifdef CONFIG_SMP
#define v7wbi_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_V7_IS_BTB | \
#define v7wbi_tlb_flags_smp (TLB_WB | TLB_DCLEAN | TLB_V7_IS_BTB | \
TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | TLB_V7_UIS_ASID)
#else
#define v7wbi_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_BTB | \
#define v7wbi_tlb_flags_up (TLB_WB | TLB_DCLEAN | TLB_BTB | \
TLB_V6_U_FULL | TLB_V6_U_PAGE | TLB_V6_U_ASID)
#endif

#ifdef CONFIG_CPU_TLB_V7
# define v7wbi_possible_flags v7wbi_tlb_flags
# define v7wbi_always_flags v7wbi_tlb_flags

# ifdef CONFIG_SMP_ON_UP
# define v7wbi_possible_flags (v7wbi_tlb_flags_smp | v7wbi_tlb_flags_up)
# define v7wbi_always_flags (v7wbi_tlb_flags_smp & v7wbi_tlb_flags_up)
# elif defined(CONFIG_SMP)
# define v7wbi_possible_flags v7wbi_tlb_flags_smp
# define v7wbi_always_flags v7wbi_tlb_flags_smp
# else
# define v7wbi_possible_flags v7wbi_tlb_flags_up
# define v7wbi_always_flags v7wbi_tlb_flags_up
# endif
# ifdef _TLB
# define MULTI_TLB 1
# else
Expand Down
11 changes: 5 additions & 6 deletions arch/arm/kernel/entry-armv.S
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
* this macro assumes that irqstat (r6) and base (r5) are
* preserved from get_irqnr_and_base above
*/
test_for_ipi r0, r6, r5, lr
ALT_SMP(test_for_ipi r0, r6, r5, lr)
ALT_UP_B(9997f)
movne r0, sp
adrne lr, BSYM(1b)
bne do_IPI
Expand All @@ -57,6 +58,7 @@
adrne lr, BSYM(1b)
bne do_local_timer
#endif
9997:
#endif

.endm
Expand Down Expand Up @@ -965,11 +967,8 @@ kuser_cmpxchg_fixup:
beq 1b
rsbs r0, r3, #0
/* beware -- each __kuser slot must be 8 instructions max */
#ifdef CONFIG_SMP
b __kuser_memory_barrier
#else
usr_ret lr
#endif
ALT_SMP(b __kuser_memory_barrier)
ALT_UP(usr_ret lr)

#endif

Expand Down
50 changes: 50 additions & 0 deletions arch/arm/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ ENTRY(stext)
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
bl __vet_atags
#ifdef CONFIG_SMP_ON_UP
bl __fixup_smp
#endif
bl __create_page_tables

/*
Expand Down Expand Up @@ -333,4 +336,51 @@ __create_page_tables:
ENDPROC(__create_page_tables)
.ltorg

#ifdef CONFIG_SMP_ON_UP
__fixup_smp:
mov r7, #0x00070000
orr r6, r7, #0xff000000 @ mask 0xff070000
orr r7, r7, #0x41000000 @ val 0x41070000
and r0, r9, r6
teq r0, r7 @ ARM CPU and ARMv6/v7?
bne __fixup_smp_on_up @ no, assume UP

orr r6, r6, #0x0000ff00
orr r6, r6, #0x000000f0 @ mask 0xff07fff0
orr r7, r7, #0x0000b000
orr r7, r7, #0x00000020 @ val 0x4107b020
and r0, r9, r6
teq r0, r7 @ ARM 11MPCore?
moveq pc, lr @ yes, assume SMP

mrc p15, 0, r0, c0, c0, 5 @ read MPIDR
tst r0, #1 << 31
movne pc, lr @ bit 31 => SMP

__fixup_smp_on_up:
adr r0, 1f
ldmia r0, {r3, r6, r7}
sub r3, r0, r3
add r6, r6, r3
add r7, r7, r3
2: cmp r6, r7
ldmia r6!, {r0, r4}
strlo r4, [r0, r3]
blo 2b
mov pc, lr
ENDPROC(__fixup_smp)

1: .word .
.word __smpalt_begin
.word __smpalt_end

.pushsection .data
.globl smp_on_up
smp_on_up:
ALT_SMP(.long 1)
ALT_UP(.long 0)
.popsection

#endif

#include "head-common.S"
4 changes: 3 additions & 1 deletion arch/arm/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <asm/procinfo.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/smp_plat.h>
#include <asm/mach-types.h>
#include <asm/cacheflush.h>
#include <asm/cachetype.h>
Expand Down Expand Up @@ -825,7 +826,8 @@ void __init setup_arch(char **cmdline_p)
request_standard_resources(&meminfo, mdesc);

#ifdef CONFIG_SMP
smp_init_cpus();
if (is_smp())
smp_init_cpus();
#endif
reserve_crashkernel();

Expand Down
11 changes: 11 additions & 0 deletions arch/arm/kernel/vmlinux.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ SECTIONS
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
#ifdef CONFIG_SMP_ON_UP
__smpalt_begin = .;
*(.alt.smp.init)
__smpalt_end = .;
#endif

INIT_SETUP(16)

Expand Down Expand Up @@ -237,6 +242,12 @@ SECTIONS

/* Default discards */
DISCARDS

#ifndef CONFIG_SMP_ON_UP
/DISCARD/ : {
*(.alt.smp.init)
}
#endif
}

/*
Expand Down
14 changes: 4 additions & 10 deletions arch/arm/mm/cache-v7.S
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,8 @@ ENTRY(v7_flush_kern_cache_all)
THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
bl v7_flush_dcache_all
mov r0, #0
#ifdef CONFIG_SMP
mcr p15, 0, r0, c7, c1, 0 @ invalidate I-cache inner shareable
#else
mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
#endif
ALT_SMP(mcr p15, 0, r0, c7, c1, 0) @ invalidate I-cache inner shareable
ALT_UP(mcr p15, 0, r0, c7, c5, 0) @ I+BTB cache invalidate
ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
mov pc, lr
Expand Down Expand Up @@ -171,11 +168,8 @@ ENTRY(v7_coherent_user_range)
cmp r0, r1
blo 1b
mov r0, #0
#ifdef CONFIG_SMP
mcr p15, 0, r0, c7, c1, 6 @ invalidate BTB Inner Shareable
#else
mcr p15, 0, r0, c7, c5, 6 @ invalidate BTB
#endif
ALT_SMP(mcr p15, 0, r0, c7, c1, 6) @ invalidate BTB Inner Shareable
ALT_UP(mcr p15, 0, r0, c7, c5, 6) @ invalidate BTB
dsb
isb
mov pc, lr
Expand Down
46 changes: 21 additions & 25 deletions arch/arm/mm/mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,8 @@ static void __init build_mem_type_table(void)
cachepolicy = CPOLICY_WRITEBACK;
ecc_mask = 0;
}
#ifdef CONFIG_SMP
cachepolicy = CPOLICY_WRITEALLOC;
#endif
if (is_smp())
cachepolicy = CPOLICY_WRITEALLOC;

/*
* Strip out features not present on earlier architectures.
Expand Down Expand Up @@ -406,13 +405,11 @@ static void __init build_mem_type_table(void)
cp = &cache_policies[cachepolicy];
vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;

#ifndef CONFIG_SMP
/*
* Only use write-through for non-SMP systems
*/
if (cpu_arch >= CPU_ARCH_ARMv5 && cachepolicy > CPOLICY_WRITETHROUGH)
if (!is_smp() && cpu_arch >= CPU_ARCH_ARMv5 && cachepolicy > CPOLICY_WRITETHROUGH)
vecs_pgprot = cache_policies[CPOLICY_WRITETHROUGH].pte;
#endif

/*
* Enable CPU-specific coherency if supported.
Expand All @@ -436,22 +433,23 @@ static void __init build_mem_type_table(void)
mem_types[MT_MINICLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;
mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;

#ifdef CONFIG_SMP
/*
* Mark memory with the "shared" attribute for SMP systems
*/
user_pgprot |= L_PTE_SHARED;
kern_pgprot |= L_PTE_SHARED;
vecs_pgprot |= L_PTE_SHARED;
mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_S;
mem_types[MT_DEVICE_WC].prot_pte |= L_PTE_SHARED;
mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_S;
mem_types[MT_DEVICE_CACHED].prot_pte |= L_PTE_SHARED;
mem_types[MT_MEMORY].prot_sect |= PMD_SECT_S;
mem_types[MT_MEMORY].prot_pte |= L_PTE_SHARED;
mem_types[MT_MEMORY_NONCACHED].prot_sect |= PMD_SECT_S;
mem_types[MT_MEMORY_NONCACHED].prot_pte |= L_PTE_SHARED;
#endif
if (is_smp()) {
/*
* Mark memory with the "shared" attribute
* for SMP systems
*/
user_pgprot |= L_PTE_SHARED;
kern_pgprot |= L_PTE_SHARED;
vecs_pgprot |= L_PTE_SHARED;
mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_S;
mem_types[MT_DEVICE_WC].prot_pte |= L_PTE_SHARED;
mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_S;
mem_types[MT_DEVICE_CACHED].prot_pte |= L_PTE_SHARED;
mem_types[MT_MEMORY].prot_sect |= PMD_SECT_S;
mem_types[MT_MEMORY].prot_pte |= L_PTE_SHARED;
mem_types[MT_MEMORY_NONCACHED].prot_sect |= PMD_SECT_S;
mem_types[MT_MEMORY_NONCACHED].prot_pte |= L_PTE_SHARED;
}
}

/*
Expand Down Expand Up @@ -829,8 +827,7 @@ static void __init sanity_check_meminfo(void)
* rather difficult.
*/
reason = "with VIPT aliasing cache";
#ifdef CONFIG_SMP
} else if (tlb_ops_need_broadcast()) {
} else if (is_smp() && tlb_ops_need_broadcast()) {
/*
* kmap_high needs to occasionally flush TLB entries,
* however, if the TLB entries need to be broadcast
Expand All @@ -840,7 +837,6 @@ static void __init sanity_check_meminfo(void)
* (must not be called with irqs off)
*/
reason = "without hardware TLB ops broadcasting";
#endif
}
if (reason) {
printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n",
Expand Down
Loading

0 comments on commit f00ec48

Please sign in to comment.