summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kexec/arch/ppc/Makefile1
-rw-r--r--kexec/arch/ppc/fixup_dtb.c105
-rw-r--r--kexec/arch/ppc/fixup_dtb.h6
-rw-r--r--kexec/arch/ppc/kexec-elf-ppc.c29
4 files changed, 137 insertions, 4 deletions
diff --git a/kexec/arch/ppc/Makefile b/kexec/arch/ppc/Makefile
index 0ef7ca4..5e01237 100644
--- a/kexec/arch/ppc/Makefile
+++ b/kexec/arch/ppc/Makefile
@@ -9,6 +9,7 @@ ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-rel-ppc.c
ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-dol-ppc.c
ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-simple.S
ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-dol.S
+ppc_KEXEC_SRCS += kexec/arch/ppc/fixup_dtb.c
libfdt_SRCS = kexec/arch/ppc/libfdt-wrapper.c
libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/arch/ppc/libfdt/%)
diff --git a/kexec/arch/ppc/fixup_dtb.c b/kexec/arch/ppc/fixup_dtb.c
new file mode 100644
index 0000000..40e9350
--- /dev/null
+++ b/kexec/arch/ppc/fixup_dtb.c
@@ -0,0 +1,105 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "../../kexec.h"
+#include <libfdt.h>
+#include "ops.h"
+#include "page.h"
+#include "fixup_dtb.h"
+
+const char proc_dts[] = "/proc/device-tree";
+
+static void fixup_nodes(char *nodes[])
+{
+ int index = 0;
+ char *fname;
+ char *prop_name;
+ char *node_name;
+ void *node;
+ int len;
+ char *content;
+ off_t content_size;
+ int ret;
+
+ while (nodes[index]) {
+
+ len = asprintf(&fname, "%s%s", proc_dts, nodes[index]);
+ if (len < 0)
+ fatal("asprintf() failed\n");
+
+ content = slurp_file(fname, &content_size);
+ if (!content) {
+ fprintf(stderr, "Can't open %s: %s\n",
+ fname, strerror(errno));
+ exit(1);
+ }
+
+ prop_name = fname + len;
+ while (*prop_name != '/')
+ prop_name--;
+
+ *prop_name = '\0';
+ prop_name++;
+
+ node_name = fname + sizeof(proc_dts) - 1;
+
+ node = finddevice(node_name);
+ if (!node)
+ node = create_node(NULL, node_name + 1);
+
+ ret = setprop(node, prop_name, content, content_size);
+ if (ret < 0)
+ fatal("setprop of %s/%s size: %ld failed: %s\n",
+ node_name, prop_name, content_size,
+ fdt_strerror(ret));
+
+ free(content);
+ free(fname);
+ index++;
+ };
+}
+
+/*
+ * command line priority:
+ * - use the supplied command line
+ * - if none available use the command line from .dtb
+ * - if not available use the current command line
+ */
+static void fixup_cmdline(const char *cmdline)
+{
+ void *chosen;
+ char *fixup_cmd_node[] = {
+ "/chosen/bootargs",
+ NULL,
+ };
+
+ chosen = finddevice("/chosen");
+
+ if (!cmdline) {
+ if (!chosen)
+ fixup_nodes(fixup_cmd_node);
+ } else {
+ if (!chosen)
+ chosen = create_node(NULL, "chosen");
+ setprop_str(chosen, "bootargs", cmdline);
+ }
+ return;
+}
+
+char *fixup_dtb_nodes(char *blob_buf, off_t *blob_size, char *nodes[], char *cmdline)
+{
+ fdt_init(blob_buf);
+
+ fixup_nodes(nodes);
+ fixup_cmdline(cmdline);
+
+ blob_buf = (char *)dt_ops.finalize();
+ *blob_size = fdt_totalsize(blob_buf);
+ return blob_buf;
+}
diff --git a/kexec/arch/ppc/fixup_dtb.h b/kexec/arch/ppc/fixup_dtb.h
new file mode 100644
index 0000000..0ff981e
--- /dev/null
+++ b/kexec/arch/ppc/fixup_dtb.h
@@ -0,0 +1,6 @@
+#ifndef __FIXUP_DTB_H
+#define __FIXUP_DTB_H
+
+char *fixup_dtb_nodes(char *blob_buf, off_t *blob_size, char *nodes[], char *cmdline);
+
+#endif
diff --git a/kexec/arch/ppc/kexec-elf-ppc.c b/kexec/arch/ppc/kexec-elf-ppc.c
index 03b8a94..a54a5d5 100644
--- a/kexec/arch/ppc/kexec-elf-ppc.c
+++ b/kexec/arch/ppc/kexec-elf-ppc.c
@@ -25,6 +25,7 @@
#include <arch/options.h>
#include "config.h"
+#include "fixup_dtb.h"
static const int probe_debug = 0;
@@ -115,12 +116,14 @@ static void gamecube_hack_addresses(struct mem_ehdr *ehdr)
#define OPT_APPEND (OPT_ARCH_MAX+0)
#define OPT_GAMECUBE (OPT_ARCH_MAX+1)
#define OPT_DTB (OPT_ARCH_MAX+2)
+#define OPT_NODES (OPT_ARCH_MAX+3)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{"command-line", 1, 0, OPT_APPEND},
{"append", 1, 0, OPT_APPEND},
{"gamecube", 1, 0, OPT_GAMECUBE},
{"dtb", 1, 0, OPT_DTB},
+ {"reuse-node", 1, 0, OPT_NODES},
{0, 0, 0, 0},
};
static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
@@ -132,7 +135,9 @@ void elf_ppc_usage(void)
" --append=STRING Set the kernel command line to STRING.\n"
" --gamecube=1|0 Enable/disable support for ELFs with changed\n"
" addresses suitable for the GameCube.\n"
- " --dtb=<filename> Specify device tree blob file.\n"
+ " --dtb=<filename> Specify device tree blob file.\n"
+ " --reuse-node=node Specify nodes which should be taken from /proc/device-tree.\n"
+ " Can be set multiple times.\n"
);
}
@@ -157,6 +162,9 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
int target_is_gamecube = 0;
unsigned int addr;
unsigned long dtb_addr;
+#define FIXUP_ENTRYS (20)
+ char *fixup_nodes[FIXUP_ENTRYS + 1];
+ int cur_fixup = 0;
#endif
int opt;
@@ -182,13 +190,25 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
case OPT_DTB:
dtb = optarg;
break;
+
+ case OPT_NODES:
+ if (cur_fixup >= FIXUP_ENTRYS) {
+ fprintf(stderr, "The number of entries for the fixup is too large\n");
+ exit(1);
+ }
+ fixup_nodes[cur_fixup] = optarg;
+ cur_fixup++;
+ break;
}
}
+
command_line_len = 0;
if (command_line) {
command_line_len = strlen(command_line) + 1;
}
+ fixup_nodes[cur_fixup] = NULL;
+
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, &ehdr, 0);
if (result < 0) {
@@ -247,12 +267,13 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
/* Grab device tree from buffer */
blob_buf = slurp_file(dtb, &blob_size);
+ if (!blob_buf || !blob_size)
+ die("Device tree seems to be an empty file.\n");
+ blob_buf = fixup_dtb_nodes(blob_buf, &blob_size, fixup_nodes, command_line);
dtb_addr = add_buffer(info, blob_buf, blob_size, blob_size, 0, 0,
KERNEL_ACCESS_TOP, -1);
- if (command_line)
- die("Don't consider command line because dtb is supplied\n");
} else {
- die("Missing dtb.\n");
+ dtb_addr = 0;
}
/* set various variables for the purgatory */