Skip to contentSkip to conversation

ARM big-endian - atags and loader updates

PATCH New Ben Dooks 2 participants 10 messages Jun 19, 2013 – Jun 27, 2013 Arm (32-bit)

lore
Share & tools
lore

Ben Dooks

author
#0 lore raw

Add a series of patches to update the system to support little-endian boot loaders with big-endian kernels. Mostly fixes for ATAGS parsing and loader magic values.

author
#1 lore raw

PATCH 1/6 ARM: add CPU_BE8_BOOT_LE configuration

Add configuration for booting a BE8 system from a LE boot environment.

---
 arch/arm/mm/Kconfig |    7 +++++++
 1 file changed, 7 insertions(+)
arch/arm/mm/Kconfig +7 -0
index 35955b5..2e1e70f 100644
@@ -688,6 +688,13 @@ config CPU_ENDIAN_BE8
 	help
 	  Support for the BE-8 (big-endian) mode on ARMv6 and ARMv7 processors.
 
+config CPU_BE8_BOOT_LE
+	bool "Boot BE system from LE loader"
+	depends on CPU_ENDIAN_BE8
+	help
+	  Support for running BE-8 (big-endian) from a boot environment that is
+	  running in LE mode.
+
 config CPU_ENDIAN_BE32
 	bool
 	depends on CPU_BIG_ENDIAN
Signature · 1 line
1.7.10.4
#2 lore raw

PATCH 1/6 ARM: add CPU_BE8_BOOT_LE configuration

Dear Ben Dooks,

arch/arm/mm/Kconfig config CPU_ENDIAN_BE8 · line 697

Quoted 7 lines · Ben Dooks
arch/arm/mm/Kconfig +7 -0
+config CPU_BE8_BOOT_LE
+	bool "Boot BE system from LE loader"
+	depends on CPU_ENDIAN_BE8
+	help
+	  Support for running BE-8 (big-endian) from a boot environment that is
+	  running in LE mode.
+

I'm not sure to understand how this option articulates with the CPU_ENDIAN_BE8 option, because your other patch series ("Initial big-endian support series") in its PATCH 4 ("ARM: set BE8 if LE in head code") already switches the kernel into BE mode even if it's booted from a Little Endian bootloader.

Maybe what you wanted to achieve is:

* The first patch series "Initial big-endian support series" allows to run the kernel big-endian if the bootloader has booted it big-endian.

* The second patch series "ARM big-endian - atags and loader updates", allows to run the kernel big-endian even if the bootloader started it little-endian.

In this case, then the PATCH 4 of your first patch series ("ARM: set BE8 if LE in head code") should be in the second patch series, and the code be conditional under CPU_BE8_BOOT_LE.

Or maybe I'm missing something?

Best regards,

Thomas

Signature · 4 lines
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
author
#3 lore raw

PATCH 1/6 ARM: add CPU_BE8_BOOT_LE configuration

Quoted 23 lines · Thomas Petazzoni

Dear Ben Dooks,

arch/arm/mm/Kconfig config CPU_ENDIAN_BE8 · line 697

Quoted 7 lines · Ben Dooks
arch/arm/mm/Kconfig +7 -0
+config CPU_BE8_BOOT_LE
+	bool "Boot BE system from LE loader"
+	depends on CPU_ENDIAN_BE8
+	help
+	  Support for running BE-8 (big-endian) from a boot environment that is
+	  running in LE mode.
+

I'm not sure to understand how this option articulates with the CPU_ENDIAN_BE8 option, because your other patch series ("Initial big-endian support series") in its PATCH 4 ("ARM: set BE8 if LE in head code") already switches the kernel into BE mode even if it's booted from a Little Endian bootloader.

Maybe what you wanted to achieve is:

* The first patch series "Initial big-endian support series" allows to run the kernel big-endian if the bootloader has booted it big-endian.

Quoted 7 lines

* The second patch series "ARM big-endian - atags and loader updates", allows to run the kernel big-endian even if the bootloader started it little-endian.

In this case, then the PATCH 4 of your first patch series ("ARM: set BE8 if LE in head code") should be in the second patch series, and the code be conditional under CPU_BE8_BOOT_LE.

I suppose the second series should be to allow little endian ATAGS based loaders to boot the kernel. If the loader starts the kernel LE with OF then it is easy to swap to big endian in the head code.

The secondary boots will always require the LE->BE switching as there is no guarantee that if the primary processor started the kernel BE8 that any of the secondary cpus will be started in the same way.

Signature · 2 lines
Ben Dooks				http://www.codethink.co.uk/
Senior Engineer				Codethink - Providing Genius
author
#4 lore raw

PATCH 2/6 ARM: fixup head for atag verification

In reply to Ben Dooks

If we booted LE but running BE8, ensure we read ATAGs data from head code in the correct mode.

---
 arch/arm/kernel/head-common.S |    8 ++++++++
 1 file changed, 8 insertions(+)
arch/arm/kernel/head-common.S +8 -0
index 5b391a6..ae73ffe 100644
@@ -21,6 +21,12 @@
 #define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */
 #endif
 
+#ifdef CONFIG_CPU_BE8_BOOT_LE
+#define BE8_LE(x...)	x
+#else
+#define BE8_LE(x...)
+#endif
+
 /*
  * Exception handling.  Something went wrong and we can't proceed.  We
  * ought to tell the user, but since we don't have any guarantee that
@@ -53,10 +59,12 @@ __vet_atags:
 	cmp	r5, r6
 	beq	2f
 #endif
+BE8_LE(	rev	r5, r5 )
 	cmp	r5, #ATAG_CORE_SIZE		@ is first tag ATAG_CORE?
 	cmpne	r5, #ATAG_CORE_SIZE_EMPTY
 	bne	1f
 	ldr	r5, [r2, #4]
+BE8_LE(	rev	r5, r5 )
 	ldr	r6, =ATAG_CORE
 	cmp	r5, r6
 	bne	1f
Signature · 1 line
1.7.10.4
author
#5 lore raw

PATCH 3/6 ARM: add atag32_to_cpu() function

In reply to Ben Dooks

Add atag32_to_cpu() function to allow code manipulating ATAGs to deal with the case where the boot-loader was in little-endian and Linux is running big-endian.

---
 arch/arm/include/uapi/asm/setup.h |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
arch/arm/include/uapi/asm/setup.h +9 -2
index 979ff40..d3b3fa1 100644
@@ -175,13 +175,20 @@ struct tagtable {
 
 #define tag_member_present(tag,member)				\
 	((unsigned long)(&((struct tag *)0L)->member + 1)	\
-		<= (tag)->hdr.size * 4)
+		<= atag32_to_cpu((tag)->hdr.size) * 4)
 
-#define tag_next(t)	((struct tag *)((__u32 *)(t) + (t)->hdr.size))
+#define tag_next(t)	((struct tag *)((__u32 *)(t) + atag32_to_cpu((t)->hdr.size)))
 #define tag_size(type)	((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
 
 #define for_each_tag(t,base)		\
 	for (t = base; t->hdr.size; t = tag_next(t))
 
+#ifdef CONFIG_CPU_BE8_BOOT_LE
+#define atag32_to_cpu(x)	le32_to_cpu(x)
+#define cpu_to_atag32(x)	cpu_to_le32(x)
+#else
+#define	cpu_to_atag32(x)	x
+#define atag32_to_cpu(x)	x
+#endif
 
 #endif /* _UAPI__ASMARM_SETUP_H */
Signature · 1 line
1.7.10.4
author
#6 lore raw

PATCH 4/6 ARM: update atag-to-fdt code to be endian agnostic

In reply to Ben Dooks

The atag-to-fdt code can now use the conversion function we introduced previously to avoid it having to know about the endian-ness of the environment which booted the processor.

---
 arch/arm/boot/compressed/atags_to_fdt.c |   22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
arch/arm/boot/compressed/atags_to_fdt.c +11 -11
index aabc02a..74e193e 100644
@@ -108,9 +108,9 @@ int atags_to_fdt(void *atag_list, void *fdt, int total_space)
 	       return 0;
 
 	/* validate the ATAG */
-	if (atag->hdr.tag != ATAG_CORE ||
-	    (atag->hdr.size != tag_size(tag_core) &&
-	     atag->hdr.size != 2))
+	if (atag->hdr.tag != cpu_to_atag32(ATAG_CORE) ||
+	    (atag->hdr.size != cpu_to_atag32(tag_size(tag_core)) &&
+	     atag->hdr.size != cpu_to_atag32(2)))
 		return 1;
 
 	/* let's give it all the room it could need */
@@ -119,7 +119,7 @@ int atags_to_fdt(void *atag_list, void *fdt, int total_space)
 		return ret;
 
 	for_each_tag(atag, atag_list) {
-		if (atag->hdr.tag == ATAG_CMDLINE) {
+		if (atag->hdr.tag == cpu_to_atag32(ATAG_CMDLINE)) {
 			/* Append the ATAGS command line to the device tree
 			 * command line.
 			 * NB: This means that if the same parameter is set in
@@ -132,17 +132,17 @@ int atags_to_fdt(void *atag_list, void *fdt, int total_space)
 			else
 				setprop_string(fdt, "/chosen", "bootargs",
 					       atag->u.cmdline.cmdline);
-		} else if (atag->hdr.tag == ATAG_MEM) {
+		} else if (atag->hdr.tag == cpu_to_atag32(ATAG_MEM)) {
 			if (memcount >= sizeof(mem_reg_property)/4)
 				continue;
-			if (!atag->u.mem.size)
+			if (!atag32_to_cpu(atag->u.mem.size))
 				continue;
-			mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.start);
-			mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.size);
-		} else if (atag->hdr.tag == ATAG_INITRD2) {
+			mem_reg_property[memcount++] = cpu_to_fdt32(atag32_to_cpu(atag->u.mem.start));
+			mem_reg_property[memcount++] = cpu_to_fdt32(atag32_to_cpu(atag->u.mem.size));
+		} else if (atag->hdr.tag == cpu_to_atag32(ATAG_INITRD2)) {
 			uint32_t initrd_start, initrd_size;
-			initrd_start = atag->u.initrd.start;
-			initrd_size = atag->u.initrd.size;
+			initrd_start = atag32_to_cpu(atag->u.initrd.start);
+			initrd_size = atag32_to_cpu(atag->u.initrd.size);
 			setprop_cell(fdt, "/chosen", "linux,initrd-start",
 					initrd_start);
 			setprop_cell(fdt, "/chosen", "linux,initrd-end",
Signature · 1 line
1.7.10.4
author
#7 lore raw

PATCH 5/6 ARM: fixup atags to be endian agnostic

In reply to Ben Dooks

Use the atag32_to_cpu() function to allow the main atag handling code to be agnostic of any endian configuration.

---
 arch/arm/kernel/atags_parse.c |   51 +++++++++++++++++++++--------------------
 arch/arm/kernel/atags_proc.c  |    6 ++---
 2 files changed, 29 insertions(+), 28 deletions(-)
arch/arm/kernel/atags_parse.c +26 -25
index 14512e6..312899b 100644
@@ -43,19 +43,19 @@ static struct {
 	struct tag_mem32  mem;
 	struct tag_header hdr3;
 } default_tags __initdata = {
-	{ tag_size(tag_core), ATAG_CORE },
-	{ 1, PAGE_SIZE, 0xff },
-	{ tag_size(tag_mem32), ATAG_MEM },
-	{ MEM_SIZE },
-	{ 0, ATAG_NONE }
+	{ cpu_to_atag32(tag_size(tag_core)), cpu_to_atag32(ATAG_CORE) },
+	{ cpu_to_atag32(1), atag32_to_cpu(PAGE_SIZE), cpu_to_atag32(0xff) },
+	{ cpu_to_atag32(tag_size(tag_mem32)), cpu_to_atag32(ATAG_MEM) },
+	{ cpu_to_atag32(MEM_SIZE) },
+	{ cpu_to_atag32(0), cpu_to_atag32(ATAG_NONE) }
 };
 
 static int __init parse_tag_core(const struct tag *tag)
 {
-	if (tag->hdr.size > 2) {
-		if ((tag->u.core.flags & 1) == 0)
+	if (atag32_to_cpu(tag->hdr.size) > 2) {
+		if ((atag32_to_cpu(tag->u.core.flags) & 1) == 0)
 			root_mountflags &= ~MS_RDONLY;
-		ROOT_DEV = old_decode_dev(tag->u.core.rootdev);
+		ROOT_DEV = old_decode_dev(atag32_to_cpu(tag->u.core.rootdev));
 	}
 	return 0;
 }
@@ -64,7 +64,7 @@ __tagtable(ATAG_CORE, parse_tag_core);
 
 static int __init parse_tag_mem32(const struct tag *tag)
 {
-	return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
+	return arm_add_memory(atag32_to_cpu(tag->u.mem.start), atag32_to_cpu(tag->u.mem.size));
 }
 
 __tagtable(ATAG_MEM, parse_tag_mem32);
@@ -91,13 +91,14 @@ __tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
 static int __init parse_tag_ramdisk(const struct tag *tag)
 {
 	extern int rd_size, rd_image_start, rd_prompt, rd_doload;
+	unsigned int flags = atag32_to_cpu(tag->u.ramdisk.flags);
 
-	rd_image_start = tag->u.ramdisk.start;
-	rd_doload = (tag->u.ramdisk.flags & 1) == 0;
-	rd_prompt = (tag->u.ramdisk.flags & 2) == 0;
+	rd_image_start = atag32_to_cpu(tag->u.ramdisk.start);
+	rd_doload = (flags & 1) == 0;
+	rd_prompt = (flags & 2) == 0;
 
-	if (tag->u.ramdisk.size)
-		rd_size = tag->u.ramdisk.size;
+	if (atag32_to_cpu(tag->u.ramdisk.size))
+		rd_size = atag32_to_cpu(tag->u.ramdisk.size);
 
 	return 0;
 }
@@ -107,8 +108,8 @@ __tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
 
 static int __init parse_tag_serialnr(const struct tag *tag)
 {
-	system_serial_low = tag->u.serialnr.low;
-	system_serial_high = tag->u.serialnr.high;
+	system_serial_low = atag32_to_cpu(tag->u.serialnr.low);
+	system_serial_high = atag32_to_cpu(tag->u.serialnr.high);
 	return 0;
 }
 
@@ -116,7 +117,7 @@ __tagtable(ATAG_SERIAL, parse_tag_serialnr);
 
 static int __init parse_tag_revision(const struct tag *tag)
 {
-	system_rev = tag->u.revision.rev;
+	system_rev = atag32_to_cpu(tag->u.revision.rev);
 	return 0;
 }
 
@@ -150,7 +151,7 @@ static int __init parse_tag(const struct tag *tag)
 	struct tagtable *t;
 
 	for (t = &__tagtable_begin; t < &__tagtable_end; t++)
-		if (tag->hdr.tag == t->tag) {
+		if (atag32_to_cpu(tag->hdr.tag) == t->tag) {
 			t->parse(tag);
 			break;
 		}
@@ -164,17 +165,17 @@ static int __init parse_tag(const struct tag *tag)
  */
 static void __init parse_tags(const struct tag *t)
 {
-	for (; t->hdr.size; t = tag_next(t))
+	for_each_tag(t, t)
 		if (!parse_tag(t))
 			printk(KERN_WARNING
 				"Ignoring unrecognised tag 0x%08x\n",
-				t->hdr.tag);
+				atag32_to_cpu(t->hdr.tag));
 }
 
 static void __init squash_mem_tags(struct tag *tag)
 {
-	for (; tag->hdr.size; tag = tag_next(tag))
-		if (tag->hdr.tag == ATAG_MEM)
+	for_each_tag(tag, tag)
+		if (tag->hdr.tag == atag32_to_cpu(ATAG_MEM))
 			tag->hdr.tag = ATAG_NONE;
 }
 
@@ -213,10 +214,10 @@ struct machine_desc * __init setup_machine_tags(phys_addr_t __atags_pointer,
 	 * If we have the old style parameters, convert them to
 	 * a tag list.
 	 */
-	if (tags->hdr.tag != ATAG_CORE)
+	if (tags->hdr.tag != atag32_to_cpu(ATAG_CORE))
 		convert_to_tag_list(tags);
 #endif
-	if (tags->hdr.tag != ATAG_CORE) {
+	if (tags->hdr.tag != atag32_to_cpu(ATAG_CORE)) {
 		early_print("Warning: Neither atags nor dtb found\n");
 		tags = (struct tag *)&default_tags;
 	}
@@ -224,7 +225,7 @@ struct machine_desc * __init setup_machine_tags(phys_addr_t __atags_pointer,
 	if (mdesc->fixup)
 		mdesc->fixup(tags, &from, &meminfo);
 
-	if (tags->hdr.tag == ATAG_CORE) {
+	if (tags->hdr.tag == cpu_to_atag32(ATAG_CORE)) {
 		if (meminfo.nr_banks != 0)
 			squash_mem_tags(tags);
 		save_atags(tags);
arch/arm/kernel/atags_proc.c +3 -3
index c7ff807..9080b43 100644
@@ -40,18 +40,18 @@ static int __init init_atags_procfs(void)
 	struct buffer *b;
 	size_t size;
 
-	if (tag->hdr.tag != ATAG_CORE) {
+	if (tag->hdr.tag != cpu_to_atag32(ATAG_CORE)) {
 		printk(KERN_INFO "No ATAGs?");
 		return -EINVAL;
 	}
 
-	for (; tag->hdr.size; tag = tag_next(tag))
+	for_each_tag(tag, tag)
 		;
 
 	/* include the terminating ATAG_NONE */
 	size = (char *)tag - atags_copy + sizeof(struct tag_header);
 
-	WARN_ON(tag->hdr.tag != ATAG_NONE);
+	WARN_ON(tag->hdr.tag != cpu_to_atag32(ATAG_NONE));
 
 	b = kmalloc(sizeof(*b) + size, GFP_KERNEL);
 	if (!b)
Signature · 1 line
1.7.10.4
author
#8 lore raw

PATCH 6/6 ARM: ensure loader information in LE format for BE kernels

In reply to Ben Dooks

From: Nico Pitre <[email protected]>

If the loader for the system is assuming we are booting a BE kernel from a LE loading environment, then ensure that the magic number and image start/end values are in little endian format.

[ben.dooks at codethink.co.uk: from nico's original email on this subject]
---
 arch/arm/boot/compressed/Makefile       |    5 ++++-
 arch/arm/boot/compressed/head.S         |    7 ++++---
 arch/arm/boot/compressed/vmlinux.lds.in |   14 ++++++++++++++
 3 files changed, 22 insertions(+), 4 deletions(-)
arch/arm/boot/compressed/Makefile +4 -1
index 79e9bdb..633391c 100644
@@ -194,8 +194,11 @@ CFLAGS_font.o := -Dstatic=
 $(obj)/font.c: $(FONTC)
 	$(call cmd,shipped)
 
-$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile $(KCONFIG_CONFIG)
+$(obj)/vmlinux.lds.S: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile $(KCONFIG_CONFIG)
 	@sed "$(SEDFLAGS)" < $< > $@
 
+$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.S
+	$(call if_changed,cpp_lds_S)
+
 $(obj)/hyp-stub.S: $(srctree)/arch/$(SRCARCH)/kernel/hyp-stub.S
 	$(call cmd,shipped)
arch/arm/boot/compressed/head.S +4 -3
index 032a8d9..1b9195c 100644
@@ -130,9 +130,10 @@ start:
  THUMB(		adr	r12, BSYM(1f)	)
  THUMB(		bx	r12		)
 
-		.word	0x016f2818		@ Magic numbers to help the loader
-		.word	start			@ absolute load/run zImage address
-		.word	_edata			@ zImage end address
+		.word	_magic_sig	@ Magic numbers to help the loader
+		.word	_magic_start	@ absolute load/run zImage address
+		.word	_magic_end	@ zImage end address
+
  THUMB(		.thumb			)
 1:
 		mrs	r9, cpsr
arch/arm/boot/compressed/vmlinux.lds.in +14 -0
index 4919f2a..567250f 100644
@@ -7,6 +7,16 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
+#ifdef CONFIG_CPU_BE8_BOOT_LE
+#define ZIMAGE_MAGIC(x) ( (((x) >> 24) & 0x000000ff) | \
+			  (((x) >>  8) & 0x0000ff00) | \
+			  (((x) <<  8) & 0x00ff0000) | \
+			  (((x) << 24) & 0xff000000) )
+#else
+#define ZIMAGE_MAGIC(x) (x)
+#endif
+
 OUTPUT_ARCH(arm)
 ENTRY(_start)
 SECTIONS
@@ -57,6 +67,10 @@ SECTIONS
   .pad			: { BYTE(0); . = ALIGN(8); }
   _edata = .;
 
+  _magic_sig = ZIMAGE_MAGIC(0x016f2818);
+  _magic_start = ZIMAGE_MAGIC(_start);
+  _magic_end = ZIMAGE_MAGIC(_edata);
+
   . = BSS_START;
   __bss_start = .;
   .bss			: { *(.bss) }
Signature · 1 line
1.7.10.4
author
#9 lore raw
In reply to Ben Dooks
Quoted 3 lines · Ben Dooks

Add a series of patches to update the system to support little-endian boot loaders with big-endian kernels. Mostly fixes for ATAGS parsing and loader magic values.

Russell, would you be prepared to take this series?

Signature · 2 lines
Ben Dooks				http://www.codethink.co.uk/
Senior Engineer				Codethink - Providing Genius