ARM: zImage: allow supplementing appended DTB with traditional ATAG data
authorNicolas Pitre <nicolas.pitre@linaro.org>
Wed, 14 Sep 2011 02:37:07 +0000 (22:37 -0400)
committerNicolas Pitre <nico@fluxnic.net>
Wed, 14 Sep 2011 17:51:59 +0000 (13:51 -0400)
Some old bootloaders can't be updated to a device tree capable one,
yet they provide ATAGs with memory configuration, the ramdisk address,
the kernel cmdline string, etc.  To allow a device tree enabled
kernel to be used with such bootloaders, it is necessary to convert those
ATAGs into FDT properties and fold them into the DTB appended to zImage.

Currently the following ATAGs are converted:

ATAG_CMDLINE
ATAG_MEM
ATAG_INITRD2

If the corresponding information already exists in the appended DTB, it
is replaced, otherwise the required node is created to hold it.

The code looks for ATAGs at the location pointed by the value of r2 upon
entry into the zImage code.  If no ATAGs are found there, an attempt at
finding ATAGs at the typical 0x100 offset from start of RAM is made.
Otherwise the DTB is left unchanged.

Thisstarted from an older patch from John Bonesio <bones@secretlab.ca>,
with contributions from David Brown <davidb@codeaurora.org>.

Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
Tested-by: Dave Martin <dave.martin@linaro.org>
Tested-by: Thomas Abraham <thomas.abraham@linaro.org>
arch/arm/Kconfig
arch/arm/boot/compressed/.gitignore
arch/arm/boot/compressed/Makefile
arch/arm/boot/compressed/atags_to_fdt.c [new file with mode: 0644]
arch/arm/boot/compressed/head.S
arch/arm/boot/compressed/libfdt_env.h [new file with mode: 0644]

index c66e0808c2b1bca785f8ee3150d66883500a62b1..73c320ea172c3a8b093af54cd84a937a6b054692 100644 (file)
@@ -1801,6 +1801,18 @@ config ARM_APPENDED_DTB
          location into r2 of a bootloader provided DTB is always preferable
          to this option.
 
+config ARM_ATAG_DTB_COMPAT
+       bool "Supplement the appended DTB with traditional ATAG information"
+       depends on ARM_APPENDED_DTB
+       help
+         Some old bootloaders can't be updated to a DTB capable one, yet
+         they provide ATAGs with memory configuration, the ramdisk address,
+         the kernel cmdline string, etc.  Such information is dynamically
+         provided by the bootloader and can't always be stored in a static
+         DTB.  To allow a device tree enabled kernel to be used with such
+         bootloaders, this option allows zImage to extract the information
+         from the ATAG list and store it at run time into the appended DTB.
+
 config CMDLINE
        string "Default kernel command string"
        default ""
index c6028967d33676215841e056f8b4af7f4832f3b6..e0936a148516e2c5005d3620065938b4acec8b11 100644 (file)
@@ -5,3 +5,12 @@ piggy.lzo
 piggy.lzma
 vmlinux
 vmlinux.lds
+
+# borrowed libfdt files
+fdt.c
+fdt.h
+fdt_ro.c
+fdt_rw.c
+fdt_wip.c
+libfdt.h
+libfdt_internal.h
index c20ddc69d95014caff6f441f9712080813791340..55f86349d5471375bc9f2942efa01e7aaf9f27ef 100644 (file)
@@ -93,19 +93,36 @@ suffix_$(CONFIG_KERNEL_GZIP) = gzip
 suffix_$(CONFIG_KERNEL_LZO)  = lzo
 suffix_$(CONFIG_KERNEL_LZMA) = lzma
 
+# Borrowed libfdt files for the ATAG compatibility mode
+
+libfdt         := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c
+libfdt_hdrs    := fdt.h libfdt.h libfdt_internal.h
+
+libfdt_objs    := $(addsuffix .o, $(basename $(libfdt)))
+
+$(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/libfdt/%
+       $(call cmd,shipped)
+
+$(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
+       $(addprefix $(obj)/,$(libfdt_hdrs))
+
+ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
+OBJS   += $(libfdt_objs) atags_to_fdt.o
+endif
+
 targets       := vmlinux vmlinux.lds \
                 piggy.$(suffix_y) piggy.$(suffix_y).o \
                 font.o font.c head.o misc.o $(OBJS)
 
 # Make sure files are removed during clean
-extra-y       += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S
+extra-y       += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S $(libfdt) $(libfdt_hdrs)
 
 ifeq ($(CONFIG_FUNCTION_TRACER),y)
 ORIG_CFLAGS := $(KBUILD_CFLAGS)
 KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
 endif
 
-ccflags-y := -fpic -fno-builtin
+ccflags-y := -fpic -fno-builtin -I$(obj)
 asflags-y := -Wa,-march=all
 
 # Supply kernel BSS size to the decompressor via a linker symbol.
diff --git a/arch/arm/boot/compressed/atags_to_fdt.c b/arch/arm/boot/compressed/atags_to_fdt.c
new file mode 100644 (file)
index 0000000..6ce11c4
--- /dev/null
@@ -0,0 +1,97 @@
+#include <asm/setup.h>
+#include <libfdt.h>
+
+static int node_offset(void *fdt, const char *node_path)
+{
+       int offset = fdt_path_offset(fdt, node_path);
+       if (offset == -FDT_ERR_NOTFOUND)
+               offset = fdt_add_subnode(fdt, 0, node_path);
+       return offset;
+}
+
+static int setprop(void *fdt, const char *node_path, const char *property,
+                  uint32_t *val_array, int size)
+{
+       int offset = node_offset(fdt, node_path);
+       if (offset < 0)
+               return offset;
+       return fdt_setprop(fdt, offset, property, val_array, size);
+}
+
+static int setprop_string(void *fdt, const char *node_path,
+                         const char *property, const char *string)
+{
+       int offset = node_offset(fdt, node_path);
+       if (offset < 0)
+               return offset;
+       return fdt_setprop_string(fdt, offset, property, string);
+}
+
+static int setprop_cell(void *fdt, const char *node_path,
+                       const char *property, uint32_t val)
+{
+       int offset = node_offset(fdt, node_path);
+       if (offset < 0)
+               return offset;
+       return fdt_setprop_cell(fdt, offset, property, val);
+}
+
+/*
+ * Convert and fold provided ATAGs into the provided FDT.
+ *
+ * REturn values:
+ *    = 0 -> pretend success
+ *    = 1 -> bad ATAG (may retry with another possible ATAG pointer)
+ *    < 0 -> error from libfdt
+ */
+int atags_to_fdt(void *atag_list, void *fdt, int total_space)
+{
+       struct tag *atag = atag_list;
+       uint32_t mem_reg_property[2 * NR_BANKS];
+       int memcount = 0;
+       int ret;
+
+       /* make sure we've got an aligned pointer */
+       if ((u32)atag_list & 0x3)
+               return 1;
+
+       /* if we get a DTB here we're done already */
+       if (*(u32 *)atag_list == fdt32_to_cpu(FDT_MAGIC))
+              return 0;
+
+       /* validate the ATAG */
+       if (atag->hdr.tag != ATAG_CORE ||
+           (atag->hdr.size != tag_size(tag_core) &&
+            atag->hdr.size != 2))
+               return 1;
+
+       /* let's give it all the room it could need */
+       ret = fdt_open_into(fdt, fdt, total_space);
+       if (ret < 0)
+               return ret;
+
+       for_each_tag(atag, atag_list) {
+               if (atag->hdr.tag == ATAG_CMDLINE) {
+                       setprop_string(fdt, "/chosen", "bootargs",
+                                       atag->u.cmdline.cmdline);
+               } else if (atag->hdr.tag == ATAG_MEM) {
+                       if (memcount >= sizeof(mem_reg_property)/4)
+                               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) {
+                       uint32_t initrd_start, initrd_size;
+                       initrd_start = atag->u.initrd.start;
+                       initrd_size = atag->u.initrd.size;
+                       setprop_cell(fdt, "/chosen", "linux,initrd-start",
+                                       initrd_start);
+                       setprop_cell(fdt, "/chosen", "linux,initrd-end",
+                                       initrd_start + initrd_size);
+               }
+       }
+
+       if (memcount)
+               setprop(fdt, "/memory", "reg", mem_reg_property, 4*memcount);
+
+       return fdt_pack(fdt);
+}
index ba5c552f8c69cfe45ecc3fb38805200556b47d05..9f5ac11ccd8ef73e41d82d592a90cb3672891235 100644 (file)
@@ -246,6 +246,38 @@ restart:   adr     r0, LC0
                cmp     lr, r1
                bne     dtb_check_done          @ not found
 
+#ifdef CONFIG_ARM_ATAG_DTB_COMPAT
+               /*
+                * OK... Let's do some funky business here.
+                * If we do have a DTB appended to zImage, and we do have
+                * an ATAG list around, we want the later to be translated
+                * and folded into the former here.  To be on the safe side,
+                * let's temporarily move  the stack away into the malloc
+                * area.  No GOT fixup has occurred yet, but none of the
+                * code we're about to call uses any global variable.
+               */
+               add     sp, sp, #0x10000
+               stmfd   sp!, {r0-r3, ip, lr}
+               mov     r0, r8
+               mov     r1, r6
+               sub     r2, sp, r6
+               bl      atags_to_fdt
+
+               /*
+                * If returned value is 1, there is no ATAG at the location
+                * pointed by r8.  Try the typical 0x100 offset from start
+                * of RAM and hope for the best.
+                */
+               cmp     r0, #1
+               sub     r0, r4, #(TEXT_OFFSET - 0x100)
+               mov     r1, r6
+               sub     r2, sp, r6
+               blne    atags_to_fdt
+
+               ldmfd   sp!, {r0-r3, ip, lr}
+               sub     sp, sp, #0x10000
+#endif
+
                mov     r8, r6                  @ use the appended device tree
 
                /*
diff --git a/arch/arm/boot/compressed/libfdt_env.h b/arch/arm/boot/compressed/libfdt_env.h
new file mode 100644 (file)
index 0000000..1f4e718
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _ARM_LIBFDT_ENV_H
+#define _ARM_LIBFDT_ENV_H
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <asm/byteorder.h>
+
+#define fdt16_to_cpu(x)                be16_to_cpu(x)
+#define cpu_to_fdt16(x)                cpu_to_be16(x)
+#define fdt32_to_cpu(x)                be32_to_cpu(x)
+#define cpu_to_fdt32(x)                cpu_to_be32(x)
+#define fdt64_to_cpu(x)                be64_to_cpu(x)
+#define cpu_to_fdt64(x)                cpu_to_be64(x)
+
+#endif