kexec-tools for ppc64 - RFC
R Sharada
sharada at in.ibm.com
Tue Aug 9 01:23:48 EST 2005
Hello,
Finally, here is a first draft of the kexec-tools support for ppc64.
This has been tested on Power4 p630 machine, in both lpar and non-lpar
environments, with kexec-tools-1.101 and linux kernel version 2.6.13-rc5.
Current Status
---------------
kexec-tools can now be used to kexec load and reboot into a
second kernel. This integration uses the old tools
primarily written by Milton Miller and enables the old tools
to work from within the kexec-tools package.
- fs2dt which is the device-tree flattening code is still
retained as a separate source file, but converted into a
function call, which is called from within kexec-tools code.
- v2wrap is still retained as a separate assembly stub and
is being compiled and built from within the
kexec/arch/ppc64 directory.
- v2wrap + flat device-tree is still loaded as a single
segment.
- default compilation is 64-bit.
Current Limitations
--------------------
- User has to supply the load addresses (or range in which
the load address is to start at) for 2 segments that are
currently supported - vmlinux, and v2wrap.
This is because of a limitation in the amount of data
currently available from get_memory_ranges on ppc64.
get_memory_ranges currently reads the memory range from
/proc/device-tree which does not provide information
about the memory ranges occupied by the hash page tables
(in non-lpar case), as well as the rmo boundary for both
lpar and non-lpar.
- Hackish way to find the device-tree load address
(by calling locate_hole in advance) to fill up the
reserve_mem address for device-tree.
- initrd loading is currently not supported.
Work in Progress
-----------------
- Clean up build to put v2wrap binary into objdir
- Fix and clean up the get_memory_range support in ppc64
so that we do not have to have the user to supply load
addresses for the segments.
- Adding support to load initrd image.
ToDo
-----
- Integrate v2wrap to be built from within purgatory
Example Usage
--------------
A sample load call would be:
kexec -l vmlinux --v2wrap=v2wrap --append="v2wrap-base=0x5000000 vmlinux-base=0x6000000"
To kexec reboot:
kexec -e
ps: I still seem to have to do an occasional ifdown on the
network interface prior to a kexec -e
This however still needs lots of testing and cleanups some of which will be
posted shortly.
Kindly review / test and provide comments.
Thanks and Regards,
Sharada
---
kexec-tools-1.101-sharada/Makefile | 4
kexec-tools-1.101-sharada/configure | 7
kexec-tools-1.101-sharada/kexec/arch/ppc64/Makefile | 5
kexec-tools-1.101-sharada/kexec/arch/ppc64/fs2dt.c | 314 ++++++++++
kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-elf-ppc64.c | 201 +++++-
kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-elf-rel-ppc64.c | 2
kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-ppc64.c | 168 +++++
kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-ppc64.h | 6
kexec-tools-1.101-sharada/kexec/arch/ppc64/make_v2wrap | 13
kexec-tools-1.101-sharada/kexec/arch/ppc64/v2wrap.S | 114 +++
kexec-tools-1.101-sharada/purgatory/Makefile | 5
11 files changed, 799 insertions(+), 40 deletions(-)
diff -puN configure~kexec-tools-ppc64 configure
--- kexec-tools-1.101/configure~kexec-tools-ppc64 2005-08-08 20:26:12.000000000 +0530
+++ kexec-tools-1.101-sharada/configure 2005-08-08 20:27:19.000000000 +0530
@@ -1384,6 +1384,9 @@ case $host_cpu in
powerpc )
host_cpu="ppc"
;;
+ powerpc64 )
+ host_cpu="ppc64"
+ ;;
* )
host_cpu="$host_cpu"
;;
@@ -1421,6 +1424,10 @@ if test "${with_gamecube+set}" = set; th
EXTRA_CFLAGS="$EXTRA_CFLAGS -DCONFIG_GAMECUBE=1"
fi;
+# Check whether ppc64. Add -m64 for building 64-bit binary
+if test "$ARCH"=ppc64; then
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -m64"
+fi;
# Check whether --with-zlib or --without-zlib was given.
if test "${with_zlib+set}" = set; then
diff -puN Makefile~kexec-tools-ppc64 Makefile
--- kexec-tools-1.101/Makefile~kexec-tools-ppc64 2005-08-08 20:26:12.000000000 +0530
+++ kexec-tools-1.101-sharada/Makefile 2005-08-08 20:35:13.000000000 +0530
@@ -89,6 +89,10 @@ endif
ifeq ($(ARCH),x86_64)
include kexec_test/Makefile
endif
+ifeq ($(ARCH),ppc64)
+include kexec/arch/ppc64/make_v2wrap
+endif
+
GENERATED_SRCS:= ./configure
SPEC=$(OBJDIR)/$(PACKAGE)-$(VERSION).spec
diff -puN /dev/null kexec/arch/ppc64/fs2dt.c
--- /dev/null 2005-08-08 20:42:13.772546000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/fs2dt.c 2005-08-08 20:39:26.000000000 +0530
@@ -0,0 +1,314 @@
+/*
+ * fs2dt: creates a flattened device-tree
+ *
+ * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation
+ * Copyright (C) 2005 R Sharada (sharada at in.ibm.com), IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "kexec-ppc64.h"
+
+#define MAXPATH 1024 /* max path name length */
+#define NAMESPACE 16384 /* max bytes for property names */
+#define TREEWORDS 65536 /* max 32 bit words for property values */
+#define MEMRESERVE 256 /* max number of reserved memory blocks */
+
+enum {
+ ERR_NONE,
+ ERR_USAGE,
+ ERR_OPENDIR,
+ ERR_READDIR,
+ ERR_STAT,
+ ERR_OPEN,
+ ERR_READ,
+ ERR_RESERVE,
+};
+
+void err(const char *str, int rc)
+{
+ if (errno)
+ perror(str);
+ else
+ fprintf(stderr, "%s: unrecoverable error\n", str);
+ exit(rc);
+}
+
+typedef unsigned dvt;
+struct stat statbuf[1];
+char pathname[MAXPATH], *pathstart;
+char propnames[NAMESPACE];
+dvt dtstruct[TREEWORDS], *dt;
+unsigned long long mem_rsrv[2*MEMRESERVE];
+
+extern unsigned long initrd_base;
+extern unsigned long initrd_size;
+
+void reserve(unsigned long long where, unsigned long long length)
+{
+ unsigned long long *mr;
+
+ mr = mem_rsrv;
+
+ while(mr[1])
+ mr += 2;
+
+ mr[0] = where;
+ mr[1] = length;
+}
+
+/* look for properties we need to reserve memory space for */
+void checkprop(char *name, dvt *data)
+{
+ static unsigned long long base, size, end;
+
+ if ((data == NULL) && (base || size || end))
+ err((void *)data, ERR_RESERVE);
+ else if (!strcmp(name, "linux,rtas-base"))
+ base = *data;
+ else if (!strcmp(name, "linux,initrd-start")) {
+ if (initrd_base)
+ base = initrd_base;
+ else
+ base = *(unsigned long long *) data;
+ }
+ else if (!strcmp(name, "linux,tce-base"))
+ base = *(unsigned long long *) data;
+ else if (!strcmp(name, "rtas-size") ||
+ !strcmp(name, "linux,tce-size"))
+ size = *data;
+ else if (!strcmp(name, "linux,initrd-end")) {
+ if (initrd_size)
+ size = initrd_size;
+ else
+ end = *(unsigned long long *) data;
+ }
+ if (size && end)
+ err(name, ERR_RESERVE);
+ if (base && size) {
+ reserve(base, size);
+ base = size = 0;
+ }
+ if (base && end) {
+ reserve(base, end-base);
+ base = end = 0;
+ }
+}
+
+/*
+ * return the property index for a property name, creating a new one
+ * if needed.
+ */
+dvt propnum(const char *name)
+{
+ dvt offset = 0;
+
+ while(propnames[offset])
+ if (strcmp(name, propnames+offset))
+ offset += strlen(propnames+offset)+1;
+ else
+ return offset;
+
+ strcpy(propnames+offset, name);
+
+ return offset;
+}
+
+/* put all properties (files) in the property structure */
+void putprops(char *fn, DIR *dir)
+{
+ struct dirent *dp;
+
+ while ((dp = readdir(dir)) != NULL) {
+ strcpy(fn, dp->d_name);
+
+ if (lstat(pathname, statbuf))
+ err(pathname, ERR_STAT);
+
+ if (S_ISREG(statbuf[0].st_mode)) {
+ int fd, len = statbuf[0].st_size;
+
+ *dt++ = 3;
+ *dt++ = len;
+ *dt++ = propnum(fn);
+
+ if ((len >= 8) && ((unsigned long)dt & 0x4))
+ dt++;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd == -1)
+ err(pathname, ERR_OPEN);
+ if (read(fd, dt, len) != len)
+ err(pathname, ERR_READ);
+ close(fd);
+
+ checkprop(fn, dt);
+
+ dt += (len + 3)/4;
+ }
+ }
+ fn[0] = '\0';
+ if(errno == ENOSYS)
+ errno = 0;
+ if (errno)
+ err(pathname, ERR_READDIR);
+ checkprop(pathname, NULL);
+}
+
+/*
+ * put a node (directory) in the property structure. first properties
+ * then children.
+ */
+void putnode(void)
+{
+ DIR *dir;
+ char *dn;
+ struct dirent *dp;
+
+ *dt++ = 1;
+ strcpy((void *)dt, *pathstart ? pathstart : "/");
+ while(*dt)
+ dt++;
+ if (dt[-1] & 0xff)
+ dt++;
+
+ dir = opendir(pathname);
+
+ if (!dir)
+ err(pathname, ERR_OPENDIR);
+
+ strcat(pathname, "/");
+ dn = pathname + strlen(pathname);
+
+ putprops(dn, dir);
+
+ rewinddir(dir);
+
+ while ((dp = readdir(dir)) != NULL) {
+ strcpy(dn, dp->d_name);
+
+ if (!strcmp(dn, ".") || !strcmp(dn, ".."))
+ continue;
+
+ if (lstat(pathname, statbuf))
+ err(pathname, ERR_STAT);
+
+ if (S_ISDIR(statbuf[0].st_mode))
+ putnode();
+ }
+ if (errno)
+ err(pathname, ERR_READDIR);
+
+ *dt++ = 2;
+ closedir(dir);
+ dn[-1] = '\0';
+}
+
+/* boot block version 2 as defined by the linux kernel */
+struct bootblock {
+ unsigned magic,
+ totalsize,
+ off_dt_struct,
+ off_dt_strings,
+ off_mem_rsvmap,
+ version,
+ last_comp_version,
+ boot_physid;
+} bb[1];
+
+struct devtree_data {
+ unsigned long load_addr;
+ unsigned long initrd_start;
+ unsigned long initrd_size;
+};
+
+int create_flatten_tree(struct kexec_info *info, unsigned char **bufp, unsigned long *sizep)
+{
+ unsigned long len;
+ unsigned long tlen;
+ unsigned char *buf;
+ unsigned long me;
+ unsigned long base_addr;
+ unsigned long size;
+
+ me = 0;
+ base_addr = 0;
+
+ strcpy(pathname, "/proc/device-tree/");
+
+ pathstart = pathname + strlen(pathname);
+ dt = dtstruct;
+
+ putnode();
+ *dt++ = 9;
+
+ len = sizeof(bb[0]);
+ len += 7; len &= ~7;
+
+ bb->off_mem_rsvmap = len;
+
+ for (len = 1; mem_rsrv[len]; len += 2)
+ ;
+ len+= 3;
+ len *= sizeof(mem_rsrv[0]);
+
+ bb->off_dt_struct = bb->off_mem_rsvmap + len;
+
+ len = dt - dtstruct;
+ len *= sizeof(dvt);
+ bb->off_dt_strings = bb->off_dt_struct + len;
+
+ len = propnum("");
+ len += 3; len &= ~3;
+ bb->totalsize = bb->off_dt_strings + len;
+
+ bb->magic = 0xd00dfeed;
+ bb->version = 2;
+ bb->last_comp_version = 2;
+
+ size = bb->totalsize + 0x100;
+ /* Hacks - should clean this up so that we can set a logical and valid
+ * min and max addr to pass into locate_hole
+ */
+ unsigned long max_addr = v2wrap_base + 0x1000000;
+ base_addr = (unsigned long)locate_hole(info, size, 0, v2wrap_base, max_addr, -1);
+ if (base_addr) {
+ me = base_addr + 0x100;
+ }
+
+ reserve(me, bb->totalsize);
+
+ buf = (unsigned char *) realloc(*bufp, *sizep + bb->totalsize);
+ *bufp = buf;
+ memcpy(buf+(*sizep), bb, bb->off_mem_rsvmap);
+ tlen = *sizep + bb->off_mem_rsvmap;
+ memcpy(buf+tlen, mem_rsrv, bb->off_dt_struct - bb->off_mem_rsvmap);
+ tlen = tlen + (bb->off_dt_struct - bb->off_mem_rsvmap);
+ memcpy(buf+tlen, dtstruct, bb->off_dt_strings - bb->off_dt_struct);
+ tlen = tlen + (bb->off_dt_strings - bb->off_dt_struct);
+ memcpy(buf+tlen, propnames, bb->totalsize - bb->off_dt_strings);
+ tlen = tlen + bb->totalsize - bb->off_dt_strings;
+ *sizep = tlen;
+ return 0;
+}
diff -puN kexec/arch/ppc64/kexec-elf-ppc64.c~kexec-tools-ppc64 kexec/arch/ppc64/kexec-elf-ppc64.c
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-elf-ppc64.c~kexec-tools-ppc64 2005-08-08 20:26:13.000000000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-elf-ppc64.c 2005-08-08 20:39:50.000000000 +0530
@@ -3,6 +3,7 @@
*
* Copyright (C) 2004 Adam Litke (agl at us.ibm.com)
* Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2005 R Sharada (sharada at in.ibm.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,45 +34,14 @@
#include "../../kexec.h"
#include "../../kexec-elf.h"
#include "kexec-ppc64.h"
+#include <arch/options.h>
#define BOOTLOADER "kexec"
#define BOOTLOADER_VERSION VERSION
#define MAX_COMMAND_LINE 256
-#define UPSZ(X) ((sizeof(X) + 3) & ~3)
-static struct boot_notes {
- Elf_Bhdr hdr;
- Elf_Nhdr bl_hdr;
- unsigned char bl_desc[UPSZ(BOOTLOADER)];
- Elf_Nhdr blv_hdr;
- unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)];
- Elf_Nhdr cmd_hdr;
- unsigned char command_line[0];
-} elf_boot_notes = {
- .hdr = {
- .b_signature = 0x0E1FB007,
- .b_size = sizeof(elf_boot_notes),
- .b_checksum = 0,
- .b_records = 3,
- },
- .bl_hdr = {
- .n_namesz = 0,
- .n_descsz = sizeof(BOOTLOADER),
- .n_type = EBN_BOOTLOADER_NAME,
- },
- .bl_desc = BOOTLOADER,
- .blv_hdr = {
- .n_namesz = 0,
- .n_descsz = sizeof(BOOTLOADER_VERSION),
- .n_type = EBN_BOOTLOADER_VERSION,
- },
- .blv_desc = BOOTLOADER_VERSION,
- .cmd_hdr = {
- .n_namesz = 0,
- .n_descsz = 0,
- .n_type = EBN_COMMAND_LINE,
- },
-};
+int create_flatten_tree(struct kexec_info *, unsigned char*, unsigned long *);
+int parse_options(char *);
int elf_ppc64_probe(const char *buf, off_t len)
{
@@ -98,8 +68,67 @@ int elf_ppc64_load(int argc, char **argv
struct kexec_info *info)
{
struct mem_ehdr ehdr;
+ const char *command_line;
+ const char *input_options;
+ int command_line_len;
+ const char *ramdisk;
+ const char *v2wrap;
+ unsigned long *lp;
+ int result;
+ int opt;
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_V2WRAP (OPT_ARCH_MAX+1)
+#define OPT_RAMDISK (OPT_ARCH_MAX+2)
+
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "append", 1, NULL, OPT_APPEND },
+ { "v2wrap", 1, NULL, OPT_V2WRAP },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ initrd_base = 0;
+ initrd_size = 0;
+ v2wrap_base = 0;
+ vmlinux_base = 0;
/* Parse command line arguments */
+ command_line = 0;
+ input_options = 0;
+ ramdisk = 0;
+ v2wrap = 0;
+
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case '?':
+ usage();
+ return -1;
+ case OPT_APPEND:
+ input_options = optarg;
+ break;
+ case OPT_V2WRAP:
+ v2wrap = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ }
+ }
+
+ command_line_len = 0;
+ if (command_line) {
+ command_line_len = strlen(command_line) + 1;
+ }
+
+ parse_options(input_options);
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, &ehdr);
@@ -108,16 +137,116 @@ int elf_ppc64_load(int argc, char **argv
return result;
}
- /* Load the Elf data */
+ /* Load the Elf data. Physical load addresses in elf64 header do not
+ * show up correctly. Use user supplied address for now to patch the
+ * elf header
+ */
+ ehdr.e_phdr[0].p_paddr=vmlinux_base;
result = elf_exec_load(&ehdr, info);
if (result < 0) {
free_elf_info(&ehdr);
return result;
}
- return 1;
+
+ /* Add v2wrap to the current image */
+ if (v2wrap) {
+ unsigned char *v2wrap_buf = NULL;
+ off_t v2wrap_size = 0;
+ unsigned long hole_base = 0;
+ unsigned long max_addr;
+
+ v2wrap_buf = slurp_file(v2wrap, &v2wrap_size);
+ create_flatten_tree(info, &v2wrap_buf, &v2wrap_size);
+ max_addr = v2wrap_base + 0x1000000;
+ add_buffer(info, v2wrap_buf, v2wrap_size, v2wrap_size, 0, v2wrap_base, max_addr, -1);
+ }
+
+ lp = info->segment[1].buf + 0x100;
+ lp--;
+ *lp = info->segment[0].mem;
+ info->entry = info->segment[1].mem;
+ printf("segment[0].mem:%lx, segment[1].mem:"
+ "%lx\n", info->segment[0].mem, info->segment[1].mem);
+
+ /* Add a ram-disk to the current image */
+ if (ramdisk) {
+ /* Not supported at the moment - coming soon */
+ }
+
+ return 0;
}
void elf_ppc64_usage(void)
{
fprintf(stderr, "elf support is still broken\n");
}
+
+struct param_struct {
+ const char *name;
+ void *val;
+};
+struct param_struct params;
+
+static char *next_arg(char *args, char **param, char **val)
+{
+ unsigned int i, equals = 0;
+ char *next;
+
+ /* Chew any extra spaces */
+ while (*args == ' ') args++;
+
+ for (i = 0; args[i]; i++) {
+ if (args[i] == ' ')
+ break;
+ if (equals == 0) {
+ if (args[i] == '=')
+ equals = i;
+ }
+ }
+
+ *param = args;
+ if (!equals)
+ *val = NULL;
+ else {
+ args[equals] = '\0';
+ *val = args + equals + 1;
+ }
+
+ if (args[i]) {
+ args[i] = '\0';
+ next = args + i + 1;
+ } else
+ next = args + i;
+ return next;
+}
+
+static int add_arg(char *param, char*val)
+{
+ int ret = 0;
+ if (strcmp(param, "initrd-base")==0)
+ initrd_base=strtoul(val, NULL, 0);
+ else if (strcmp(param, "initrd-size")==0)
+ initrd_size=strtoul(val, NULL, 0);
+ else if (strcmp(param, "v2wrap-base")==0)
+ v2wrap_base=strtoul(val, NULL, 0);
+ else if (strcmp(param, "vmlinux-base")==0)
+ vmlinux_base=strtoul(val, NULL, 0);
+ else {
+ printf("invalid option\n");
+ ret = 1;
+ }
+ return ret;
+}
+
+int parse_options(char *options)
+{
+ char *param, *val;
+ /* initrd-addr , initrd-size, v2wrap-addr, vmlinux-addr */
+ while (*options) {
+ int ret;
+ options = next_arg(options, ¶m, &val);
+ ret = add_arg(param, val);
+ }
+ /* All parsed OK */
+ return 0;
+}
diff -puN kexec/arch/ppc64/kexec-elf-rel-ppc64.c~kexec-tools-ppc64 kexec/arch/ppc64/kexec-elf-rel-ppc64.c
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-elf-rel-ppc64.c~kexec-tools-ppc64 2005-08-08 20:26:13.000000000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-elf-rel-ppc64.c 2005-08-08 20:38:37.000000000 +0530
@@ -22,7 +22,7 @@ static struct mem_shdr *toc_section(cons
struct mem_shdr *shdr, *shdr_end;
unsigned char *strtab;
strtab = ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
- shdr_end = &ehdr->e_shdr[ehdr->shnum];
+ shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
if (strcmp(shdr->sh_name, ".toc") == 0) {
return shdr;
diff -puN /dev/null kexec/arch/ppc64/kexec-ppc64.c
--- /dev/null 2005-08-08 20:42:13.772546000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-ppc64.c 2005-08-08 20:40:13.000000000 +0530
@@ -0,0 +1,168 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2005 R Sharada (sharada at in.ibm.com), IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <getopt.h>
+#include <sys/utsname.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ppc64.h"
+#include <arch/options.h>
+
+#define MAX_MEMORY_RANGES 64
+#define MAX_LINE 160
+#define MAXBYTES 128
+
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/* Return a sorted list of memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges)
+{
+ int memory_ranges = 0;
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ char buf[MAXBYTES-1];
+ DIR *dir, *dmem;
+ FILE *file;
+ struct dirent *dentry, *mentry;
+ int n;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((dmem = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ return -1;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if (memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ memory_range[memory_ranges].start =
+ ((unsigned long long *)buf)[0];
+ memory_range[memory_ranges].end =
+ memory_range[memory_ranges].start +
+ ((unsigned long long *)buf)[1];
+ memory_range[memory_ranges].type = RANGE_RAM;
+ printf("%016Lx-%016Lx : %x\n", memory_range[memory_ranges].start, memory_range[memory_ranges].end, memory_range[memory_ranges].type);
+ memory_ranges++;
+ fclose(file);
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+
+ *range = memory_range;
+ *ranges = memory_ranges;
+ return 0;
+}
+
+struct file_type file_type[] = {
+ { "elf-ppc64", elf_ppc64_probe, elf_ppc64_load, elf_ppc64_usage },
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+}
+
+static struct {
+} arch_options = {
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+{
+ int result;
+ struct utsname utsname;
+ result = uname(&utsname);
+ if (result < 0) {
+ fprintf(stderr, "uname failed: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ if (strcmp(utsname.machine, "ppc64") == 0)
+ {
+ /* We are running a 32-bit kexec-tools on 64-bit ppc64.
+ * So pass KEXEC_ARCH_PPC64 here
+ */
+ *flags |= KEXEC_ARCH_PPC64;
+ }
+ else {
+ fprintf(stderr, "Unsupported machine type: %s\n",
+ utsname.machine);
+ return -1;
+ }
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *info)
+{
+}
+
diff -puN kexec/arch/ppc64/kexec-ppc64.h~kexec-tools-ppc64 kexec/arch/ppc64/kexec-ppc64.h
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-ppc64.h~kexec-tools-ppc64 2005-08-08 20:26:13.000000000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-ppc64.h 2005-08-08 20:30:33.000000000 +0530
@@ -6,4 +6,8 @@ int elf_ppc64_load(int argc, char **argv
struct kexec_info *info);
void elf_ppc64_usage(void);
-#endif /* KEXEC_PPC_H */
+unsigned long initrd_base;
+unsigned long initrd_size;
+unsigned long v2wrap_base;
+unsigned long vmlinux_base;
+#endif /* KEXEC_PPC64_H */
diff -puN kexec/arch/ppc64/Makefile~kexec-tools-ppc64 kexec/arch/ppc64/Makefile
--- kexec-tools-1.101/kexec/arch/ppc64/Makefile~kexec-tools-ppc64 2005-08-08 20:26:13.000000000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/Makefile 2005-08-08 20:41:34.000000000 +0530
@@ -1,7 +1,8 @@
#
# kexec ppc64 (linux booting linux)
#
+KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-ppc64.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-ppc64.c
KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-rel-ppc64.c
KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-zImage-ppc64.c
-
-KEXEC_S_SRCS+=
+KEXEC_C_SRCS+= kexec/arch/ppc64/fs2dt.c
diff -puN /dev/null kexec/arch/ppc64/v2wrap.S
--- /dev/null 2005-08-08 20:42:13.772546000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/v2wrap.S 2005-08-08 20:28:08.000000000 +0530
@@ -0,0 +1,114 @@
+#
+# kexec: Linux boots Linux
+#
+# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+# v2wrap.S
+# a wrapper to place in front of a v2 device tree
+# to call a ppc64 kernel with the expected arguments
+# of kernel(device-tree, phys-offset, 0)
+#
+# calling convention:
+# r3 = physical number of this cpu (all cpus)
+# r4 = address of this chunk (master only)
+# master enters at start (aka first byte of this chunk)
+# slaves (additional cpus), if any, enter a copy of the
+# first 0x100 bytes of this code relocated to 0x0
+#
+# in other words,
+# a copy of the first 0x100 bytes of this code is copied to 0
+# and the slaves are sent to address 0x60
+# with r3 = their physical cpu number.
+
+
+# look a bit like a Linux kernel here ...
+ .machine ppc64
+ .org 0
+start: b master
+ tweq 0,0
+secondary_hold:
+ .llong 0
+
+ .org 0x20 # need a bit more space than after slave,
+master:
+ std 4,secondary_hold at l(0) # bring slaves up here to this copy
+ sync # try to get the slaves to see this
+ or 1,1,1 # low priority to let other thread catchup
+ isync
+ mr 5,3 # save cpu id to r5
+ addi 3,4,0x100 # r3 = boot param block
+ lwz 6,20(3) # fetch version number
+ cmpwi 0,6,2 # v2 ?
+ blt 80f
+ stw 5,28(3) # save my cpu number as boot_cpu_phys
+80: b 81f
+
+ .org 0x60 # ABI: slaves start at 60 with r3=phys
+slave: ld 4,secondary_hold at l(0);
+ cmpdi 0,4,0
+ beq slave
+
+ # ahh, master told us where he is running from
+ # jump into our copy of the code up there so this code can change
+ addi 5,4,1f-start
+ mtctr 5
+ bctr
+
+ # ok, now wait for the master to tell is to go back to the new block
+1: ld 5,copied at l(4)
+ cmpdi 0,5,0
+ beq 1b
+ ba 0x60
+
+
+
+ .long 0 # just an eye-catcher, delete if space needed
+ .long 0 # just an eye-catcher, delete if space needed
+
+81: # master continues here
+ or 3,3,3 # ok back to high, lets boot
+ lis 6,0x1
+ mtctr 6 # delay a bit for slaves to catch up
+83: bdnz 83b # before we overwrite 0-100 again
+
+ ld 4,-8(3) # kernel pointer is at -8(bb) by loader
+ addi 5,4,-8 # prepare copy with update form instructions
+ li 6,0x100/8
+ mtctr 6
+ li 6,-8
+85: ldu 7,8(5)
+ stdu 7,8(6)
+ bdnz 85b
+
+ li 5,0 # r5 will be 0 for kernel
+ dcbst 0,5 # store dcache, flush icache
+ dcbst 0,6 # 0 and 0xf8 covers us with 128 byte lines
+ mtctr 4 # prepare branch too
+ sync
+ icbi 0,5
+ icbi 0,6
+ sync
+ isync
+ std 6,-16(3) # send slaves back down
+ bctr # start kernel
+
+ .org 0xf0
+copied: .llong 0
+kernel: .llong 0
+ .org 0x100
+__end_stub:
+ .equ boot_block, . - start
diff -puN purgatory/Makefile~kexec-tools-ppc64 purgatory/Makefile
--- kexec-tools-1.101/purgatory/Makefile~kexec-tools-ppc64 2005-08-08 20:26:13.000000000 +0530
+++ kexec-tools-1.101-sharada/purgatory/Makefile 2005-08-08 20:31:16.000000000 +0530
@@ -6,6 +6,11 @@
# There is probably a cleaner way to do this but for now this
# should keep us from accidentially include unsafe library functions
# or headers.
+
+ifeq ($(ARCH),ppc64)
+LDFLAGS = -melf64ppc
+endif
+
PCFLAGS:=-Wall -Os \
-I$(shell $(CC) -print-file-name=include) \
-Ipurgatory/include -Ipurgatory/arch/$(ARCH)/include \
diff -puN /dev/null kexec/arch/ppc64/make_v2wrap
--- /dev/null 2005-08-08 20:42:13.772546000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/make_v2wrap 2005-08-08 20:42:12.000000000 +0530
@@ -0,0 +1,13 @@
+# kexec ppc64 (linux booting linux)
+# Build v2wrap from here for now
+
+KEXEC_S_OBJ_PATH:=kexec/arch/ppc64
+KEXEC_S_SRCS:=kexec/arch/ppc64/v2wrap.S
+KEXEC_S_OBJS:=kexec/arch/ppc64/v2wrap
+
+all: $(KEXEC_S_OBJS)
+$(KEXEC_S_OBJS): $(KEXEC_S_SRCS)
+ gcc -c -o $(KEXEC_S_OBJ_PATH)/v2wrap.o $(KEXEC_S_OBJ_PATH)/v2wrap.S
+ ld -Ttext=0 -e 0 -o $(KEXEC_S_OBJ_PATH)/v2wrap.elf $(KEXEC_S_OBJ_PATH)/v2wrap.o
+ objcopy -O binary $(KEXEC_S_OBJ_PATH)/v2wrap.elf $(KEXEC_S_OBJ_PATH)/v2wrap
+
_
More information about the Linuxppc64-dev
mailing list