[PATCH 3/3] Allow PHYS_OFFSET to be runtime determined

Uwe Kleine-König u.kleine-koenig at pengutronix.de
Sat Jan 30 16:07:40 EST 2010


This bases on work done earlier by Lennert Buytenhek and Mark A. Greer.
Compared to their approach zreladdr isn't guessed based on the pc
register but the bootloader is expected to pass PHYS_OFFSET in r3.  If
that value doesn't look right (e.g. isn't aligned) it is guessed based
on the value of sp.  This should work for CONFIG_ZBOOT_ROM, too.

To use it for your machine removing the definition of PHYS_OFFSET from
<mach/memory.h> and selecting CONFIG_RUNTIME_PHYS_OFFSET should be
enough.

Cc: Lennert Buytenhek <buytenh at wantstofly.org>
Cc: Steve Chen <schen at mvista.com>
Cc: Mark A. Greer <mgreer at mvista.com>
Cc: Kevin Hilman <khilman at deeprootsystems.com>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig at pengutronix.de>
---
 Documentation/arm/Booting         |    1 +
 arch/arm/Kconfig                  |    3 ++
 arch/arm/boot/compressed/Makefile |    1 +
 arch/arm/boot/compressed/head.S   |   50 +++++++++++++++++++++++++++++++++++++
 arch/arm/include/asm/memory.h     |    5 +++
 arch/arm/kernel/head.S            |   31 +++++++++++++++++------
 arch/arm/kernel/setup.c           |   10 ++++++-
 7 files changed, 92 insertions(+), 9 deletions(-)

diff --git a/Documentation/arm/Booting b/Documentation/arm/Booting
index 7685029..ec228a4 100644
--- a/Documentation/arm/Booting
+++ b/Documentation/arm/Booting
@@ -126,6 +126,7 @@ In either case, the following conditions must be met:
   r0 = 0,
   r1 = machine type number discovered in (3) above.
   r2 = physical address of tagged list in system RAM.
+  r3 = PHYS_OFFSET
 
 - CPU mode
   All forms of interrupts must be disabled (IRQs and FIQs)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 3064e0c..ac5f59e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1254,6 +1254,9 @@ config UACCESS_WITH_MEMCPY
 	  However, if the CPU data cache is using a write-allocate mode,
 	  this option is unlikely to provide any performance gain.
 
+config RUNTIME_PHYS_OFFSET
+	bool
+
 config DEPRECATED_PARAM_STRUCT
 	bool "Provide old way to pass kernel parameters"
 	help
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 470adae..a651330 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -4,6 +4,7 @@
 # create a compressed vmlinuz image from the original vmlinux
 #
 
+AFLAGS_head.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
 HEAD	= head.o
 OBJS	= misc.o
 FONTC	= $(srctree)/drivers/video/console/font_acorn_8x8.c
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 0938d02..6441e85 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -120,6 +120,16 @@ wait:		mrc	p14, 0, pc, c0, c1, 0
 #endif
 		.endm
 
+		.macro	debug_passed_physoffset
+#ifdef DEBUG
+		bleq	1f
+		kputc	#'!'
+		kphex	r9, 8
+		kputc	#'\n'
+1:
+#endif
+		.endm
+
 		.section ".start", #alloc, #execinstr
 /*
  * sort out different calling conventions
@@ -137,6 +147,7 @@ start:
 		.word	_edata			@ zImage end address
 1:		mov	r7, r1			@ save architecture ID
 		mov	r8, r2			@ save atags pointer
+		mov	r9, r3			@ save phys_offset
 
 #ifndef __ARM_ARCH_2__
 		/*
@@ -233,6 +244,44 @@ not_relocated:	mov	r0, #0
 		cmp	r2, r3
 		blo	1b
 
+#ifdef CONFIG_RUNTIME_PHYS_OFFSET
+		/*
+		 * assert physoffset passed by bootloader is properly
+		 * 2MiB-aligned, ...
+		 */
+		ldr	r10, =0x001fffff
+
+		tst	r9, r10
+		debug_passed_physoffset
+		tst	r9, r10
+
+		/*
+		 * ... if not guess it based on sp.
+		 * sp & 0xf8000000 should work for most machines.  The needed
+		 * preconditions are:
+		 * - physoffset is aligned to a 128MiB boundary
+		 *   (As of Jan 2010 all but s3c2400, u300 and at91 have it.
+		 *   For the latter it depends on configuration.)
+		 * - sp < physoffset + 128MiB (which is definitely true if you
+		 *   only have 128MiB of RAM or less)
+		 */
+		andne	r9, sp, #0xf8000000
+
+#ifdef DEBUG
+		kputc	#'P'
+		kphex	r9, 8
+		kputc	#'\n'
+#endif
+
+		add	r4, r9, #TEXT_OFFSET
+#else /* ifdef CONFIG_RUNTIME_PHYS_OFFSET */
+		/* warn on r4(ZRELADDR) != r9 + TEXT_OFFSET */
+		add	r10, r9, #TEXT_OFFSET
+		cmp	r10, r4
+		debug_passed_physoffset
+
+#endif /* ifdef CONFIG_RUNTIME_PHYS_OFFSET / else */
+
 		/*
 		 * The C runtime environment should now be setup
 		 * sufficiently.  Turn the cache on, set up some
@@ -571,6 +620,7 @@ call_kernel:	bl	cache_clean_flush
 		mov	r0, #0			@ must be zero
 		mov	r1, r7			@ restore architecture number
 		mov	r2, r8			@ restore atags pointer
+		sub	r3, r4, #TEXT_OFFSET	@ physoffset
 		mov	pc, r4			@ call kernel
 
 /*
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 5421d82..a7390f9 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -24,6 +24,11 @@
  */
 #define UL(x) _AC(x, UL)
 
+#if defined(CONFIG_RUNTIME_PHYS_OFFSET) && !defined(__ASSEMBLY__)
+extern unsigned long phys_offset;
+#define PHYS_OFFSET	phys_offset
+#endif
+
 #ifdef CONFIG_MMU
 
 /*
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index eb62bf9..b173bb6 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -22,12 +22,14 @@
 #include <asm/thread_info.h>
 #include <asm/system.h>
 
+#if !defined(CONFIG_RUNTIME_PHYS_OFFSET)
 #if (PHYS_OFFSET & 0x001fffff)
 #error "PHYS_OFFSET must be at an even 2MiB boundary!"
 #endif
+#define KERNEL_RAM_PADDR	(PHYS_OFFSET + TEXT_OFFSET)
+#endif
 
 #define KERNEL_RAM_VADDR	(PAGE_OFFSET + TEXT_OFFSET)
-#define KERNEL_RAM_PADDR	(PHYS_OFFSET + TEXT_OFFSET)
 
 
 /*
@@ -44,8 +46,8 @@
 	.globl	swapper_pg_dir
 	.equ	swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
 
-	.macro	pgtbl, rd
-	ldr	\rd, =(KERNEL_RAM_PADDR - 0x4000)
+	.macro	pgtbl, rd, phys_offset
+	add	\rd, \phys_offset, #(TEXT_OFFSET - 0x4000)
 	.endm
 
 #ifdef CONFIG_XIP_KERNEL
@@ -215,9 +217,25 @@ ENDPROC(__turn_mmu_on)
  * Returns:
  *  r0, r3, r6, r7 corrupted
  *  r4 = physical page table address
+ *  r5 = physical start address of (the first bank of) RAM (PHYS_OFFSET)
  */
 __create_page_tables:
-	pgtbl	r4				@ page table address
+#if defined(CONFIG_RUNTIME_PHYS_OFFSET)
+	@ stext is at PHYS_OFFSET + TEXT_OFFSET.  As PHYS_OFFSET has to be
+	@ 2MiB-aligned and assuming that TEXT_OFFSET < 2MiB
+	@ stext & 0xffe00000 yields PHYS_OFFSET
+	adr	r5, stext
+	ldr	r4, =0xffe00000
+	and	r5, r5, r4
+
+	@ save phys_offset
+	ldr	r4, =(phys_offset - PAGE_OFFSET)
+	str	r5, [r4, r5]
+#else
+	ldr	r5, =PHYS_OFFSET
+#endif
+
+	pgtbl	r4, r5				@ r4 = page table address
 
 	/*
 	 * Clear the 16K level 1 swapper page table
@@ -282,10 +300,7 @@ __create_page_tables:
 	 * Then map first 1MB of ram in case it contains our boot params.
 	 */
 	add	r0, r4, #PAGE_OFFSET >> 18
-	orr	r6, r7, #(PHYS_OFFSET & 0xff000000)
-	.if	(PHYS_OFFSET & 0x00f00000)
-	orr	r6, r6, #(PHYS_OFFSET & 0x00f00000)
-	.endif
+	orr	r6, r7, r5
 	str	r6, [r0]
 
 #ifdef CONFIG_DEBUG_LL
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 3c38d4f..7f63135 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -68,6 +68,12 @@ __setup("fpe=", fpe_setup);
 extern void paging_init(struct machine_desc *desc);
 extern void reboot_setup(char *str);
 
+#if defined(CONFIG_RUNTIME_PHYS_OFFSET)
+/* assign a value to prevent phys_offset from ending in up bss */
+unsigned long phys_offset = 0xdeadbeef;
+EXPORT_SYMBOL(phys_offset);
+#endif
+
 unsigned int processor_id;
 EXPORT_SYMBOL(processor_id);
 unsigned int __machine_arch_type;
@@ -682,7 +688,7 @@ static struct init_tags {
 	{ tag_size(tag_core), ATAG_CORE },
 	{ 1, PAGE_SIZE, 0xff },
 	{ tag_size(tag_mem32), ATAG_MEM },
-	{ MEM_SIZE, PHYS_OFFSET },
+	{ MEM_SIZE, },
 	{ 0, ATAG_NONE }
 };
 
@@ -723,6 +729,8 @@ void __init setup_arch(char **cmdline_p)
 		tags = phys_to_virt(__atags_pointer);
 	else if (mdesc->boot_params)
 		tags = phys_to_virt(mdesc->boot_params);
+	else
+		init_tags.mem.start = PHYS_OFFSET;
 
 #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
 	/*
-- 
1.6.6




More information about the linux-arm-kernel mailing list