Subject: [PATCH 1/4] powerpc: consolidate feature fixup code

From: Benjamin Herrenschmidt <[email-masked]>
Date: Thu, 12 Oct 2006 18:29:53 +1000

There are currently two versions of the functions for applying the
feature fixups, one for CPU features and one for firmware features. In
addition, they are both in assembly.

This patch replaces them with a single C function. The call site is
slightly moved on ppc64 as well to be called from C instead of from
assembly, though it's a very small move, and thus shouldn't cause any
problem (called at the start of setup_system() instead of just before
calling it).

Signed-off-by: Benjamin Herrenschmidt <[email-masked]>

Index: linux-cell/arch/powerpc/kernel/head_64.S
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/head_64.S	2006-10-12 14:24:10.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/head_64.S	2006-10-12 14:24:54.000000000 +1000
@@ -2000,13 +2000,6 @@ _STATIC(start_here_common)
 	li	r0,0
 	stdu	r0,-STACK_FRAME_OVERHEAD(r1)
 
-	/* Apply the CPUs-specific fixups (nop out sections not relevant
-	 * to this CPU
-	 */
-	li	r3,0
-	bl	.do_cpu_ftr_fixups
-	bl	.do_fw_ftr_fixups
-
 	/* ptr to current */
 	LOAD_REG_IMMEDIATE(r4, init_task)
 	std	r4,PACACURRENT(r13)
Index: linux-cell/arch/powerpc/kernel/misc_64.S
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/misc_64.S	2006-10-12 14:24:10.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/misc_64.S	2006-10-12 14:24:54.000000000 +1000
@@ -277,99 +277,6 @@ _GLOBAL(identify_cpu)
 	mr	r3,r5
 	bctr
 
-/*
- * do_cpu_ftr_fixups - goes through the list of CPU feature fixups
- * and writes nop's over sections of code that don't apply for this cpu.
- * r3 = data offset (not changed)
- */
-_GLOBAL(do_cpu_ftr_fixups)
-	/* Get CPU 0 features */
-	LOAD_REG_IMMEDIATE(r6,cur_cpu_spec)
-	sub	r6,r6,r3
-	ld	r4,0(r6)
-	sub	r4,r4,r3
-	ld	r4,CPU_SPEC_FEATURES(r4)
-	/* Get the fixup table */
-	LOAD_REG_IMMEDIATE(r6,__start___ftr_fixup)
-	sub	r6,r6,r3
-	LOAD_REG_IMMEDIATE(r7,__stop___ftr_fixup)
-	sub	r7,r7,r3
-	/* Do the fixup */
-1:	cmpld	r6,r7
-	bgelr
-	addi	r6,r6,32
-	ld	r8,-32(r6)	/* mask */
-	and	r8,r8,r4
-	ld	r9,-24(r6)	/* value */
-	cmpld	r8,r9
-	beq	1b
-	ld	r8,-16(r6)	/* section begin */
-	ld	r9,-8(r6)	/* section end */
-	subf.	r9,r8,r9
-	beq	1b
-	/* write nops over the section of code */
-	/* todo: if large section, add a branch at the start of it */
-	srwi	r9,r9,2
-	mtctr	r9
-	sub	r8,r8,r3
-	lis	r0,0x60000000@h	/* nop */
-3:	stw	r0,0(r8)
-	andi.	r10,r4,CPU_FTR_SPLIT_ID_CACHE@l
-	beq	2f
-	dcbst	0,r8		/* suboptimal, but simpler */
-	sync
-	icbi	0,r8
-2:	addi	r8,r8,4
-	bdnz	3b
-	sync			/* additional sync needed on g4 */
-	isync
-	b	1b
-
-/*
- * do_fw_ftr_fixups - goes through the list of firmware feature fixups
- * and writes nop's over sections of code that don't apply for this firmware.
- * r3 = data offset (not changed)
- */
-_GLOBAL(do_fw_ftr_fixups)
-	/* Get firmware features */
-	LOAD_REG_IMMEDIATE(r6,powerpc_firmware_features)
-	sub	r6,r6,r3
-	ld	r4,0(r6)
-	/* Get the fixup table */
-	LOAD_REG_IMMEDIATE(r6,__start___fw_ftr_fixup)
-	sub	r6,r6,r3
-	LOAD_REG_IMMEDIATE(r7,__stop___fw_ftr_fixup)
-	sub	r7,r7,r3
-	/* Do the fixup */
-1:	cmpld	r6,r7
-	bgelr
-	addi	r6,r6,32
-	ld	r8,-32(r6)	/* mask */
-	and	r8,r8,r4
-	ld	r9,-24(r6)	/* value */
-	cmpld	r8,r9
-	beq	1b
-	ld	r8,-16(r6)	/* section begin */
-	ld	r9,-8(r6)	/* section end */
-	subf.	r9,r8,r9
-	beq	1b
-	/* write nops over the section of code */
-	/* todo: if large section, add a branch at the start of it */
-	srwi	r9,r9,2
-	mtctr	r9
-	sub	r8,r8,r3
-	lis	r0,0x60000000@h	/* nop */
-3:	stw	r0,0(r8)
-BEGIN_FTR_SECTION
-	dcbst	0,r8		/* suboptimal, but simpler */
-	sync
-	icbi	0,r8
-END_FTR_SECTION_IFSET(CPU_FTR_SPLIT_ID_CACHE)
-	addi	r8,r8,4
-	bdnz	3b
-	sync			/* additional sync needed on g4 */
-	isync
-	b	1b
 
 #if defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE)
 /*
Index: linux-cell/arch/powerpc/kernel/setup-common.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/setup-common.c	2006-10-12 14:24:10.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/setup-common.c	2006-10-12 17:46:02.000000000 +1000
@@ -520,3 +520,32 @@ void __init setup_panic(void)
 {
 	atomic_notifier_chain_register(&panic_notifier_list, &ppc_panic_block);
 }
+
+void do_feature_fixups(unsigned long offset, unsigned long value,
+		       void *fixup_start, void *fixup_end)
+{
+	struct fixup_entry {
+		unsigned long	mask;
+		unsigned long	value;
+		unsigned int	*start;
+		unsigned int	*end;
+	} *fcur, *fend;
+
+	fcur = fixup_start;
+	fend = fixup_end;
+
+	for (; fcur < fend; fcur++) {
+		unsigned int *pcur, *pend;
+
+		if ((value & fcur->mask) == fcur->value)
+			continue;
+
+		pcur = fcur->start - offset;
+		pend = fcur->end - offset;
+		for (; pcur < pend; pcur++) {
+			*pcur = 0x60000000u;
+			asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync"
+			     : : "r" (pcur));
+		}
+	}
+}
Index: linux-cell/arch/powerpc/kernel/setup.h
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/setup.h	2006-10-12 14:24:10.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/setup.h	2006-10-12 17:46:02.000000000 +1000
@@ -1,9 +1,12 @@
 #ifndef _POWERPC_KERNEL_SETUP_H
 #define _POWERPC_KERNEL_SETUP_H
 
-void check_for_initrd(void);
-void do_init_bootmem(void);
-void setup_panic(void);
+extern void check_for_initrd(void);
+extern void do_init_bootmem(void);
+extern void setup_panic(void);
+extern void do_feature_fixups(unsigned long offset, unsigned long value,
+			      void *fixup_start, void *fixup_end);
+
 extern int do_early_xmon;
 
 #endif /* _POWERPC_KERNEL_SETUP_H */
Index: linux-cell/arch/powerpc/kernel/setup_32.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/setup_32.c	2006-10-12 14:24:10.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/setup_32.c	2006-10-12 17:46:24.000000000 +1000
@@ -90,6 +90,7 @@ int ucache_bsize;
  */
 unsigned long __init early_init(unsigned long dt_ptr)
 {
+	extern unsigned int __start___ftr_fixup, __stop___ftr_fixup;
 	unsigned long offset = reloc_offset();
 
 	/* First zero the BSS -- use memset_io, some platforms don't have
@@ -101,7 +102,9 @@ unsigned long __init early_init(unsigned
 	 * that depend on which cpu we have.
 	 */
 	identify_cpu(offset, 0);
-	do_cpu_ftr_fixups(offset);
+
+	do_feature_fixups(offset, cur_cpu_spec->cpu_features,
+			  &__start___ftr_fixup, &__stop___ftr_fixup);
 
 	return KERNELBASE + offset;
 }
Index: linux-cell/arch/powerpc/kernel/setup_64.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/setup_64.c	2006-10-12 14:24:10.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/setup_64.c	2006-10-12 17:46:18.000000000 +1000
@@ -346,8 +346,19 @@ static void __init initialize_cache_info
  */
 void __init setup_system(void)
 {
+	extern unsigned int __start___ftr_fixup, __stop___ftr_fixup;
+	extern unsigned int __start___fw_ftr_fixup, __stop___fw_ftr_fixup;
+
 	DBG(" -> setup_system()\n");
 
+	/* Apply the CPUs-specific and firmware specific fixups to kernel
+	 * text (nop out sections not relevant to this CPU or this firmware)
+	 */
+	do_feature_fixups(0, cur_cpu_spec->cpu_features,
+			  &__start___ftr_fixup, &__stop___ftr_fixup);
+	do_feature_fixups(0, powerpc_firmware_features,
+			  &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup);
+
 	/*
 	 * Unflatten the device-tree passed by prom_init or kexec
 	 */