Skip to contentSkip to conversation

bootwrapper: support for kexec to zImage

PATCH In review Milton Miller 3 participants 25 messages Jul 10, 2007 – Jul 19, 2007 PowerPC

lore
Share & tools
lore
#0 lore raw

cover bootwrapper: support for kexec to zImage

This series creates a 32 bit zImage wrapper for a 32 or 64 bit PowerPC
Linux kernel. This allows you to kexec a zImage with its compressed
vmlinux instead of the uncompressed vmlinux elf. The elf is also packaged as a 64 bit elf for use by kexec-tools for 64 bit kernels.

This series also adds code to extract the vmlinux elf file from an initramfs (or other platform supplied location). This allows one to load the elf once (compressed) and use it both for userspace and for the actual boot.

milton

#1 lore raw

PATCH 1/15 boot: find initrd location from device-tree

Some platforms have a boot agent that can create or modify properties in the device-tree and load images into memory. Provide a helper to set loader_info used by prep_initrd().

---
Moved to devtree.c as suggested.

define UNIT_MAX hardcoded as requested.

Print exactly why we are not propagting loader supplied initrd knowledge to wrapper code.

Having start_prop and end_prop as variables allows several source lines to fit in 80 columns.

arch/powerpc/boot/ops.h +1 -0
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -159,6 +159,7 @@ void dt_fixup_clock(const char *path, u3
 void __dt_fixup_mac_addresses(u32 startindex, ...);
 #define dt_fixup_mac_addresses(...) \
 	__dt_fixup_mac_addresses(0, __VA_ARGS__, NULL)
+void dt_find_initrd(void);
 
 
 static inline void *find_node_by_linuxphandle(const u32 linuxphandle)
arch/powerpc/boot/types.h +2 -0
Index: work.git/arch/powerpc/boot/types.h
===================================================================
@@ -12,6 +12,8 @@ typedef short			s16;
 typedef int			s32;
 typedef long long		s64;
 
+#define UINT_MAX	0xFFFFFFFF
+
 #define min(x,y) ({ \
 	typeof(x) _x = (x);	\
 	typeof(y) _y = (y);	\
arch/powerpc/boot/devtree.c +66 -0

75 lines not on this page · raw message

#3 lore raw

PATCH 2/15 boot: record header bytes in gunzip_start

In reply to Milton Miller

Record the number of header bytes skipped in the total bytes read field.

This is needed for the initramfs parsing code to find the end of the zip file.

---

arch/powerpc/boot/gunzip_util.c +1 -0
Index: work.git/arch/powerpc/boot/gunzip_util.c
===================================================================
@@ -78,6 +78,7 @@ void gunzip_start(struct gunzip_state *s
 			fatal("inflateInit2 returned %d\n\r", r);
 	}
 
+	state->s.total_in = hdrlen;
 	state->s.next_in = src + hdrlen;
 	state->s.avail_in = srclen - hdrlen;
 }
#6 lore raw

PATCH 3/15 boot: simplfy gunzip_finish

In reply to Milton Miller

Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.

---

arch/powerpc/boot/gunzip_util.c +2 -5
Index: work.git/arch/powerpc/boot/gunzip_util.c
===================================================================
@@ -194,13 +194,10 @@ int gunzip_finish(struct gunzip_state *s
 {
 	int len;
 
+	len = gunzip_partial(state, dst, dstlen);
+
 	if (state->s.workspace) {
-		len = gunzip_partial(state, dst, dstlen);
 		zlib_inflateEnd(&state->s);
-	} else {
-		/* uncompressed image */
-		len = min(state->s.avail_in, (unsigned)dstlen);
-		memcpy(dst, state->s.next_in, len);
 	}
 
 	return len;
#10 lore raw

PATCH 4/15 bootwrapper: smp support code

In reply to Milton Miller

Support code to move cpus around, both a spin loop and c code to move the cpus before uncompressing and copying the kernel to 0.

The low level code is designed to be included in a crt0 or other assembly file because it may need to be at a fixed location or there may be other entry point requirements.

Note: this code works with kernel head_64.S. head_6xx.S needs the
0x60 entry point (it currently uses something closer to 0xC0; but the similar code is at 0xC4); the other heads don't appear to support SMP.

---
Cleaned up documentation. Hopefully the format is correct.

Removed the #if 1 #else #endif

Move hidden asm code outside function instead of branching over it.

This code has previously survied days of kexec stress and also works when the next stage is itself (ie zBoot to zImage).

arch/powerpc/boot/marshal_low.S new file +103 -0
Index: work.git/arch/powerpc/boot/marshal_low.S
===================================================================
@@ -0,0 +1,103 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright 2007 IBM Corporation.
+ *
+ * Authors: Milton Miller <[email protected]>
+ *
+ */
+
+#include "ppc_asm.h"
+
+	.text
+	/*
+	 * This code is designed to be a kexec entry point block.
+	 * That is, it has both code for the master cpu that begins
+	 * at offset 0 as linked into the image, and a sequence of
+	 * 0x100 bytes that, when copied to address 0, forms the
+	 * wait loop for slave cpus.  Each slave should have its
+	 * unique hardware cpu identifier in r3 before entering
+	 * this code.
+	 */
+	.globl	master
+master: b	_zimage_start_plat
+
+	.global slave_wait
+slave_wait:
+	/* r3 cpu id, r4 slaves_wait, r5 cpu bit, r6 cpu mask word offset */
+
+	/* set our bit in the slaves mask  */
+98:	lwarx	r7,r4,r6
+	or	r8,r7,r5
+	stwcx.	r8,r4,r6
+	bne	98b
+
+	and.	r8,r7,r5
+	bnel-	err_slave
+
+99:	lwz	r7,gohere-slave_wait(r4)
+	cmpwi	0,r7,0
+	beq	99b
+	mtctr	r7
+	mr	r4,r7
+	bctr
+
+
+	.global gohere
+gohere:	.long	0			/* when set the slave moves */
+
+
+err_slave:
+	stw	r5,slave_error-slave_wait(4)	/* no locking */
+	blr
+
+	.globl	slave_error		/* set when slave detects error */
+slave_error:
+	.long	0
+
+	/*
+	 * The slaves may be in 32 or 64 bit mode, we don't care
+	 * r3 is the slave cpu number, matching the device tree.
+	 */
+	.org	master+0x60
+	.globl	slave
+slave:	bl	1f
+1:	mflr	r4
+	addi	r4,r4,slave_wait-1b	/* code assumes r4=slave_wait */
+	li	r5,1
+	rlwnm	r5,r5,r3,0,31		/* bit within word */
+	rlwinm	r6,r3,32-5+2,4,29	/* word in array */
+	addi	r6,r6,slaves-slave_wait	/* relative to r4, slave_wait */
+	b	slave_wait
+
+	.org	master+0x80	/* put locked bitmask data in another line */
+	.global	slaves
+slaves:
+
+	.globl slaves_end;
+slaves_end = 0f
+
+#if 0
+	/* today, the 32 bit kernel starts slaves at 0xc0
+	 * but this limits us to cpu to 512 vs 1024
+	 */
+	.org	master+0xc0
+0:	b	slave
+#endif
+
+
+	.org	master+0x100		/* we must fit in 0x100 bytes */
+0:
+
arch/powerpc/boot/marshal.c new file +275 -0

275 lines not on this page · raw message

arch/powerpc/boot/Makefile +1 -0
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -42,6 +42,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
 	$(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
+		marshal.c \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
 		gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
 		44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
arch/powerpc/boot/reg.h +5 -0
Index: work.git/arch/powerpc/boot/reg.h
===================================================================
@@ -19,4 +19,9 @@ static inline u32 mfpvr(void)
 register void *__stack_pointer asm("r1");
 #define get_sp()	(__stack_pointer)
 
+#define HMT_MEDIUM	asm volatile("or 2,2,2")
+#define HMT_LOW		asm volatile("or 1,1,1")
+#define barrier()	asm volatile("":::"memory")
+
+
 #endif	/* _PPC_BOOT_REG_H */
arch/powerpc/boot/ops.h +9 -1
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -18,6 +18,7 @@
 #define	COMMAND_LINE_SIZE	512
 #define	MAX_PATH_LEN		256
 #define	MAX_PROP_LEN		256 /* What should this be? */
+#define	SMP_SLAVE_SIZE		256 /* Size of SMP slave block, kexec/kernel */
 
 typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5);
 
@@ -84,10 +85,17 @@ int ns16550_console_init(void *devp, str
 int mpsc_console_init(void *devp, struct serial_console_data *scdp);
 void *simple_alloc_init(char *base, unsigned long heap_size,
 			unsigned long granularity, unsigned long max_allocs);
-extern void flush_cache(void *, unsigned long);
+void flush_cache(void *, unsigned long);
 int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size);
 int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr);
 
+/* marshal slave cpus around to kernel */
+void move_slaves_up(void);
+void move_slaves_here(void *where);
+void send_slaves_to_kernel(void *vmlinux_addr);
+void slaves_are_low(void);
+void wait_slaves_moved(void);
+
 static inline void *finddevice(const char *name)
 {
 	return (dt_ops.finddevice) ? dt_ops.finddevice(name) : NULL;
#11 lore raw

PATCH 5/15 bootwrapper: occupied memory ranges

In reply to Milton Miller

Add a set of library routines to manage gross memory allocations.

This code uses an array in bss to store upto 32 entrys with merging representing a range of memory below rmo_end (aka end of real mode memory at 0).

To use this code, a platform would set rmo_end, call occupy_memory, then then call init_malloc for fine grain allocation.

Also, an optional vmlinux_alloc.

---
Fixed a bug when extending across multiple ranges.

Cleaned up documentation

arch/powerpc/boot/memranges.c new file +242 -0
Index: work.git/arch/powerpc/boot/memranges.c
===================================================================
@@ -0,0 +1,242 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corporation 2007
+ *
+ * Authors: Milton Miller <[email protected]>
+ */
+
+#include "ops.h"
+#include "stdio.h"
+#include "flatdevtree.h"
+#include "page.h"
+#include "types.h"
+
+extern char _start[], _end[];
+
+void *rmo_end;
+
+static struct {
+	void *start, *end;
+} ranges[32];
+static int num_ranges;
+
+/**
+ * add_occupied_range - mark a range as occupied
+ * @start: start of range pointer
+ * @end: end of range pointer
+ *
+ * Mark the range from @start to @end as occupied.
+ * Ignore anything above rmo_end.
+ */
+void add_occupied_range(void *start, void *end)
+{
+	int i, j;
+
+	if (start == end)
+		return;
+	if (start > rmo_end)
+		return;
+	if (end > rmo_end)
+		end = rmo_end;
+	if (start > end)
+		fatal("%s: BUG: start %p > end %p\n\r", __FUNCTION__,
+				start, end);
+
+	printf("add %p %p: ", start, end);
+
+	for (i=0; i < num_ranges; i++)
+		if (start <= ranges[i].end)
+			break;
+
+	/* extend and merge any overlapping ranges */
+	if (i < num_ranges && end >= ranges[i].start) {
+		ranges[i].start = min(start, ranges[i].start);
+		for (j=i; j < num_ranges; j++)
+			if (end >= ranges[j].start)
+				end = max(end, ranges[j].end);
+			else
+				break;
+		ranges[i].end = end;
+
+		if (j == i + 1) {
+			printf("extending range %d to %p %p\n\r", i,
+				ranges[i].start, ranges[i].end);
+		} else {
+			printf("merged ranges %d to %d now %p %p\n\r", i, j,
+				ranges[i].start, ranges[i].end);
+
+			++i;
+			memmove(&ranges[i], &ranges[j],
+				(num_ranges - j) * sizeof(ranges[0]));
+			num_ranges -= j-i;
+		}
+	} else {
+		/* insert a new range */
+		if (num_ranges >= ARRAY_SIZE(ranges) - 1)
+			fatal("Too many memory ranges to track\n");
+
+		printf("inserting range %d between %p and %p\n\r",
+				i, i ? ranges[i-1].end : 0,
+				i == num_ranges ? rmo_end : ranges[i].start);
+
+		memmove(&ranges[i+1], &ranges[i],
+				(num_ranges - i) * sizeof(ranges[0]));
+		num_ranges++;
+
+		ranges[i].start = start;
+		ranges[i].end = end;
+	}
+}
+
+/**
+ * add_occupied_range_ulong - mark a range as occupied
+ * @start: start of block to occupy
+ * @end: start of block to occupy
+ *
+ * Call add_occupied_range() after casting to ulong @start and @end to
+ * void * pointers.
+ */
+void add_occupied_range_ulong(unsigned long start, unsigned long end)
+{
+	add_occupied_range((void *)start, (void *)end);
+}
+
+/**
+ * add_known_ranges - occupy some known regions
+ * @dt_blob: a flattend device tree to occupy, or NULL to skip
+ *
+ * call add_occupied_range() for the wrapper, loader supplied initrd,
+ * and, if not %NULL, the device tree blob @dt_blob and any reserved
+ * memory ranges therein.
+ */
+void add_known_ranges(struct boot_param_header *dt_blob)
+{
+	unsigned long long rstart, rlen, rend, *rsrv;
+
+	add_occupied_range(_start, _end);
+
+	add_occupied_range_ulong(loader_info.initrd_addr,
+		loader_info.initrd_addr + loader_info.initrd_size);
+
+	if (dt_blob == NULL)
+		return;
+
+	add_occupied_range(dt_blob, (void *)dt_blob + dt_blob->totalsize);
+
+	/* only support 8-byte reserve map.  Only care about < 4G */
+	rsrv = (void *)dt_blob + dt_blob->off_mem_rsvmap;
+	do {
+		rstart = *rsrv++;
+		rlen = *rsrv++;
+		rend = rstart + rlen;
+
+		if (rlen && rstart < UINT_MAX) {
+			if (rend < UINT_MAX)
+				add_occupied_range_ulong(rstart, rend);
+			else
+				add_occupied_range_ulong(rstart, UINT_MAX);
+		}
+	} while (rlen);
+}
+
+/**
+ * ranges_init_malloc - initialize malloc heap in a free memory range.
+ *
+ * Call simple_alloc_init using the largest gap between occupied ranges.
+ * Does not consider before the first or after the last range.
+ */
+void ranges_init_malloc(void)
+{
+	int i;
+	unsigned long size = 0;
+	void *heap_start, *heap_end;
+
+	/*
+	 * Allow the beginning for the kernel and the end for
+	 * other things the platform might want to have reserved.
+	 */
+
+	heap_start = NULL;		/* avoid gcc warning */
+	for (i=1; i < num_ranges; i++) {
+		unsigned long newsize;
+
+		newsize = ranges[i].start - ranges[i-1].end;
+		if (newsize > size) {
+			size = newsize;
+			heap_start = ranges[i-1].end;
+		}
+	}
+
+	if (size < 4 * 1024 * 1024)
+		fatal("Can't find a sutiable gap (largest 0x%lx)", size);
+
+	printf("putting heap between %p and %p size 0x%lx\n\r", heap_start,
+			heap_start + size, size);
+	heap_end = simple_alloc_init(heap_start, size * 7 / 8,
+			PAGE_SIZE, /* max num alloc */ 4096);
+	if (heap_end > (heap_start + size))
+		fatal("heap alloc overflowed gap (%p)\n\r", heap_end);
+
+	add_occupied_range(heap_start, heap_end);
+}
+
+/**
+ * ranges_vmlinux_alloc - an optonal kernel allocator.
+ * @size: the image size of the kernel
+ *
+ * Searches for a location to put the kernel, then reserve that range
+ * and the area to which the kernel will relocate itself.   First try
+ * address %0.  If that is blocked by a previos call to add_occupied_range(),
+ * try malloc().  If that also fails search for free space between the
+ * occupied ranges or between the last range and rmo_end.
+ */
+void *ranges_vmlinux_alloc(unsigned long size)
+{
+	void *addr;
+	int i;
+
+	/* Assume _start to _end is occupied */
+	addr = (void *)0;
+	if (addr + size < ranges[0].start)
+		goto occupy;
+
+	addr = malloc(size);
+	if (addr)
+		goto out;
+
+	for (i=1; i < num_ranges; i++) {
+		if (size < ranges[i].start - ranges[i-1].end)
+			goto occupy_range;
+	}
+	if (size < rmo_end - ranges[i-1].end)
+		goto occupy_range;
+
+	fatal("Unable to find a 0x%lx byte gap for the kernel\n", size);
+
+occupy_range:
+	addr = ranges[i-1].end;
+occupy:
+	add_occupied_range(addr, addr + size);
+out:
+	/*
+	 * Assume the kernel will decompress to 0, but don't implicity
+	 * create a new gap below the current first range.
+	 */
+	if ((unsigned long)ranges[0].end < size)
+		add_occupied_range_ulong(0, size);
+
+	return addr;
+}
arch/powerpc/boot/ops.h +10 -0
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -22,6 +22,8 @@
 
 typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5);
 
+struct boot_param_header;
+
 /* Platform specific operations */
 struct platform_ops {
 	void	(*fixups)(void);
@@ -96,6 +98,14 @@ void send_slaves_to_kernel(void *vmlinux
 void slaves_are_low(void);
 void wait_slaves_moved(void);
 
+/* memory ranges */
+extern void *rmo_end;
+void add_occupied_range(void *start, void *end);
+void add_occupied_range_ulong(unsigned long start, unsigned long end);
+void add_known_ranges(struct boot_param_header *dt_blob);
+void ranges_init_malloc(void);
+void *ranges_vmlinux_alloc(unsigned long size);
+
 static inline void *finddevice(const char *name)
 {
 	return (dt_ops.finddevice) ? dt_ops.finddevice(name) : NULL;
arch/powerpc/boot/Makefile +1 -1
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -42,7 +42,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
 	$(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
-		marshal.c \
+		marshal.c memranges.c \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
 		gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
 		44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
#12 lore raw

PATCH 6/15 bootwrapper: switch 64 bit cpus to 32 bit mode

In reply to Milton Miller

Add code to check if the processor is in 64 or 32 bit mode using only instructions from the 32 bit subset. If the processor is in 64 bit mode, switch to 32 bit mode by clearing MSR[SF].

Also add a 64 bit procedure descriptor to use as a elf64 entry point.

---
Merged into crt0.S instead of a separate file with a separate entry point.

arch/powerpc/boot/crt0.S +36 -0
Index: kernel/arch/powerpc/boot/crt0.S
===================================================================
@@ -17,11 +17,47 @@
 _zimage_start_opd:
 	.long	_zimage_start, 0, 0, 0
 
+	/* a procedure descriptor used when pretending to be elf64_powerpc */
+	.balign	8
+	.globl	_zimage_start_64
+_zimage_start_64:
+	.long	0, _zimage_start	/* big endian, supported reloc ppc32 */
+	.long	0, 0, 0, 0, 0, 0
+
+
 	.weak	_zimage_start
 	.globl	_zimage_start
 _zimage_start:
 	.globl	_zimage_start_lib
 _zimage_start_lib:
+	/* Check if the processor is running in 32 bit mode, using
+	 * only 32 bit instructions which should be safe on 32 and
+	 * 64 bit processors.
+	 *
+	 * Subtract the bottom 32 bits of MSR from the full value
+	 * recording the result.  Since MSR[SF] is in the high word,
+	 * the result will be not-equal iff in 32 bit mode (either
+	 * the processor is a 32 bit processor or MSR[SF] = 0).
+	 */
+	mfmsr	r0		/* grab whole msr		*/
+	rlwinm	r8,r0,0,0,31	/* extract bottom word		*/
+	subf.	r8,r8,r0	/* subtract, same?		*/
+	beq	0f		/* yes: we are 32 bit mode	*/
+
+	/* We are in 64-bit mode.  This program must run in 32 bit
+	 * mode.  Assume we are actually running somewhere in the
+	 * low 32 bits of the address space, so we can just turn
+	 * off MSR[SF] which is bit 0.
+	 */
+	.machine push
+	.machine "ppc64"
+	rldicl	r0,r0,0,1
+	sync
+	mtmsrd	r0
+	isync
+	.machine pop
+0:	/* We are now in 32-bit mode */
+
 	/* Work out the offset between the address we were linked at
 	   and the address where we're running. */
 	bl	1f
#14 lore raw

PATCH 7/15 bootwrapper: Add kexec callable zImage wrapper

In reply to Milton Miller

This code creates a 32 bit zImage wrapper for a 32 or 64 bit PowerPC
Linux kernel. This allows you to kexec a zImage with its compressed
vmlinux instead of the uncompressed vmlinux elf. The elf is also packaged as a 64 bit elf for use by kexec-tools for 64 bit kernels.

Limitations:

The memory node off the root with a name starting with "memory" must contain enough free memory (not in the reserved ranges) in the first reg range to uncompress the the kernel with padding.

---
I split a few more routines out from this file.

I removed the call serial_console_init because some of the serial console require a loader supplied virtural address mapping which will not exist when called from kexec.

Successfully boots from and to a 64 bit kernel when loaded at 0 and when loaded after the kernel _end on machines with with initrds, rtas, and various other data reserved.

The next patch needed to load using kexec-tools without an externally generated flat device tree.

arch/powerpc/boot/kexec.c new file +181 -0
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -0,0 +1,181 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corporation 2007
+ *
+ * Authors: Milton Miller <[email protected]>
+ */
+
+#include "ops.h"
+#include "stdio.h"
+#include "flatdevtree.h"
+#include "page.h"
+#include "types.h"
+
+extern char _start[];
+extern char _end[];
+
+BSS_STACK(16*1024);
+
+static void find_console_from_tree(void)
+{
+	int rc = -1;
+
+	if (rc) {
+		/* no console, oh well */
+	}
+}
+
+/* fixme: use find_device_by_type "memory" and xlate_reg */
+static void find_rmo_end(void)
+{
+	unsigned int na, ns, reg[4], *rp;
+	void *devp;
+	int rc;
+
+	devp = finddevice("/");
+	if (!devp)
+		fatal("Ack, device-tree root");
+	rc = getprop(devp, "#address-cells", &na, sizeof(na));
+	if (rc != sizeof(na))
+		fatal("Ack, no #address-cells in root");
+	rc = getprop(devp, "#size-cells", &ns, sizeof(ns));
+	if (rc != sizeof(ns))
+		fatal("Ack, no #size-cells in root");
+	if (!na || !ns || na + ns > ARRAY_SIZE(reg))
+		fatal("#addr-cells or #size-cells unusable");
+	do {
+		devp = finddevice("/memory@0");
+		if (!devp)
+			devp = finddevice("/memory");
+		if (!devp)
+			devp = finddevice("/memory@00000000");
+		if (!devp)
+			devp = finddevice("/memory@0000000000000000");
+		if (!devp)
+			fatal("Ack, can't find  memory");
+		rc = getprop(devp, "reg", reg, sizeof(reg));
+		if (rc < (na + ns) * sizeof(int))
+			fatal("Ack, no valid reg property in memory");
+
+		rp = &reg[0];
+		while (na--) {
+			if (*rp)
+				fatal("didn't find memory at 0");
+			rp++;
+		}
+		while (--ns) {
+			if (*rp) {
+				/* first range >= 4G , use 2G */
+				rmo_end = (void *)0x80000000;
+				return;
+			}
+			rp++;
+		}
+	} while (0);
+
+	rmo_end = (void *)*rp;
+}
+
+/**
+ * setup_initial_heap - setup a small heap in the bss
+ * Using a preallocated heap, setup for scanning the device tree.
+ * Intended for the initial read while the tree will remain read-only so
+ * a minimal malloc and search limit can be used.  This way we don't have
+ * lots of data or bss to clear.
+ */
+static void setup_initial_heap(void)
+{
+	static char initial_heap[8*1024];
+	void *heap_end;
+
+	heap_end = simple_alloc_init(initial_heap,
+			sizeof(initial_heap) * 7 / 8,
+			sizeof(long), 64);
+
+	if (heap_end - sizeof(initial_heap) > (void *)&initial_heap[0])
+		fatal("Initial heap too big\n\r");
+}
+
+static void early_scan_flat_tree(struct boot_param_header *dt_blob)
+{
+	int rc;
+
+	rc = ft_init(dt_blob, dt_blob->totalsize, 50);
+	if (rc)
+		fatal("couldn't initialize device-tree\n\r");
+
+	find_rmo_end();
+	dt_find_initrd();
+}
+
+static void init_flat_tree(struct boot_param_header *dt_blob)
+{
+	int rc;
+
+	rc = ft_init(dt_blob, dt_blob->totalsize, /* max_finddevice */ 1024);
+	if (rc)
+		fatal("Unable to initialize device_tree library!\n\r");
+}
+
+static void *saved_vmlinux_addr;
+
+static void *kexec_vmlinux_alloc(unsigned long size)
+{
+	void *addr;
+
+	addr = ranges_vmlinux_alloc(size);
+
+	saved_vmlinux_addr = addr;
+	return addr;
+}
+
+static void kexec_fixups(void)
+{
+	wait_slaves_moved();
+}
+
+static unsigned long (*finalize_chain)(void);
+
+static unsigned long kexec_finalize(void)
+{
+	send_slaves_to_kernel(saved_vmlinux_addr);
+
+	return finalize_chain();
+}
+
+void kexec_platform_init(struct boot_param_header *dt_blob)
+{
+	slaves_are_low();
+	move_slaves_up();
+
+	setup_initial_heap();
+	early_scan_flat_tree(dt_blob);
+
+	/* drivers can malloc and read the tree, but not realloc later
+	 * or modify the tree now.
+	 */
+	if (!console_ops.write)
+		find_console_from_tree();
+
+	add_known_ranges(dt_blob);
+	ranges_init_malloc();
+	init_flat_tree(dt_blob);
+
+	platform_ops.vmlinux_alloc = kexec_vmlinux_alloc;
+	platform_ops.fixups = kexec_fixups;
+	finalize_chain = dt_ops.finalize;
+	dt_ops.finalize = kexec_finalize;
+}
arch/powerpc/boot/crt0_kexec.S new file +45 -0

45 lines not on this page · raw message

arch/powerpc/boot/wrapper +9 -0
Index: work.git/arch/powerpc/boot/wrapper
===================================================================
@@ -133,6 +133,12 @@ coff)
     platformo=$object/of.o
     lds=$object/zImage.coff.lds
     ;;
+kexec)
+    platformo=$object/crt0_kexec.o
+    ;;
+kexec64)
+    platformo="-e _zimage_start_64 $object/crt0_kexec.o"
+    ;;
 miboot|uboot)
     # miboot and U-boot want just the bare bits, not an ELF binary
     ext=bin
@@ -190,6 +196,9 @@ uboot)
     fi
     exit 0
     ;;
+kexec64)
+    ${CROSS}objcopy -O elf64-powerpc $ofile
+    ;;
 esac
 
 addsec() {
arch/powerpc/boot/Makefile +9 -5
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -42,11 +42,11 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
 	$(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
-		marshal.c memranges.c \
+		marshal.c memranges.c kexec.c \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
 		gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
 		44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
-src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c \
+src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c crt0_kexec.S \
 		cuboot-ebony.c treeboot-ebony.c prpmc2800.c \
 		ps3-head.S ps3-hvcall.S ps3.c
 src-boot := $(src-wlib) $(src-plat) empty.c
@@ -126,6 +126,9 @@ quiet_cmd_wrap	= WRAP    $@
       cmd_wrap	=$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \
 		$(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux
 
+kexec-$(CONFIG_PPC32)			+= zImage.kexec
+kexec-$(CONFIG_PPC64)			+= zImage.kexec64
+
 image-$(CONFIG_PPC_PSERIES)		+= zImage.pseries
 image-$(CONFIG_PPC_MAPLE)		+= zImage.pseries
 image-$(CONFIG_PPC_IBM_CELL_BLADE)	+= zImage.pseries
@@ -138,6 +141,7 @@ image-$(CONFIG_PPC_HOLLY)		+= zImage.hol
 image-$(CONFIG_PPC_PRPMC2800)		+= zImage.prpmc2800
 image-$(CONFIG_PPC_ISERIES)		+= zImage.iseries
 image-$(CONFIG_DEFAULT_UIMAGE)		+= uImage
+image-$(CONFIG_KEXEC)			+= $(kexec-y)
 
 ifneq ($(CONFIG_DEVICE_TREE),"")
 image-$(CONFIG_PPC_83xx)		+= cuImage.83xx
@@ -151,7 +155,7 @@ ifeq ($(CONFIG_PPC32),y)
 image-$(CONFIG_PPC_PMAC)	+= zImage.coff zImage.miboot
 endif
 
-initrd-  := $(patsubst zImage%, zImage.initrd%, $(image-n) $(image-))
+initrd-  := $(patsubst zImage%, zImage.initrd%, $(image-n) $(image-) $(kexec-))
 initrd-y := $(patsubst zImage%, zImage.initrd%, \
 		$(patsubst treeImage%, treeImage.initrd%, $(image-y)))
 initrd-y := $(filter-out $(image-y), $(initrd-y))
@@ -219,8 +223,8 @@ install: $(CONFIGURE) $(addprefix $(obj)
 	sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $<
 
 # anything not in $(targets)
-clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* treeImage.* \
-	otheros.bld
+clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* \
+	treeImage.* $(kexec-)
 
 # clean up files cached by wrapper
 clean-kernel := vmlinux.strip vmlinux.bin
arch/powerpc/boot/ops.h +1 -0
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -90,6 +90,7 @@ void *simple_alloc_init(char *base, unsi
 void flush_cache(void *, unsigned long);
 int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size);
 int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr);
+void kexec_platform_init(struct boot_param_header *dt_blob);
 
 /* marshal slave cpus around to kernel */
 void move_slaves_up(void);
#16 lore raw

PATCH 8/15 bootwrapper: convert flatdevtree to version 16

In reply to Milton Miller

kexec-tools still produces a version 2 device tree, while the libraries in the wrapper only support version 16 and later.

Add a routine to convert a v2 flat device tree to a v16 one inplace by inserting OF_DT_NOP and chomping full path. Make space for new headers by moving and then chomping the OF_DT_NOPs.

---
Rediffed Makefile

arch/powerpc/boot/flatdevtree_conv.c new file +280 -0
Index: work.git/arch/powerpc/boot/flatdevtree_conv.c
===================================================================
@@ -0,0 +1,280 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corporation 2007
+ *
+ * Authors: Milton Miller <[email protected]>
+ */
+#include "flatdevtree.h"
+#include "stdio.h"
+#include "ops.h"
+
+#define MIN_VERSION 2
+#define OUT_VERSION 16
+#define OUT_COMPAT 16
+
+#ifdef NO_CHECK
+static int check_v123_tree(u32 *start, u32 *limit)
+{
+	return 0;
+}
+#else
+/**
+ * check_v123_tree - check integrety of a version 1, 2, or 3 tree
+ * @start: the start of the device tree struct
+ * @limit: the end of the region for the struct
+ * structural checks on device_tree
+ */
+static int check_v123_tree(u32 *start, u32 *limit)
+{
+	u32 len;
+	int depth = 0;
+	u32 *dtp = start;
+
+	while (dtp < limit)
+		switch (*dtp) {
+		case OF_DT_END:
+			if (depth)
+				return -1;
+			return ++dtp - start;
+		case OF_DT_NOP:
+			dtp++;
+			break;
+		case OF_DT_END_NODE:
+			dtp++;
+			depth--;
+			break;
+		case OF_DT_BEGIN_NODE:
+			len = strlen((char *)(++dtp));
+			/* check path is suffix to previous? */
+			dtp += 1 + (len / 4);
+			depth++;
+			break;
+		case OF_DT_PROP:
+			len = dtp[1];
+			dtp += 3;
+			if ((len >= 8) && ((long)dtp & 4))
+				dtp++;
+			dtp += (len + 3) / 4;
+			break;
+		default:
+			return -1;
+		}
+	return -1;	/* no OF_DT_END */
+}
+#endif
+
+/**
+ * nop_to_v16 - add %OF_DT_NOP to hide alignment differences
+ * @dtp: pointer to the beginning of the struct area to modify
+ * insert %OF_DT_NOP into the dt_struct @dtp to make it v16 from v1, 2, or 3.
+ */
+static int nop_to_v16(u32 *dtp)
+{
+	int nops = 0;
+	char *p, *s;
+	int len;
+	u32 *next;
+
+	while (*dtp != OF_DT_END)
+		switch (*dtp) {
+		case OF_DT_BEGIN_NODE:
+			/* v2 & v3 names are full path, v16+ is relative */
+			p = (char *)(++dtp);
+			len = strlen(p);
+			next = dtp + 1 + len / 4;
+
+			for (s = p + len; *s != '/'; s--)
+				if (s == p)
+					fatal("name %s has no '/'", p);
+
+			len -= s++ - p;		/* not the slash but the nul */
+			memmove(p, s, len);
+			while (len % 4)
+				p[len++] = '\0';
+			dtp += len / 4;
+			while (dtp != next) {
+				*dtp++ = OF_DT_NOP;
+				nops++;
+			}
+			break;
+		case OF_DT_PROP:
+			/* convert from align_8 to align_4 via prefixing nop */
+			len = dtp[1];
+			if ((len >= 8) && !((long)dtp & 4)) {
+				memmove(dtp+1, dtp, 12);
+				*dtp++ = OF_DT_NOP;
+				nops++;
+			}
+			dtp += 3 + (len + 3)/4;
+			break;
+		default:
+			fatal("%s: unrecognised tag %d at %p\n", __FUNCTION__,
+				*dtp, dtp);
+		case OF_DT_NOP:
+			nops ++;
+			/* fall through */
+		case OF_DT_END_NODE:
+			dtp ++;
+			break;
+		}
+	return nops;
+}
+
+#if MIN_VERSION < 3 || OUT_VERSION > 16
+/**
+ * move_nops_fwd - move nops in a v16 dt_struct to the beginning
+ * @start - device tree starting address
+ * @count - number of %OF_DT_NOP cells to move
+ */
+static void move_nops_fwd(u32 *start, int count)
+{
+	u32 *dtp = start;
+	int len;
+	while (count)
+		switch (*dtp) {
+		case OF_DT_NOP:
+			memmove(start+1,start,(dtp-start) * 4);
+			*start++ = OF_DT_NOP;
+			dtp++;
+			count--;
+			break;
+		case OF_DT_END_NODE:
+			dtp++;
+			break;
+		case OF_DT_BEGIN_NODE:
+			len = strlen((char *)(++dtp));
+			dtp += 1 + len / 4;
+			break;
+		case OF_DT_PROP:
+			len = dtp[1];
+			dtp += 3 + (len + 3) / 4;
+			break;
+		case OF_DT_END:
+			fatal("Not enough nops -- need %d more\n", count);
+			return;
+		default:
+			fatal("%s: unknown tag %d at %p", __FUNCTION__, *dtp, dtp)
+		}
+}
+#endif
+
+/**
+ * conv_flattree_inplace upgrade the version of a boot_param_header
+ * @tree: pointer to the device tree header to convert
+ *
+ * Converts a v1, 2, 3 device tree (of at least MIN_VERSION)
+ * in place to OUT_VERSION (16) format, usable by flatdevtree.c
+ */
+void conv_flattree_inplace(struct boot_param_header *tree)
+{
+	u32 *dtp;
+	u32 need = 0, nops;
+	int slen;
+
+	if (tree->magic != OF_DT_HEADER)
+		fatal("%s: no magic", __FUNCTION__);
+
+	if (tree->last_comp_version > 3)
+		return;			/* don't know what to do */
+
+	if (tree->version < MIN_VERSION) {
+		printf("%s: Warning: can't handle version %d tree\n",
+				__FUNCTION__, tree->version);
+		return;
+	}
+
+	if (tree->version < 2)
+		need++;		/* boot_cpu_id */
+
+	if (tree->version < 3)
+		need++;		/* dt_string_size */
+
+	if (OUT_VERSION > 16)
+		need++;		/* dt_struct_size */
+
+	dtp = (void *)tree + tree->off_dt_struct;
+
+	slen = check_v123_tree(dtp, (void *)tree + tree->totalsize);
+	if (slen < 0)
+		fatal("device tree check failed\n");
+
+	nops = nop_to_v16(dtp);
+
+	if (need & 1)		/* keep 8 byte alignment of mem reserve */
+		need++;
+
+	if (need > nops)
+		fatal("Didn't find enough space to add new header fields\n\r"
+			"(needed %d found %d tree %p)", need, nops, tree);
+
+	/* ok now compress the dtb struct */
+	move_nops_fwd(dtp, need);
+	dtp += need;
+
+	/*
+	 * move mem_rsvmap and dt_strings if they are before dt_struct
+	 * onto our nops .  Adjust start addresses for the 3 sections.
+	 */
+	if ((tree->off_mem_rsvmap < tree->off_dt_struct) ||
+		(tree->off_dt_strings < tree->off_dt_struct)) {
+		int start, end;
+		void *ptr;
+
+		if (tree->off_mem_rsvmap < tree->off_dt_strings)
+			start = tree->off_mem_rsvmap;
+		else
+			start = tree->off_dt_strings;
+
+		end = tree->off_dt_struct;
+		ptr = (void *)tree + start;
+
+		memmove(ptr + 4 * need, ptr, end - start);
+
+		if (tree->off_mem_rsvmap < tree->off_dt_struct)
+			tree->off_mem_rsvmap += 4 * need;
+		if (tree->off_dt_strings < tree->off_dt_struct)
+			tree->off_dt_strings += 4 * need;
+	}
+	tree->off_dt_struct += 4 * need;
+
+	/* ok now we have space to extend the header. */
+	if (tree->version < 2 && MIN_VERSION < 2) {
+		tree->boot_cpuid_phys = 0;	/* default, caller can fix */
+	}
+
+	/* calculate size of dt_strings_size */
+	if (tree->version < 3 && MIN_VERSION < 3) {
+		int end = tree->totalsize;
+
+		if (tree->off_dt_strings < tree->off_mem_rsvmap)
+			end = tree->off_mem_rsvmap;
+
+		if ((tree->off_dt_strings < tree->off_dt_struct) &&
+				(end > tree->off_dt_struct))
+			end = tree->off_dt_struct;
+
+		tree->dt_strings_size = end - tree->off_dt_strings;
+	}
+
+#if OUT_VERSION > 16
+	tree->dt_struct_size = 4 * slen;
+#endif
+
+	tree->version = OUT_VERSION;
+	tree->last_comp_version = OUT_COMPAT;
+
+	return;
+}
arch/powerpc/boot/Makefile +1 -1
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -42,7 +42,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
 	$(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
-		marshal.c memranges.c kexec.c \
+		flatdevtree_conv.c marshal.c memranges.c kexec.c \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
 		gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
 		44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
arch/powerpc/boot/ops.h +3 -0
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -80,7 +80,10 @@ struct loader_info {
 };
 extern struct loader_info loader_info;
 
+struct boot_param_header;
+
 void start(void);
+void conv_flattree_inplace(struct boot_param_header *tree);
 int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device);
 int serial_console_init(void);
 int ns16550_console_init(void *devp, struct serial_console_data *scdp);
arch/powerpc/boot/kexec.c +1 -0
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -162,6 +162,7 @@ void kexec_platform_init(struct boot_par
 	move_slaves_up();
 
 	setup_initial_heap();
+	conv_flattree_inplace(dt_blob);
 	early_scan_flat_tree(dt_blob);
 
 	/* drivers can malloc and read the tree, but not realloc later
#17 lore raw

PATCH 9/15 bootwrapper: rtas support

In reply to Milton Miller

This code provides a console write that calls put-term-char.

To avoid PIC relocation of the absolute rtas addresses, hide the actual call to rtas in assembly and declare all variables as int.

An instantiated rtas will be protected by a reserved range in the device tree, so no explicit call to add_occupied_range is needed here.

---
Removed use_call_rtas(); instead put asm() at the file level.

arch/powerpc/boot/rtas.c new file +146 -0
Index: work.git/arch/powerpc/boot/rtas.c
===================================================================
@@ -0,0 +1,146 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright 2007 IBM Corporation.
+ *
+ * Authors: Milton Miller <[email protected]>
+ *
+ */
+
+#include "ops.h"
+#include "stdio.h"
+#include "flatdevtree.h"
+
+static int rtas_entry;
+static int rtas_base;
+static int rtas_size;
+
+static void find_rtas(void)
+{
+	int rc;
+	void *devp;
+	char *str;
+
+	devp = finddevice("/rtas");
+	if (! devp)
+		return;
+
+	str = "linux,rtas-entry";
+	rc = getprop(devp, str, &rtas_entry, sizeof(rtas_entry));
+	if (rc < 0)
+		return;
+	if (rc != sizeof(rtas_entry))
+		goto fail;
+
+	str = "rtas-size";
+	rc = getprop(devp, str, &rtas_size, sizeof(rtas_size));
+	if (rc < 0)
+		return;
+	if (rc != sizeof(rtas_size))
+		goto fail;
+
+	str = "linux,rtas-base";
+	rc = getprop(devp, str, &rtas_base, sizeof(rtas_base));
+	if (rc < 0) {
+		printf("rtas-size but no linux,rtas-base in /rtas.  "
+			"disabling wrapper rtas interface\n\r");
+		rtas_entry = 0;
+		return;
+	}
+
+	if (rc != sizeof(rtas_base))
+		goto fail;
+
+	return;
+
+
+fail:
+	printf("Unexpected length %d of %s property in /rtas.\n\r"
+			"disabling wrapper rtas interface\n\r", rc, str);
+	rtas_entry = 0;
+	return;
+}
+
+/*
+ * PIC relocation of function pointers happens at call time.
+ * We have an absolute out-of-image address.   So tell C they
+ * are just integers, and hide the call as an out-of-file
+ * function.
+ */
+__asm__ (
+		"	.globl call_rtas\n"
+		"	call_rtas: mtctr 5\n"
+		"	bctr\n"
+		);
+
+void call_rtas(int args[], int base, int entry);
+
+
+static int put_term_char;
+
+static void rtas_put_term_write(const char *buf, int len)
+{
+	int i, args[5];
+
+	args[0] = put_term_char;
+	args[1] = 1;	/* num inputs */
+	args[2] = 1;	/* num outputs */
+
+	for (i=0; i < len; ) {
+		args[3] = buf[i];
+		args[4] = 0;
+
+		call_rtas(args, rtas_base, rtas_entry);
+		if (args[4] == 0)		/* SUCCESS */
+			i++;
+		else if (args[4] == -1)		/* HARDWARE_ERROR */
+			break;
+		/* else retry */
+	}
+}
+
+int rtas_console_init(void)
+{
+	void *devp;
+	int rc;
+
+
+	devp = finddevice("/rtas");
+	if (!devp)
+		return -1;
+
+	if (!rtas_entry)
+		find_rtas();
+	if (!rtas_entry)
+		return -1;
+
+	rc = getprop(devp, "put-term-char", &put_term_char,
+			sizeof(put_term_char));
+	if (rc == sizeof(put_term_char))
+		console_ops.write = rtas_put_term_write;
+	else
+		put_term_char = -1;
+
+	return put_term_char == -1 ? -1 : 0;
+}
+
+/* for debug, hard code */
+void use_rtas_console(int entry, int base, int tc)
+{
+	rtas_entry = entry;
+	rtas_base = base;
+	put_term_char = tc;
+	console_ops.write = rtas_put_term_write;
+}
arch/powerpc/boot/ops.h +2 -0
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -94,6 +94,8 @@ void flush_cache(void *, unsigned long);
 int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size);
 int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr);
 void kexec_platform_init(struct boot_param_header *dt_blob);
+int rtas_console_init(void);
+void use_rtas_console(int entry, int base, int tc);
 
 /* marshal slave cpus around to kernel */
 void move_slaves_up(void);
arch/powerpc/boot/kexec.c +3 -0
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -33,6 +33,9 @@ static void find_console_from_tree(void)
 {
 	int rc = -1;
 
+	rc = rtas_console_init();
+	if (!rc)
+		return;
 	if (rc) {
 		/* no console, oh well */
 	}
arch/powerpc/boot/Makefile +1 -1
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -43,7 +43,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
 		flatdevtree_conv.c marshal.c memranges.c kexec.c \
-		ns16550.c serial.c simple_alloc.c div64.S util.S \
+		ns16550.c serial.c simple_alloc.c div64.S util.S rtas.c \
 		gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
 		44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
 src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c crt0_kexec.S \
#18 lore raw

PATCH 10/15 bootwrapper: add cpio file extraction library.

In reply to Milton Miller

Add a library to search through a cpio or initramfs to a specified path contained in a cpio.

---
Status: tested and working.

This file is designed to also be usable in a stand alone user space application.

arch/powerpc/boot/cpio.c new file +306 -0
Index: work.git/arch/powerpc/boot/cpio.c
===================================================================
@@ -0,0 +1,306 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright 2007 IBM Corporation.
+ *
+ * Authors: Milton Miller <[email protected]>
+ *
+ */
+
+#include "cpio.h"
+#include "string.h"
+
+struct cpio_header {
+	char ino[8];
+	char mode[8];
+	char uid[8];
+	char gid[8];
+	char nlink[8];
+	char mtime[8];
+	char filesize[8];
+	char maj[8];
+	char min[8];
+	char rmaj[8];
+	char rmin[8];
+	char namesize[8];
+	char chksum[8];
+} cpio_header_buf;
+
+static int check_magic(char magic[6])
+{
+	return !memcmp(magic,"070701",6) || !memcmp(magic,"070702",6);
+}
+
+static int read_magic(struct gunzip_state *stream)
+{
+	int len;
+	char magic[6];
+
+	len = gunzip_partial(stream, magic, sizeof(magic));
+	if (len == 0)
+		return 0;
+
+	if (len == sizeof(magic) && check_magic(magic))
+		return len;
+
+
+	/* Not the right magic or short read.  We might have stumbled
+	 * onto a compressed archive immediately following an
+	 * uncompressed one, or just some NUL bytes at the end of the
+	 * archive.  Inform the higher layers by the negative length.
+	 */
+	return -len;
+}
+
+static int get_cpio_header(struct gunzip_state *stream)
+{
+	int len;
+
+	len = read_magic(stream);
+	if (len <= 0)
+		return len;
+
+	gunzip_exactly(stream, &cpio_header_buf, sizeof(cpio_header_buf));
+	len += sizeof(cpio_header_buf);
+
+	return len;
+}
+
+static unsigned int cpio_str_to_num(char hexascii[8])
+{
+	unsigned int num = 0;
+	char c;
+	int d;
+
+	for (d=0; d < 8; d++) {
+		c = hexascii[d];
+		num <<= 4;
+		if (c >= '0' && c <= '9') {
+			num += c - '0';
+		} else if (c >= 'A' && c <= 'F') {
+			num += c - 'A' + 10;
+		} else if (c >= 'a' && c <= 'f') {
+			num += c - 'a' + 10;
+		} else {
+			cpio_error("bad cpio archive header: "
+					"invalid a hex digit");
+		}
+	}
+
+	return num;
+}
+
+static char name_buf[MAX_PATH+1];
+static const char cpio_end[] = "TRAILER!!!";
+#define CPIO_END_LEN sizeof(cpio_end)
+
+/* check_next_file
+ * Look for @path in @stream.  Set @consumed to the number of bytes
+ * succesfully read and processed.  return 1 on match, 0 for discarding
+ * an unmatched file, -1 on end of archive (either detected trailer or
+ * EOF on stream), or -(1 + bytes read) for a short read or bad magic
+ * number.  (For the short or bad read, the consumed is not changed).
+ */
+static int check_next_file(char *path, int pathlen,
+		struct gunzip_state *stream, int *consumed)
+{
+	int len, total, match;
+
+	if (pathlen > MAX_PATH) {
+		cpio_error("path too long to search\n");
+	}
+	total = get_cpio_header(stream);
+	if (total <= 0)
+		return total - 1;
+
+	len = cpio_str_to_num(cpio_header_buf.namesize);
+
+	if (len == pathlen || len == CPIO_END_LEN) {
+		gunzip_exactly(stream, name_buf, len);
+		total += len;
+		match = !strcmp(name_buf, path);
+		if (!match && !cpio_str_to_num(cpio_header_buf.filesize))
+			match = -!strcmp(name_buf, cpio_end);
+	} else {
+		gunzip_discard(stream, len);
+		total += len;
+		name_buf[0] = '\0';
+		match = 0;
+	}
+
+	len = total % 4;
+	if (len) {
+		gunzip_discard(stream, 4 - len);
+		total += 4 - len;
+	}
+
+	if (!match) {
+		len = cpio_str_to_num(cpio_header_buf.filesize);
+		gunzip_discard(stream, len);
+		total += len;
+
+		len = total % 4;
+		if (len) {
+			gunzip_discard(stream, 4 - len);
+			total += 4 - len;
+		}
+	}
+
+	*consumed += total;
+	return match;
+}
+
+static char *this_buf;
+static int this_archive;
+
+/* find_in_cpio.
+ * find a pathname @path in a single cpio archive described by @stream.
+ * Return is the same as check_next_file.
+ */
+int find_in_cpio(char *path, struct gunzip_state *stream)
+{
+	int found;
+	int pathlen = strlen(path) + 1;
+
+	this_archive = 0;
+	do {
+		found = check_next_file(path, pathlen, stream, &this_archive);
+	} while (found == 0);
+
+	return found;
+}
+
+/* find_in_initramfs
+ * Search a initramfs buffer for a given path name.  Returns 0 on
+ * not found, or 1 with @state ready to read the file.
+ *
+ * Note: this returns the first match, not the last.   The kernel
+ * decompressor  effectivly uses the last match.  This code also
+ * doesn't worry about the directories in the path existing.
+ */
+int find_in_initramfs(char *path, char *buf, int len,
+		struct gunzip_state *stream)
+{
+	int found, total = 0;
+	int pathlen = strlen(path) + 1;
+	int *ibuf;
+
+	do {
+		/* get to word boundary, but stop if not NUL */
+		for (; total % 4 && total < len - 4; total++)
+			if (buf[total])
+				break;
+
+		if ((total % 4) == 0) {
+			/* fast forward over NUL words.  */
+			for (ibuf = (int *)&buf[total];
+					total < len - sizeof(*ibuf) && !*ibuf;
+					ibuf++)
+			total += sizeof(*ibuf);
+		}
+
+		/* check remainder of a short archive -- it must all be
+		 * zero as both gzip header and cpio header are bigger
+		 * than this.
+		 */
+		if (total >= len - 4) {
+			for (;total < len; total++) {
+				if (buf[len]) {
+					cpio_error("Junk at end of buffer");
+				}
+			}
+			break;
+		}  else if (total % 4) {
+			/*
+			 * If we are unalinged and not at the end of the buffer
+			 * we must be following a compressed archive.  Only
+			 * NUL and gzip headers are allowed.  We skipped NUL.
+			 */
+			if (!(buf[total] == 0x1b && buf[total + 1] == 0x8b))
+				cpio_error("unalinged junk in buffer");
+		}
+
+		this_buf = buf + total;
+		this_archive = 0;
+
+		gunzip_start(stream, this_buf, len - total);
+
+		do {
+			found = check_next_file(path, pathlen, stream,
+					&this_archive);
+		} while (!found);
+
+		if (found > 0)
+			return found;
+
+		if (stream->s.workspace) {
+			int discard;
+
+			if (found < -1) {
+				cpio_error("Junk in compressed archive");
+			}
+
+			/* we either found EOF or TRAILER!!!.  In the later
+			 * case we need to discard to the end of the gzip
+			 * contents.
+			 */
+			do {
+				discard =  gunzip_partial(stream, name_buf,
+					MAX_PATH);
+				this_archive += discard;
+			} while (discard);
+
+			/*
+			 * Peek at how many input bytes were consumed.
+			 * reset our consumed input.
+			 */
+			total += stream->s.total_in + 8;
+
+			/* clean up zlib */
+			discard = gunzip_finish(stream, name_buf, 0);
+		} else {
+			if (this_archive % 4) {
+				cpio_error("Archive not multiple of 4?");
+			}
+			total += this_archive;
+
+			if (!this_archive) {
+				cpio_error("junk between archives");
+			}
+			/* don't check the < -1 of found, it might be an
+			 * archive.  This will be caught by the !this_archive
+			 * check on the next loop.
+			 */
+		}
+	} while (total < len);
+
+	return 0;
+}
+
+void get_cpio_info(void **archive_start, int *consumed)
+{
+	*archive_start = this_buf;
+	*consumed = this_archive;
+}
+
+int get_cpio_file_mode(void)
+{
+	return cpio_str_to_num(cpio_header_buf.mode);
+}
+
+int get_cpio_file_size(void)
+{
+	return cpio_str_to_num(cpio_header_buf.filesize);
+}
arch/powerpc/boot/cpio.h new file +12 -0
Index: work.git/arch/powerpc/boot/cpio.h
===================================================================
@@ -0,0 +1,12 @@
+#include "gunzip_util.h"
+
+extern int find_in_cpio(char *path, struct gunzip_state *stream);
+extern int find_in_initramfs(char *path, char *buf, int len,
+		struct gunzip_state *state);
+extern void get_cpio_info(void **archive_start, int *consumed);
+extern int get_cpio_file_size(void);
+extern int get_cpio_file_mode(void);
+
+#define MAX_PATH 256
+
+extern void cpio_error(char *msg);
arch/powerpc/boot/Makefile +2 -2
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -36,13 +36,13 @@ $(obj)/ebony.o: BOOTCFLAGS += -mcpu=440
 
 zlib       := inffast.c inflate.c inftrees.c
 zlibheader := inffast.h inffixed.h inflate.h inftrees.h infutil.h
-zliblinuxheader := zlib.h zconf.h zutil.h
+zliblinuxheader := zlib.h zconf.h zutil.h stat.h
 
 $(addprefix $(obj)/,$(zlib) gunzip_util.o main.o): \
 	$(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
-		flatdevtree_conv.c marshal.c memranges.c kexec.c \
+		flatdevtree_conv.c marshal.c memranges.c kexec.c cpio.c \
 		ns16550.c serial.c simple_alloc.c div64.S util.S rtas.c \
 		gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
 		44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
#19 lore raw

PATCH 11/15 bootwrapper: allow vmlinuz to be an external payload

In reply to Milton Miller

Allow the boot wrapper to obtain the vmlinux elf from an external platform supplied location instead of embedding it at link time.

When used with the cpio library, the kernel can be stored as a file an file in the initramfs, allowing the same (compressed) copy of the vmlinux elf to be used for both the kernel to execute and any user space tools such as kexec (for reboot), crash dump, or oprofile.

Another use would be to uncompress directly from a memory mapped region such as a flash partition.

---
Check for no find_vmlinuz method before calling it.

The source and source len varables are passed to the routine only to allow them to be printed later.

This patch follows the current policy of using linker attached data in preference to platform supplied data (matching the initrd and command line).

While the platform could supply archive-start, length, and offset, in loader data, it would require decompressing the skiped portion twice when the offset is not known in advance.

The prpmc2800 code shows that this hook to find the kernel and the the code to uncompress the header may want to be separated from the vmlinux alloc, decompress, and flush code. I haven't explored an actual split.

arch/powerpc/boot/main.c +12 -3
Index: work.git/arch/powerpc/boot/main.c
===================================================================
@@ -47,10 +47,19 @@ static struct addr_range prep_kernel(voi
 	struct elf_info ei;
 	int len;
 
-	/* gunzip the ELF header of the kernel */
-	gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
-	gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
+	/* Initialze zlib.  Any attached kernel overrides find_vmlinuz */
+	if (vmlinuz_size)
+		gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
+	else
+		if (platform_ops.find_vmlinuz)
+			platform_ops.find_vmlinuz(&gzstate, &vmlinuz_addr,
+					&vmlinuz_size);
+		else
+			fatal("Can't find a kernel to boot: no attached "
+				"vmlinuz and no find_vmlinuz method\n\r");
 
+	/* gunzip and parse the ELF header of the kernel */
+	gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
 	if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei))
 		fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
 
arch/powerpc/boot/ops.h +4 -0
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -24,6 +24,8 @@ typedef void (*kernel_entry_t)(unsigned 
 
 struct boot_param_header;
 
+struct gunzip_state;
+
 /* Platform specific operations */
 struct platform_ops {
 	void	(*fixups)(void);
@@ -33,6 +35,8 @@ struct platform_ops {
 	void *	(*realloc)(void *ptr, unsigned long size);
 	void	(*exit)(void);
 	void *	(*vmlinux_alloc)(unsigned long size);
+ 	void	(*find_vmlinuz)(struct gunzip_state *, void **srcp,
+ 			unsigned long *lenp);
 };
 extern struct platform_ops platform_ops;
 
#21 lore raw

PATCH 12/15 bootwrapper: extract the vmlinux from initramfs

In reply to Milton Miller

Teach the kexec platform to find the vmlinux from the initramfs.

The implementation searches the initramfs for a file specified by the boot-file property in chosen.

---
Status: working.

arch/powerpc/boot/kexec.c +48 -0
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -23,6 +23,8 @@
 #include "flatdevtree.h"
 #include "page.h"
 #include "types.h"
+#include "cpio.h"
+#include "stat.h"
 
 extern char _start[];
 extern char _end[];
@@ -92,6 +94,51 @@ static void find_rmo_end(void)
 	rmo_end = (void *)*rp;
 }
 
+void find_vmlinux_in_initramfs(struct gunzip_state *state,
+		void **srcp, unsigned long *lenp)
+{
+	void *devp;
+	int rc;
+	char path[MAX_PATH];
+
+	if (!loader_info.initrd_size)
+		dt_find_initrd();
+	if (!loader_info.initrd_size)
+		fatal("find_vmlinux: no initramfs");
+	devp = finddevice("/chosen");
+	if (!devp)
+		fatal("find_vmlinux: no /chosen to find vmlinux");
+	rc = getprop(devp, "boot-file", path, sizeof(path));
+	if (rc < 0)
+		fatal("find_vmlinux: no boot-file property in /chosen")
+	else if (rc == 0 || rc > MAX_PATH)
+		fatal("boot-file too long in /chosen")
+
+	rc = find_in_initramfs(path, (void *)loader_info.initrd_addr,
+			loader_info.initrd_size, state);
+	if (!rc)
+		fatal("find_vmlinux: couldn't find boot-file %s in initramfs",
+				path);
+
+	rc = get_cpio_file_mode();
+	if (!S_ISREG(rc))
+		fatal("find_vmlinux: boot-file %s is not a regular file",
+				path)
+
+	get_cpio_info(srcp, &rc);
+	*lenp = get_cpio_file_size();
+}
+
+void cpio_error(char *msg)
+{
+	void *srcp;
+	int read;
+
+	get_cpio_info(&srcp, &read);
+
+	fatal("cpio_error at %p + 0x%x(%d): %s", srcp, read, read, msg);
+}
+
 /**
  * setup_initial_heap - setup a small heap in the bss
  * Using a preallocated heap, setup for scanning the device tree.
@@ -178,6 +225,7 @@ void kexec_platform_init(struct boot_par
 	ranges_init_malloc();
 	init_flat_tree(dt_blob);
 
+	platform_ops.find_vmlinuz = find_vmlinux_in_initramfs;
 	platform_ops.vmlinux_alloc = kexec_vmlinux_alloc;
 	platform_ops.fixups = kexec_fixups;
 	finalize_chain = dt_ops.finalize;
#22 lore raw

PATCH 13/15 bootwrapper: attach an empty vmlinux

In reply to Milton Miller

Allow the boot wrapper code to be linked without an attached vmlinux.

Rather than invent a new syntax to invoke the wrapper, attach the stripped version of empty.o, which produces the same result.

This new intermediary is called zBoot.

---
Initial attempts to remove the dependency on vmlinux in the arch Makefile have lead to multiple make commands competing in the boot directory and destroying each other.

arch/powerpc/boot/Makefile +7 -2
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -124,7 +124,8 @@ endif
 # args (to if_changed): 1 = (this rule), 2 = platform, 3 = dts 4=dtb 5=initrd
 quiet_cmd_wrap	= WRAP    $@
       cmd_wrap	=$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \
-		$(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux
+		$(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) \
+		$(if $6,$6,vmlinux)
 
 kexec-$(CONFIG_PPC32)			+= zImage.kexec
 kexec-$(CONFIG_PPC64)			+= zImage.kexec64
@@ -142,6 +143,7 @@ image-$(CONFIG_PPC_PRPMC2800)		+= zImage
 image-$(CONFIG_PPC_ISERIES)		+= zImage.iseries
 image-$(CONFIG_DEFAULT_UIMAGE)		+= uImage
 image-$(CONFIG_KEXEC)			+= $(kexec-y)
+image-$(CONFIG_KEXEC)			+= $(patsubst zImage%,zBoot%,$(kexec-y))
 
 ifneq ($(CONFIG_DEVICE_TREE),"")
 image-$(CONFIG_PPC_83xx)		+= cuImage.83xx
@@ -190,6 +192,9 @@ $(obj)/vmlinux.strip: vmlinux
 $(obj)/zImage.iseries: vmlinux
 	$(STRIP) -s -R .comment $< -o $@
 
+$(obj)/zBoot.%: $(wrapperbits)
+	$(call if_changed,wrap,$*,,,,$(obj)/empty.o)
+
 $(obj)/zImage.ps3: vmlinux  $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts
 	$(STRIP) -s -R .comment $< -o vmlinux.strip
 	$(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,)
@@ -227,7 +232,7 @@ clean-files += $(image-) $(initrd-) zIma
 	treeImage.* $(kexec-)
 
 # clean up files cached by wrapper
-clean-kernel := vmlinux.strip vmlinux.bin
+clean-kernel := vmlinux.strip vmlinux.bin empty.o.bin
 clean-kernel += $(addsuffix .gz,$(clean-kernel))
 # If not absolute clean-files are relative to $(obj).
 clean-files += $(addprefix $(objtree)/, $(clean-kernel))
arch/powerpc/boot/wrapper +7 -0
Index: work.git/arch/powerpc/boot/wrapper
===================================================================
@@ -161,6 +161,13 @@ ps3)
     ;;
 esac
 
+if [ "$kernel" = $object/empty.o ] ; then
+    ext=bin
+    objflags="-O binary"
+    gzip=
+fi
+
+
 vmz="$tmpdir/`basename \"$kernel\"`.$ext"
 if [ -z "$cacheit" -o ! -f "$vmz$gzip" -o "$vmz$gzip" -ot "$kernel" ]; then
     ${CROSS}objcopy $objflags "$kernel" "$vmz.$$"
#23 lore raw

PATCH 14/15 boot: add a hook to start cpus

In reply to Milton Miller

The kexec code is doing strange contortions with dtops.finalize and
platform_ops.vmlinux_alloc to manage the slave cpus. Add a hook
with the needed information.

---

arch/powerpc/boot/kexec.c +2 -24
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -180,32 +180,11 @@ static void init_flat_tree(struct boot_p
 		fatal("Unable to initialize device_tree library!\n\r");
 }
 
-static void *saved_vmlinux_addr;
-
-static void *kexec_vmlinux_alloc(unsigned long size)
-{
-	void *addr;
-
-	addr = ranges_vmlinux_alloc(size);
-
-	saved_vmlinux_addr = addr;
-	return addr;
-}
-
 static void kexec_fixups(void)
 {
 	wait_slaves_moved();
 }
 
-static unsigned long (*finalize_chain)(void);
-
-static unsigned long kexec_finalize(void)
-{
-	send_slaves_to_kernel(saved_vmlinux_addr);
-
-	return finalize_chain();
-}
-
 void kexec_platform_init(struct boot_param_header *dt_blob)
 {
 	slaves_are_low();
@@ -226,8 +205,7 @@ void kexec_platform_init(struct boot_par
 	init_flat_tree(dt_blob);
 
 	platform_ops.find_vmlinuz = find_vmlinux_in_initramfs;
-	platform_ops.vmlinux_alloc = kexec_vmlinux_alloc;
+	platform_ops.vmlinux_alloc = ranges_vmlinux_alloc;
 	platform_ops.fixups = kexec_fixups;
-	finalize_chain = dt_ops.finalize;
-	dt_ops.finalize = kexec_finalize;
+	platform_ops.start_smp = send_slaves_to_kernel;
 }
arch/powerpc/boot/main.c +3 -0
Index: work.git/arch/powerpc/boot/main.c
===================================================================
@@ -212,6 +212,9 @@ void start(void)
 	else
 		printf(" using OF tree (promptr=%p)\n\r", loader_info.promptr);
 
+	if (platform_ops.start_smp)
+		platform_ops.start_smp(vmlinux.addr);
+
 	if (console_ops.close)
 		console_ops.close();
 
arch/powerpc/boot/ops.h +1 -0
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -35,6 +35,7 @@ struct platform_ops {
 	void *	(*realloc)(void *ptr, unsigned long size);
 	void	(*exit)(void);
 	void *	(*vmlinux_alloc)(unsigned long size);
+	void	(*start_smp)(void *vmlinux);
  	void	(*find_vmlinuz)(struct gunzip_state *, void **srcp,
  			unsigned long *lenp);
 };
#24 lore raw

PATCH 15/15 bootwrapper: example sreset marshalling

In reply to Milton Miller

An example using the marshalling code that can be entered by sreset. By linking the marshalling code is at 0 and differentiating a cpu id from a device tree, it also works for kexec.

---
For reference only, not intended to be merged.

arch/powerpc/boot/crt0_sample.S new file +73 -0
Index: work.git/arch/powerpc/boot/crt0_sample.S
===================================================================
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2006-2007 Milton Miller, 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+	/*
+	 * This can be either a kexec started image or a sreset initiated
+	 * one.  kexec-tools purgatory is suppposed to copy from entry
+	 * point, but instead copies from image start, so put marshal_low
+	 * at address 0.
+	 */
+
+	.globl _zimage_start
+_zimage_start:
+#include "marshal_low.S"
+
+	.org 0x100
+	bl	get_cpu_id
+1:	mflr	0
+	lis	r4,1b@ha
+	addi	r4,r4,1b@l
+	subf	r0,r4,r0
+
+	lis	r4,elected_master@ha
+	addi	r4,r4,elected_master@l
+	add	r4,r4,r0
+2:	lwarx	r6,r0,r4
+	cmpwi	r6,0
+	bge	3f
+	stwcx.	r3,r0,r4
+	bne-	2b
+	lwz	r6,0(r4)
+
+3:	lis	r4,cpus_found@ha
+	addi	r4,r4,cpus_found@l
+	add	r4,r4,r0
+4:	lwarx	r12,r0,r4
+	addi	r12,r12,1
+	stwcx.	r12,r0,r4
+	bne-	4b
+
+	cmpw	r6,r3
+	bne	slave
+
+	mr	r4,r0
+	li	r5,0
+	b	master
+
+
+	.globl	_zimage_start_plat
+_zimage_start_plat:
+	b	_zimage_start_lib
+
+	.weak	get_cpu_id
+get_cpu_id:
+
+get_pir:
+	mfspr	r3,1023	/* SPRN_PIR */
+	blr
+
+	.balign 8
+	.globl elected_master
+elected_master:
+	.long -1
+	.globl cpus_expected
+cpus_expected:
+	.long 8
+	.globl cpus_found
+cpus_found:
+	.long 0
arch/powerpc/boot/sample.c new file +62 -0

62 lines not on this page · raw message

arch/powerpc/boot/Makefile +2 -2
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -48,7 +48,7 @@ src-wlib := string.S crt0.S stdio.c main
 		44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
 src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c crt0_kexec.S \
 		cuboot-ebony.c treeboot-ebony.c prpmc2800.c \
-		ps3-head.S ps3-hvcall.S ps3.c
+		ps3-head.S ps3-hvcall.S ps3.c crt0_sample.S sample.c
 src-boot := $(src-wlib) $(src-plat) empty.c
 
 src-boot := $(addprefix $(obj)/, $(src-boot))
@@ -128,7 +128,7 @@ quiet_cmd_wrap	= WRAP    $@
 		$(if $6,$6,vmlinux)
 
 kexec-$(CONFIG_PPC32)			+= zImage.kexec
-kexec-$(CONFIG_PPC64)			+= zImage.kexec64
+kexec-$(CONFIG_PPC64)			+= zImage.kexec64 zImage.sample
 
 image-$(CONFIG_PPC_PSERIES)		+= zImage.pseries
 image-$(CONFIG_PPC_MAPLE)		+= zImage.pseries
arch/powerpc/boot/wrapper +3 -0
Index: work.git/arch/powerpc/boot/wrapper
===================================================================
@@ -139,6 +139,9 @@ kexec)
 kexec64)
     platformo="-e _zimage_start_64 $object/crt0_kexec.o"
     ;;
+sample)
+    platformo="$object/crt0_sample.o $object/sample.o"
+    ;;
 miboot|uboot)
     # miboot and U-boot want just the bare bits, not an ELF binary
     ext=bin
#13 lore raw

Re: [PATCH 6/15] bootwrapper: switch 64 bit cpus to 32 bit mode

In reply to Milton Miller

arch/powerpc/boot/crt0.S line 41

Quoted 9 lines
arch/powerpc/boot/crt0.S +9 -0
+	/* Check if the processor is running in 32 bit mode, using
+	 * only 32 bit instructions which should be safe on 32 and
+	 * 64 bit processors.
+	 *
+	 * Subtract the bottom 32 bits of MSR from the full value
+	 * recording the result.  Since MSR[SF] is in the high word,
+	 * the result will be not-equal iff in 32 bit mode (either
+	 * the processor is a 32 bit processor or MSR[SF] = 0).
+	 */

The other way around -- the processor is 64-bit and MSR[SF]=1.

Great trick btw! :-)

arch/powerpc/boot/crt0.S line 45

Quoted 4 lines
arch/powerpc/boot/crt0.S +4 -0
+	mfmsr	r0		/* grab whole msr		*/
+	rlwinm	r8,r0,0,0,31	/* extract bottom word		*/
+	subf.	r8,r8,r0	/* subtract, same?		*/
+	beq	0f		/* yes: we are 32 bit mode	*/

The code is fine though.

Segher

#20 lore raw

Re: [PATCH 11/15] bootwrapper: allow vmlinuz to be an external payload

In reply to Milton Miller
Quoted 7 lines

Allow the boot wrapper to obtain the vmlinux elf from an external platform supplied location instead of embedding it at link time.

When used with the cpio library, the kernel can be stored as a file an file in the initramfs, allowing the same (compressed) copy of the vmlinux elf to be used for both the kernel to execute and any user space tools such as kexec (for reboot), crash dump, or oprofile.

Awesome!

Segher

#15 lore raw

Re: [PATCH 7/15] bootwrapper: Add kexec callable zImage wrapper

In reply to Milton Miller
Quoted 3 lines

The memory node off the root with a name starting with "memory" must contain enough free memory (not in the reserved ranges) in the first reg range to uncompress the the kernel with padding.

There can be many nodes called "/memory". You probably want to use the node pointed to by /chosen/memory instead?

Segher

Acked-by on v1 1/15
#2 lore raw

Re: [PATCH 1/15] boot: find initrd location from device-tree

In reply to Milton Miller
Quoted 5 lines · Milton Miller

Some platforms have a boot agent that can create or modify properties in the device-tree and load images into memory. Provide a helper to set loader_info used by prep_initrd().

Looks reasonable.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#4 lore raw

Re: [PATCH 2/15] boot: record header bytes in gunzip_start

In reply to Milton Miller
Quoted 6 lines · Milton Miller

Record the number of header bytes skipped in the total bytes read field.

This is needed for the initramfs parsing code to find the end of the zip file.

Ok... I assume you've checked that this is actually the correct semantics for that field of the zlib structure?

Quoted 17 lines

Index: work.git/arch/powerpc/boot/gunzip_util.c
===================================================================

arch/powerpc/boot/gunzip_util.c +1 -0
@@ -78,6 +78,7 @@ void gunzip_start(struct gunzip_state *s
 			fatal("inflateInit2 returned %d\n\r", r);
 	}
 
+	state->s.total_in = hdrlen;
 	state->s.next_in = src + hdrlen;
 	state->s.avail_in = srclen - hdrlen;
 }

_______________________________________________
Linuxppc-dev mailing list
[email protected]
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#7 lore raw

Re: [PATCH 3/15] boot: simplfy gunzip_finish

In reply to Milton Miller
Quoted 6 lines · Milton Miller

Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.

Hrm... I guess this is sufficient. Since we'll shortly be blowing away the state anyway, the lack of call to inflateEnd doesn't really matter. Originally I was going to make this function call fatal() if the destination didn't have enough space to contain the decompressed tail of the image, but obviously I decided aginst that.

But, if you're going to do this, you might as well get rid of gunzip_finish() entirely, and have the callers using gunzip_partial instead.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#8 lore raw

Re: [PATCH 3/15] boot: simplfy gunzip_finish

Quoted 14 lines · David Gibson
Quoted 6 lines · Milton Miller

Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.

Hrm... I guess this is sufficient. Since we'll shortly be blowing away the state anyway, the lack of call to inflateEnd doesn't really matter. Originally I was going to make this function call fatal() if the destination didn't have enough space to contain the decompressed tail of the image, but obviously I decided aginst that.

Quoted 3 lines

But, if you're going to do this, you might as well get rid of gunzip_finish() entirely, and have the callers using gunzip_partial instead.

Huh? I stilll call inflateEnd if s.workspace is not NULL, I just don't duplicate gunzip_partial in gunzip_finish. Calling inflateEnd will mean a future call would return an error in the compressed case instead of the behavior in the non-compressed case that changes with this patch from get same data again to get further data that may exist after the patch.

Yes, one can argue that we don't need to call inflateEnd, but I didn't make that change in this patch.

Or were you suggesting that we really don't need the finish function?

milton

Acked-by
#9 lore raw

Re: [PATCH 3/15] boot: simplfy gunzip_finish

Quoted 17 lines · Milton Miller
Quoted 14 lines · David Gibson
Quoted 6 lines · Milton Miller

Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.

Hrm... I guess this is sufficient. Since we'll shortly be blowing away the state anyway, the lack of call to inflateEnd doesn't really matter. Originally I was going to make this function call fatal() if the destination didn't have enough space to contain the decompressed tail of the image, but obviously I decided aginst that.

Quoted 6 lines
Quoted 3 lines

But, if you're going to do this, you might as well get rid of gunzip_finish() entirely, and have the callers using gunzip_partial instead.

Huh? I stilll call inflateEnd if s.workspace is not NULL, I just
don't duplicate gunzip_partial in gunzip_finish. Calling inflateEnd

Oh, yes, oops. Missed the couple of lines there that didn't have a '-' in front.

Quoted 9 lines

will mean a future call would return an error in the compressed case instead of the behavior in the non-compressed case that changes with this patch from get same data again to get further data that may exist after the patch.

Yes, one can argue that we don't need to call inflateEnd, but I didn't make that change in this patch.

Or were you suggesting that we really don't need the finish function?

Well, we probably don't. But for the time being.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#5 lore raw

Re: [PATCH 2/15] boot: record header bytes in gunzip_start

In reply to David Gibson
Quoted 10 lines · David Gibson
Quoted 6 lines · Milton Miller

Record the number of header bytes skipped in the total bytes read field.

This is needed for the initramfs parsing code to find the end of the zip file.

Ok... I assume you've checked that this is actually the correct semantics for that field of the zlib structure?

From looking at the comments in include/linux/zlib.h, I think its consistent. It talks about looking at that field for statistics and for the application to record how many bytes were read during compression. It mentions it brifely in the inflateSync to show how many bytes have been processed. So I say its consistent to say we have processed the header bytes in the input file.

It appears to be for recording the number of bytes processed over all iterations.

I dig into zlib to see if this matches the beahvior of decompressing a zlib header, but expect it would match.

milton