summaryrefslogtreecommitdiff
path: root/kexec/arch/arm/kexec-zImage-arm.c
diff options
context:
space:
mode:
authorMatthew Leach <matthew.leach@arm.com>2012-09-07 10:11:26 +0100
committerSimon Horman <horms@verge.net.au>2012-09-12 09:32:34 +0900
commitf0839230276d32a67b5a4a65e199c1937adb3643 (patch)
tree778edaa15d14ca1cd3e626c3010f969b7f8d1360 /kexec/arch/arm/kexec-zImage-arm.c
parent7f4d00a19a56a3b9ffd0b793ae31082ad926b68c (diff)
ARM: Add device tree support to the ARM platform
To allow newer ARM platforms to use kexec, pass device tree information to the kernel during boot. By default the dtb is found from /proc/device-tree. A user can specify a dtb file or use legacy ATAGs Signed-off-by: Matthew Leach <matthew.leach@arm.com> Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'kexec/arch/arm/kexec-zImage-arm.c')
-rw-r--r--kexec/arch/arm/kexec-zImage-arm.c87
1 files changed, 78 insertions, 9 deletions
diff --git a/kexec/arch/arm/kexec-zImage-arm.c b/kexec/arch/arm/kexec-zImage-arm.c
index 88a6c29..46cedbf 100644
--- a/kexec/arch/arm/kexec-zImage-arm.c
+++ b/kexec/arch/arm/kexec-zImage-arm.c
@@ -13,13 +13,18 @@
#include <unistd.h>
#include <getopt.h>
#include <unistd.h>
+#include <libfdt.h>
#include <arch/options.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
+#include "kexec-arm.h"
+#include "../../fs2dt.h"
#include "crashdump-arm.h"
#define BOOT_PARAMS_SIZE 1536
+off_t initrd_base = 0, initrd_size = 0;
+
struct tag_header {
uint32_t size;
uint32_t tag;
@@ -96,6 +101,8 @@ void zImage_arm_usage(void)
" --append=STRING Set the kernel command line to STRING.\n"
" --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
" --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --dtb=FILE Use FILE as the fdt blob.\n"
+ " --atags Use ATAGs instead of device-tree.\n"
);
}
@@ -219,9 +226,13 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
off_t command_line_len;
const char *ramdisk;
char *ramdisk_buf;
- off_t ramdisk_length;
- off_t ramdisk_offset;
int opt;
+ int use_atags;
+ char *dtb_buf;
+ off_t dtb_length;
+ char *dtb_file;
+ off_t dtb_offset;
+
/* See options.h -- add any more there, too. */
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
@@ -229,6 +240,8 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
{ "append", 1, 0, OPT_APPEND },
{ "initrd", 1, 0, OPT_RAMDISK },
{ "ramdisk", 1, 0, OPT_RAMDISK },
+ { "dtb", 1, 0, OPT_DTB },
+ { "atags", 0, 0, OPT_ATAGS },
{ 0, 0, 0, 0 },
};
static const char short_options[] = KEXEC_ARCH_OPT_STR "a:r:";
@@ -240,7 +253,9 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
command_line_len = 0;
ramdisk = 0;
ramdisk_buf = 0;
- ramdisk_length = 0;
+ initrd_size = 0;
+ use_atags = 0;
+ dtb_file = NULL;
while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch(opt) {
default:
@@ -257,15 +272,28 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
case OPT_RAMDISK:
ramdisk = optarg;
break;
+ case OPT_DTB:
+ dtb_file = optarg;
+ break;
+ case OPT_ATAGS:
+ use_atags = 1;
+ break;
}
}
+
+ if (use_atags && dtb_file) {
+ fprintf(stderr, "You can only use ATAGs if you don't specify a "
+ "dtb file.\n");
+ return -1;
+ }
+
if (command_line) {
command_line_len = strlen(command_line) + 1;
if (command_line_len > COMMAND_LINE_SIZE)
command_line_len = COMMAND_LINE_SIZE;
}
if (ramdisk) {
- ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
+ ramdisk_buf = slurp_file(ramdisk, &initrd_size);
}
/*
@@ -315,12 +343,53 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
/* assume the maximum kernel compression ratio is 4,
* and just to be safe, place ramdisk after that
*/
- ramdisk_offset = base + len * 4;
+ initrd_base = base + len * 4;
- if (atag_arm_load(info, base + atag_offset,
- command_line, command_line_len,
- ramdisk_buf, ramdisk_length, ramdisk_offset) == -1)
- return -1;
+ if (use_atags) {
+ /*
+ * use ATAGs from /proc/atags
+ */
+ if (atag_arm_load(info, base + atag_offset,
+ command_line, command_line_len,
+ ramdisk_buf, initrd_size, initrd_base) == -1)
+ return -1;
+ } else {
+ /*
+ * Read a user-specified DTB file.
+ */
+ if (dtb_file) {
+ dtb_buf = slurp_file(dtb_file, &dtb_length);
+ } else {
+ /*
+ * Extract the DTB from /proc/device-tree.
+ */
+ create_flatten_tree(&dtb_buf, &dtb_length, command_line);
+ }
+
+ if (fdt_check_header(dtb_buf) != 0) {
+ fprintf(stderr, "Invalid FDT buffer.\n");
+ return -1;
+ }
+
+ if (base + atag_offset + dtb_length > base + offset) {
+ fprintf(stderr, "DTB too large!\n");
+ return -1;
+ }
+
+ if (ramdisk) {
+ add_segment(info, ramdisk_buf, initrd_size,
+ initrd_base, initrd_size);
+ }
+
+ /* Stick the dtb at the end of the initrd and page
+ * align it.
+ */
+ dtb_offset = initrd_base + initrd_size + getpagesize();
+ dtb_offset &= ~(getpagesize() - 1);
+
+ add_segment(info, dtb_buf, dtb_length,
+ dtb_offset, dtb_length);
+ }
add_segment(info, buf, len, base + offset, len);