Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f00ec48f authored by Russell King's avatar Russell King
Browse files

ARM: Allow SMP kernels to boot on UP systems



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: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 06717352
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line Diff line number Diff line
@@ -1191,6 +1191,19 @@ config SMP


	  If you don't know what to do here, say N.
	  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
config HAVE_ARM_SCU
	bool
	bool
	depends on SMP
	depends on SMP
+25 −2
Original line number Original line Diff line number Diff line
@@ -154,16 +154,39 @@
	.long	9999b,9001f;			\
	.long	9999b,9001f;			\
	.popsection
	.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
 * SMP data memory barrier
 */
 */
	.macro	smp_dmb
	.macro	smp_dmb
#ifdef CONFIG_SMP
#ifdef CONFIG_SMP
#if __LINUX_ARM_ARCH__ >= 7
#if __LINUX_ARM_ARCH__ >= 7
	dmb
	ALT_SMP(dmb)
#elif __LINUX_ARM_ARCH__ == 6
#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
#endif
	ALT_UP(nop)
#endif
#endif
	.endm
	.endm


+6 −1
Original line number Original line Diff line number Diff line
@@ -4,7 +4,12 @@
#define hard_smp_processor_id()						\
#define hard_smp_processor_id()						\
	({								\
	({								\
		unsigned int cpunum;					\
		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));				\
			: "=r" (cpunum));				\
		cpunum &= 0x0F;						\
		cpunum &= 0x0F;						\
	})
	})
+15 −0
Original line number Original line Diff line number Diff line
@@ -18,4 +18,19 @@ static inline int cache_ops_need_broadcast(void)
	return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 1;
	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
#endif
+17 −7
Original line number Original line Diff line number Diff line
@@ -70,6 +70,10 @@
#undef _TLB
#undef _TLB
#undef MULTI_TLB
#undef MULTI_TLB


#ifdef CONFIG_SMP_ON_UP
#define MULTI_TLB 1
#endif

#define v3_tlb_flags	(TLB_V3_FULL | TLB_V3_PAGE)
#define v3_tlb_flags	(TLB_V3_FULL | TLB_V3_PAGE)


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


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


#ifdef CONFIG_CPU_TLB_V7
#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
# ifdef _TLB
#  define MULTI_TLB 1
#  define MULTI_TLB 1
# else
# else
Loading