diff options
-rw-r--r-- | kexec/arch/ppc/Makefile | 1 | ||||
-rw-r--r-- | kexec/arch/ppc/fixup_dtb.c | 105 | ||||
-rw-r--r-- | kexec/arch/ppc/fixup_dtb.h | 6 | ||||
-rw-r--r-- | kexec/arch/ppc/kexec-elf-ppc.c | 29 |
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 */ |