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, &param, &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