Skip to contentSkip to conversation

powerpc zImage kexec

PATCH New Milton Miller 8 participants 25 messages Apr 11, 2007 – Apr 13, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 powerpc zImage kexec

The patches following this message implement support for creating a compressed zImage that can be used where a vmlinux image with a flat device-tree would be. It has been tested with kexec-tools on 64 bit platforms with upto 8 threads, and with an external bootloader. that supplies a device tree and initrd.

The patches are:

(1) a series of 3 patches for kexec-tools
(a) a hack to fix memory corruption in the testing tree
(b) a fix for @ha relocations
(c) a major change to the handling of SMP slaves during purgatory
(2) a related patch for increasing the times for slaves to transiton
(3) a convinence patch to expose a retained initrd for testing kexec-reboot
(4) and a series of 8 patches for the zImage wrapper
(a) a debug message
(b) smp handling code
(c) coarse memory allocation for finding malloc pool, vmlinux, etc.
(d) 64 bit support
(e) kexec entrypoint base support
-- or any environment that can supply a flat tree that fully describes the machine, and wishes to use it to find memory for the machine.
(f) support for converting older versions of the flat device tree
(g) support for writing to an rtas put-term-char console
(h) alternative example code for using a-f on another palatform.

The kernel series is based on the patches I extracted from the for-2.6.22 branch of powerpc.git as well as 1, 3, and 4 from my last series.

I did not include my external payload patch at this point because I still have to debug the initramfs search code, but the plan is to allow the base code to find the vmlinux in a compressed cpio.gz in the initrd area, with the file to decompress being selected by a property in the device-tree.

Because of the vaired nature of the patches, each has its own CC list.

I ask that the non-bootwrapper patchs and the debug message be queued
now. I expect the last one be discussion only, and the others to
provoke discussion.

I have been running a loop on 4 and 8 thread pSeries boxes of continous kexecs for several thousand interations now.

Thanks,
milton

kexec-tools:

	ppc64-miscount-exlude-hack
	ppc64-reloc-ha
	ppc64-steal-kernel-slaves

misc:
initrd-debugfs
kexec-slaves-earlier

Patch: [PATCH 1/4] bootwrapper: missing relocation in crt0.S
http://patchwork.ozlabs.org/linuxppc/patch?id=10219
Message ID <[email protected]>

Patch: Re: Patch: [PATCH 3/4] bootwrapper: no gzip fixes
http://patchwork.ozlabs.org/linuxppc/patch?id=10223
Message ID <[email protected]>

Patch: Re: [PATCH 4/4] bootwrapper: decompress less, check more
http://patchwork.ozlabs.org/linuxppc/patch?id=10232
Message ID <[email protected]>

boot-exact-debug
boot-marshal-smp
boot-memranges
boot-64
boot-kexec
boot-flat-conv-2-16
boot-rtas
boot-bml.patch

Patch: [RFC] bootwrapper: allow vmlinuz to be an external payload
Submitter Milton Miller
Date 2007-03-28 18:21:03
Message ID <[email protected]>

#1 lore raw

PATCH 1/1 malloc corruption hack

On a system with rtas, a hash table, no pci, and one memory node I got the following error:

*** glibc detected *** double free or corruption (out): 0x0000000010115800 ***
kexec-reboot: line 9: 2366 Aborted \
./kexec --mem-max=0x20000000 -l /root/vmlinux --devicetreeblob=dtall.dtb

The problem is count_memory_ranges doesn't count what get_devtree_details will be adding to the exlcude list.

This is a very gross hack to get around the problem, I post it so others might benifit until the real problem is fixed.

However, I would also like to point out that the code is deficent, as it is only looking for the properties in specific directories. It should be looking in all directories for the properties, like the reserve code
building the device tree. TCE (iommu) tables can be attached to any
pci bus. Also, it allows other ranges to be reserved by just changing the device tree, instead of adding new searches to kexec-tools.

---
Thanks to Sonny for getting a debug envrionment laoded to debug this failure.

kexec/arch/ppc64/kexec-ppc64.c renamed from kexec/arch/ppc64/kexec-ppc64.c.orig +2 -0
@@ -124,6 +124,8 @@ static int count_memory_ranges()
 	}
 	closedir(dir);
 
+	max_memory_ranges += 5;	/* base exclude ranges - kernel, rtas, etc */
+
 	return 0;
 }
 
#2 lore raw

PATCH 1/1 ppc64: correct @ha relocation

In reply to Milton Miller

The @ha relocation is supposed to account for the @l relocation being a signed quantity by adding 1 if the msb of @l is set.

The trick of adding 0x8000 stolen from the linux kernel file arch/powerpc/kernel/module_64.c which credits binutils.

---
This relocation is used in v2wrap.S to hold and release the slave cpus. Without this fix the memory location may be 64k off the initialized variable which is checked against 0.

kexec/arch/ppc64/kexec-elf-rel-ppc64.c renamed from kexec/arch/ppc64/kexec-elf-rel-ppc64.c.orig +1 -1
@@ -97,7 +97,7 @@ void machine_apply_elf_rel(struct mem_eh
 		break;
 
 	case R_PPC64_ADDR16_HA:
-		*(uint16_t *)location = ((value>>16)  & 0xffff);
+		*(uint16_t *)location = (((value+0x8000)>>16)  & 0xffff);
 		break;
 
 	case R_PPC64_ADDR16_HIGHEST:
#3 lore raw

Re: [PATCH kexec-tools] ppc64: correct @ha relocation

Quoted 6 lines · Milton Miller

The @ha relocation is supposed to account for the @l relocation being a signed quantity by adding 1 if the msb of @l is set.

The trick of adding 0x8000 stolen from the linux kernel file arch/powerpc/kernel/module_64.c which credits binutils.

This and the following patch seem fine to me.
But I would appreciate a review from someone
a little more ppc savy than I.

Signature · 3 lines
Horms
  H: http://www.vergenet.net/~horms/
  W: http://www.valinux.co.jp/en/
#4 lore raw

Re: [Fastboot] [PATCH kexec-tools] ppc64: correct @ha relocation

Quoted 11 lines · Simon Horman
Quoted 6 lines · Milton Miller

The @ha relocation is supposed to account for the @l relocation being a signed quantity by adding 1 if the msb of @l is set.

The trick of adding 0x8000 stolen from the linux kernel file arch/powerpc/kernel/module_64.c which credits binutils.

This and the following patch seem fine to me.
But I would appreciate a review from someone
a little more ppc savy than I.

Sorry, I should have been a little clearer. I was refering to the two kexec-tools patches only. I'm just getting to the kernel ones.

Signature · 3 lines
Horms
  H: http://www.vergenet.net/~horms/
  W: http://www.valinux.co.jp/en/
Acked-by on v1 1/1
#5 lore raw

Re: [PATCH kexec-tools] ppc64: correct @ha relocation

In reply to Milton Miller
Quoted 9 lines · Milton Miller

The @ha relocation is supposed to account for the @l relocation being a signed quantity by adding 1 if the msb of @l is set.

The trick of adding 0x8000 stolen from the linux kernel file arch/powerpc/kernel/module_64.c which credits binutils.

Cross checked with PPC64 ELF ABI also.

Quoted 17 lines

---
This relocation is used in v2wrap.S to hold and release the slave cpus. Without this fix the memory location may be 64k off the initialized variable which is checked against 0.

kexec/arch/ppc64/kexec-elf-rel-ppc64.c renamed from kexec/arch/ppc64/kexec-elf-rel-ppc64.c.orig +1 -1
@@ -97,7 +97,7 @@ void machine_apply_elf_rel(struct mem_eh
 		break;
 
 	case R_PPC64_ADDR16_HA:
-		*(uint16_t *)location = ((value>>16)  & 0xffff);
+		*(uint16_t *)location = (((value+0x8000)>>16)  & 0xffff);
 		break;
 
 	case R_PPC64_ADDR16_HIGHEST:
#6 lore raw

Re: [PATCH kexec-tools] ppc64: correct @ha relocation

Quoted 8 lines · Mohan Kumar M
Quoted 7 lines · Milton Miller

The @ha relocation is supposed to account for the @l relocation being a signed quantity by adding 1 if the msb of @l is set.

The trick of adding 0x8000 stolen from the linux kernel file arch/powerpc/kernel/module_64.c which credits binutils.

Quoted 6 lines
Quoted 2 lines

Cross checked with PPC64 ELF ABI also.

Thanks Milton. Thanks Mohan. I've applied this to kexec-tools-testing.

Signature · 3 lines
Horms
  H: http://www.vergenet.net/~horms/
  W: http://www.valinux.co.jp/en/
#7 lore raw

PATCH 1/1 ppc64: use kernels slave loop for purgatory

In reply to Milton Miller

Purgatory doesn't really care about the SMP cpus. But if we leave them behind, they end up getting lost when the kernel overwrites purgatory or the previous kernel. The current slave handling in purgatory doesn't have any handshaking to make sure the cpus have moved on before leaving.

Instead of moving the slave cpus up to purgatory and then back down to the kernel, just copy bytes 4-255 from the kernel and use it as the purgatory start / slave hold block.

---
I wrote this while debugging a problem in my zImage.kexec smp code. Since the first instruction is the branch for the master, changing it to another branch should be safe.

We could copy the kernels original branch to 0 before jumping to the kernel for completeness.

We could also just compute the branch for the master instead of reading the code in the original symbol.

purgatory/arch/ppc64/v2wrap.S renamed from purgatory/arch/ppc64/v2wrap.S.orig +17 -44
@@ -45,13 +45,22 @@
 	oris    rn,rn,name##@h;         \
 	ori     rn,rn,name##@l
 
-# look a bit like a Linux kernel here ...
+
 	.machine ppc64
 	.globl purgatory_start
 purgatory_start:	b	master
-	tweq	0,0
+	.org purgatory_start + 0x60     # ABI: slaves start at 60 with r3=phys
+slave:	b $
+	.org purgatory_start + 0x100    # ABI: end of copied region
+	.size purgatory_start, . - purgatory_start
+
+#
+# The above 0x100 bytes at purgatory_start are replaced with the
+# code from the kernel (or next stage) by kexec/arch/ppc64/kexec-ppc64.c
+#
+
 master:
-	or	1,1,1		# low priority to let other thread catchup
+	or	1,1,1		# low priority to let other threads catchup
 	isync
 	mr      17,3            # save cpu id to r17
 	mr      15,4            # save physical address in reg15
@@ -66,25 +75,7 @@ master:
 	bl      .purgatory
 	nop
 
-	b       81f
-	.org purgatory_start + 0x60     # ABI: slaves start at 60 with r3=phys
-slave:
-	# load slave spin code address and branch into that
-	LOADADDR(6,slave_spin)
-	ld      4,0(6)
-	mtctr 4
-	bctr
-
-spin: .long 1
-slave_spin_code:
-	lis     5,spin@ha
-	lwz     5,spin@l(5)
-	cmpwi   0,5,0
-	bne     slave_spin_code
-	ba 0x60
-
-81:				# master continues here
-	or	3,3,3		# ok back to high, lets boot
+	or	3,3,3		# ok now to high priority, lets boot
 	lis	6,0x1
 	mtctr	6		# delay a bit for slaves to catch up
 83:	bdnz	83b		# before we overwrite 0-100 again
@@ -99,30 +90,12 @@ slave_spin_code:
 80:
 	LOADADDR(6,kernel)
 	ld      4,0(6)          # load the kernel address
-
-	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
-	lis     6,spin@ha
-	li      0,0
-	stw     0,spin@l(6)
 	mr      3,16            # restore dt address
 
-	bctr			# start kernel
-
-slave_spin: .llong  slave_spin_code
+	lwz	7,0(4)		# get the first instruction that we stole
+	stw	7,0(0)		# and put it in the slave loop at 0
+				# skip cache flush, do we care?
 
+	bctr			# start kernel
kexec/arch/ppc64/kexec-elf-ppc64.c renamed from kexec/arch/ppc64/kexec-elf-ppc64.c.orig +10 -0
@@ -89,6 +89,7 @@ int elf_ppc64_load(int argc, char **argv
 	unsigned int my_panic_kernel;
 	unsigned long my_stack, my_backup_start;
 	unsigned long toc_addr;
+	unsigned int slave_code[256/sizeof (unsigned int)], master_entry;
 
 #define OPT_APPEND     (OPT_ARCH_MAX+0)
 #define OPT_RAMDISK     (OPT_ARCH_MAX+1)
@@ -281,6 +282,15 @@ int elf_ppc64_load(int argc, char **argv
 	elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
 				sizeof(my_dt_offset));
 
+	/* get slave code from new kernel, put in purgatory */
+	elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code, 
+			sizeof(slave_code));
+	master_entry = slave_code[0];
+	memcpy(slave_code, info->segment[0].buf, sizeof(slave_code));
+	slave_code[0] = master_entry;
+	elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code,
+				sizeof(slave_code));
+
 	if (info->kexec_flags & KEXEC_ON_CRASH) {
 		my_panic_kernel = 1;
 		/* Set panic flag */
Applied
#8 lore raw

Re: [PATCH kexec-tools] ppc64: use kernels slave loop for purgatory

Quoted 6 lines · Milton Miller

Purgatory doesn't really care about the SMP cpus. But if we leave them behind, they end up getting lost when the kernel overwrites purgatory or the previous kernel. The current slave handling in purgatory doesn't have any handshaking to make sure the cpus have moved on before leaving.

Quoted 6 lines

Instead of moving the slave cpus up to purgatory and then back down to the kernel, just copy bytes 4-255 from the kernel and use it as the purgatory start / slave hold block.

Thanks, applied.

Signature · 3 lines
Horms
  H: http://www.vergenet.net/~horms/
  W: http://www.valinux.co.jp/en/
#9 lore raw

PATCH 1/1 export retained initrd in debugfs

In reply to Milton Miller

Export initrd in debugfs when retained.

As a side effect, initrd_start and initrd_end are not cleared when the initrd is retained.

After this patch, one can then copy the initrd space from debugfs and pass it to kexec as the initrd, or do something else with it (maybe it was an initrd not an initramfs).

---
This is a lot more reliable than extracting it via dd from /dev/mem.

Currently debugfs doesn't have a getattr even for blobs, so one has to cp elsewhere before using kexec from kexec-tools.

init/initramfs.c +28 -1
Index: kernel/init/initramfs.c
===================================================================
@@ -6,6 +6,7 @@
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/syscalls.h>
+#include <linux/debugfs.h>
 
 static __initdata char *message;
 static void __init error(char *x)
@@ -519,7 +520,7 @@ static void __init free_initrd(void)
 	unsigned long crashk_end   = (unsigned long)__va(crashk_res.end);
 #endif
 	if (do_retain_initrd)
-		goto skip;
+		return;
 
 #ifdef CONFIG_KEXEC
 	/*
@@ -588,3 +589,29 @@ static int __init populate_rootfs(void)
 	return 0;
 }
 rootfs_initcall(populate_rootfs);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+static struct debugfs_blob_wrapper initrd_blob;
+
+static int __init export_initrd(void)
+{
+	struct dentry *d;
+	initrd_blob.data = (char *)initrd_start;
+	initrd_blob.size = initrd_end - initrd_start;
+
+	if (!do_retain_initrd)
+		return 0;
+
+	if (!initrd_start)
+		return 0;
+
+	d = debugfs_create_blob("initrd", S_IFREG, NULL, &initrd_blob);
+
+	if (!d)
+		return 1;
+
+	return 0;
+}
+
+__initcall(export_initrd);
+#endif
#10 lore raw

Re: [PATCH] export retained initrd in debugfs

This needs to goto lkml

Quoted 72 lines · In message <[email protected]> you wrote:

Export initrd in debugfs when retained.

As a side effect, initrd_start and initrd_end are not cleared when the initrd is retained.

After this patch, one can then copy the initrd space from debugfs and pass it to kexec as the initrd, or do something else with it (maybe it was an initrd not an initramfs).

---
This is a lot more reliable than extracting it via dd from /dev/mem.

Currently debugfs doesn't have a getattr even for blobs, so one has to cp elsewhere before using kexec from kexec-tools.

Index: kernel/init/initramfs.c
===================================================================

init/initramfs.c +28 -1
@@ -6,6 +6,7 @@
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/syscalls.h>
+#include <linux/debugfs.h>
 
 static __initdata char *message;
 static void __init error(char *x)
@@ -519,7 +520,7 @@ static void __init free_initrd(void)
 	unsigned long crashk_end   = (unsigned long)__va(crashk_res.end);
 #endif
 	if (do_retain_initrd)
-		goto skip;
+		return;
 
 #ifdef CONFIG_KEXEC
 	/*
@@ -588,3 +589,29 @@ static int __init populate_rootfs(void)
 	return 0;
 }
 rootfs_initcall(populate_rootfs);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+static struct debugfs_blob_wrapper initrd_blob;
+
+static int __init export_initrd(void)
+{
+	struct dentry *d;
+	initrd_blob.data = (char *)initrd_start;
+	initrd_blob.size = initrd_end - initrd_start;
+
+	if (!do_retain_initrd)
+		return 0;
+
+	if (!initrd_start)
+		return 0;
+
+	d = debugfs_create_blob("initrd", S_IFREG, NULL, &initrd_blob);
+
+	if (!d)
+		return 1;
+
+	return 0;
+}
+
+__initcall(export_initrd);
+#endif

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

#11 lore raw

PATCH 1/1 kexec: send slaves to new kernel earlier

In reply to Milton Miller

Copy the code and start the slaves on their journey to next kernels spin loop as soon as we copy the kexec image into place.

The kernel doesn't know exactly which slaves are spinning in kexec_wait. This allows us to pass more than max-cpus to the next kernel. But it also means that we might leave some behind.

Moving the code here means they have the time it takes us to clear the hash table to wake up and move on. Moving the code any earlier would reuqire walking the image description to search for the code, which could span multiple pages.

---
I applied this change while searching for the cause of lost cpus. The actual
cause was a sequence error causing the slaves to attempt to execute invalid instructions, but it showed the result is bouncing off the kernels ISI handler scribling on low memory.

arch/powerpc/kernel/misc_64.S +13 -13
Index: kernel/arch/powerpc/kernel/misc_64.S
===================================================================
@@ -606,6 +606,19 @@ _GLOBAL(kexec_sequence)
 	/* turn off mmu */
 	bl	real_mode
 
+	/* copy  0x100 bytes starting at start to 0 */
+	li	r3,0
+	mr	r4,r30		/* start, aka phys mem offset */
+	li	r5,0x100
+	li	r6,0
+	bl	.copy_and_flush	/* (dest, src, copy limit, start offset) */
+1:	/* assume normal blr return */
+
+	/* release other cpus to the new kernel secondary start at 0x60 */
+	mflr	r5
+	li	r6,1
+	stw	r6,kexec_flag-1b(5)
+
 	/* clear out hardware hash page table and tlb */
 	ld	r5,0(r27)		/* deref function descriptor */
 	mtctr	r5
@@ -636,19 +649,6 @@ _GLOBAL(kexec_sequence)
  *    are the boot cpu ?????
  *    other device tree differences (prop sizes, va vs pa, etc)...
  */
-
-	/* copy  0x100 bytes starting at start to 0 */
-	li	r3,0
-	mr	r4,r30
-	li	r5,0x100
-	li	r6,0
-	bl	.copy_and_flush	/* (dest, src, copy limit, start offset) */
-1:	/* assume normal blr return */
-
-	/* release other cpus to the new kernel secondary start at 0x60 */
-	mflr	r5
-	li	r6,1
-	stw	r6,kexec_flag-1b(5)
 	mr	r3,r25	# my phys cpu
 	mr	r4,r30	# start, aka phys mem offset
 	mtlr	4
#12 lore raw

PATCH 1/8 boot: more verbose gunzip error message

In reply to Milton Miller

Change the error message in gunzip_exactly to be more verbose.

Besides the identifier being unrelated to the current function name, the user had no indication if the corruption was near the beginning or the end.

arch/powerpc/boot/gunzip_util.c +2 -1
Index: kernel/arch/powerpc/boot/gunzip_util.c
===================================================================
@@ -142,7 +142,8 @@ void gunzip_exactly(struct gunzip_state 
 
 	len  = gunzip_partial(state, dst, dstlen);
 	if (len < dstlen)
-		fatal("gunzip_block: ran out of data\n\r");
+		fatal("\n\rgunzip_exactly: ran out of data!"
+				" Wanted %d, got %d.\n\r", dstlen, len);
 }
 
 /**
Acked-by on v1 1/8
#13 lore raw

Re: [PATCH 1/8] boot: more verbose gunzip error message

Quoted 7 lines · Milton Miller

Change the error message in gunzip_exactly to be more verbose.

Besides the identifier being unrelated to the current function name, the user had no indication if the corruption was near the beginning or the end.

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
#14 lore raw

PATCH 2/8 bootwrapper: smp support code

In reply to Milton Miller

Add 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 because it may need to be at a fixed location or there may be other entry point requirements.

This code supports arbitrary cpu identifiers in the 0-1023 range.

---
I first had move_slaves_up, then added move_to_here to support loading at zero where the bss is overwritten by the kernel. After moving the slave area from the end to the beginning of the decompressed kernel when it was not at 0, I started getting crashes. At that point, I wrote checkout_slaves_to_kernel. Debugging checkout_slaves prompted the kexec-slaves-earlier and kexec-tools purgatory patches.

Note: this works with the 64 bit kernel using head_64.S. The 32 bit
6xx kernel head_32.S needs the 0x60 slave entry point (it currently uses something closer to 0xC0) and kexec slave shutdown.

arch/powerpc/boot/marshal_low.S new file +103 -0
Index: kernel/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 (C) 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 +283 -0

283 lines not on this page · raw message

arch/powerpc/boot/Makefile +1 -0
Index: kernel/arch/powerpc/boot/Makefile
===================================================================
@@ -41,6 +41,7 @@ $(addprefix $(obj)/,$(zlib) main.o): $(a
 		$(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 $(zlib)
 src-plat := of.c
arch/powerpc/boot/reg.h +5 -0
Index: kernel/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: kernel/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 */
 
 /* Platform specific operations */
 struct platform_ops {
@@ -79,7 +80,14 @@ int serial_console_init(void);
 int ns16550_console_init(void *devp, struct serial_console_data *scdp);
 void *simple_alloc_init(char *base, u32 heap_size, u32 granularity,
 		u32 max_allocs);
-extern void flush_cache(void *, unsigned long);
+void flush_cache(void *, unsigned long);
+
+/* 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)
 {
#15 lore raw

PATCH 3/8 bootwrapper: occuppied memory ranges

In reply to Milton Miller

Here is a set of library routines to manage gross memory allocations.

It 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, set rmo_end to the highest address (32 bits) you can
access and wish to manage. Then follow with calls to occupy memory,
followed by calls init_malloc for fine grain allocation.

Also, an optional vmlinux_alloc with hooks into the smp marshaling code.

---

arch/powerpc/boot/memranges.c new file +230 -0
Index: kernel/arch/powerpc/boot/memranges.c
===================================================================
@@ -0,0 +1,230 @@
+/*
+ * 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 (C) 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
+ * 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 = 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
+ * Call add_occupied_range_ulong 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
+ * 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.
+ * Searches for a location to put the kernel.   Ideally at 0, which could
+ * be blocked by a range_add.  If not, check if the kernel @size will
+ * overwrite the image, and if so, tack on space for the slaves.  Find
+ * space either through malloc() or free space between the occupied
+ * ranges, including after the last range to 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: kernel/arch/powerpc/boot/ops.h
===================================================================
@@ -20,6 +20,8 @@
 #define	MAX_PROP_LEN		256 /* What should this be? */
 #define	SMP_SLAVE_SIZE		256 /* Size of SMP slave block, kexec/kernel */
 
+struct boot_param_header;
+
 /* Platform specific operations */
 struct platform_ops {
 	void	(*fixups)(void);
@@ -89,6 +91,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: kernel/arch/powerpc/boot/Makefile
===================================================================
@@ -41,7 +41,7 @@ $(addprefix $(obj)/,$(zlib) main.o): $(a
 		$(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 $(zlib)
 src-plat := of.c
arch/powerpc/boot/types.h +3 -0
Index: kernel/arch/powerpc/boot/types.h
===================================================================
@@ -1,6 +1,9 @@
 #ifndef _TYPES_H_
 #define _TYPES_H_
 
+#define _LIBC_LIMITS_H_		/* don't recurse to system's headers */
+#include <limits.h>		/* MAX_UINT, etc */
+
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
 typedef unsigned char		u8;
#16 lore raw

PATCH 4/8 bootwrapper: help for 64 bit cpus

In reply to Milton Miller

A procedure descriptior for the entry point, and a routine to clear MSR[SF] if we are running in 64 bit mode via mtmsrd before calling _zimage_start_lib.

---

arch/powerpc/boot/misc64.S new file +67 -0
Index: kernel/arch/powerpc/boot/misc64.S
===================================================================
@@ -0,0 +1,67 @@
+/*
+ * 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 (C) 2007 IBM Corporation.
+ *
+ * Authors: Milton Miller <[email protected]>
+ *
+ */
+#include "ppc_asm.h"
+
+
+	.globl _zimage_start_32_64
+_zimage_start_32_64:
+
+	/*
+	 * Run code compiled and linked as ELF32 in 32 bit mode.
+	 *
+	 * 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 bottom 32 bits of MSR from full value recording
+	 * the result.  Since MSR[SF] is in the high word, we will
+	 * be not-equal iff in 32 bit mode (MSR[SF] = 0 or 32 bit
+	 * processor).
+	 */
+	mfmsr	r0		/* grab whole msr		*/
+	rlwinm	r8,r0,0,0,31	/* extract bottom word		*/
+	subf.	r8,r8,r0	/* subtract, same?		*/
+	beq	1f		/* 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
+
+1:	b	_zimage_start_lib
+
+
+	.data
+
+	/* a procedure descriptor used when pretending to be elf64_powerpc */
+	.balign	8
+	.global	_zimage_start64
+_zimage_start64:
+	.long	0, _zimage_start	/* big endian, supported reloc ppc32 */
+	.quad	0, 0, 0
arch/powerpc/boot/Makefile +1 -1
Index: kernel/arch/powerpc/boot/Makefile
===================================================================
@@ -41,7 +41,7 @@ $(addprefix $(obj)/,$(zlib) main.o): $(a
 		$(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 misc64.S \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
 		gunzip_util.c $(zlib)
 src-plat := of.c
#17 lore raw

Re: [PATCH 4/8] bootwrapper: help for 64 bit cpus

arch/powerpc/boot/misc64.S line 55

Quoted 12 lines
arch/powerpc/boot/misc64.S new file +12 -0
+	/* 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

I'm not sure this actually works on all CPUs --
aren't you supposed to use rfid for this?

Segher

#18 lore raw

Re: [PATCH 4/8] bootwrapper: help for 64 bit cpus

Quoted 7 lines · Segher Boessenkool
Quoted 7 lines
arch/powerpc/boot/misc64.S new file +7 -0
+	/* 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"
Quoted 8 lines

arch/powerpc/boot/misc64.S line 55

Quoted 5 lines
arch/powerpc/boot/misc64.S new file +5 -0
+	rldicl	r0,r0,0,1
+	sync
+	mtmsrd	r0
+	isync
+	.machine pop

I'm not sure this actually works on all CPUs --
aren't you supposed to use rfid for this?

Not for 32/64 bits switches afaik... as long as your PC is known to fit in 32 bits, you don't generate an implicit branch.

Ben.

#19 lore raw

PATCH 5/8 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 (instead of a kernel).
This can reduce the memory needed to reboot, or to provide a common image for boot and reboot.

It also provides rules to pretend the binary is a 64-bit elf, with the same calling convention as the 64-bit kernel.

In theory this code should be useable in any envrionment that supplies a flat device tree to describe the usable memory of the machine.

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 kenrel with padding.

---
Status: Successfully boots from and to a 64 bit kernel when loaded at 0 and after the kernel _end, when initrds and various other data reserved are loaded discontigiously above the size of the kernel.

The memory search should be converted to use the address translation framework and find_node_by_type, although we have a limited malloc space during the search. That points out the need for a read-only scan of the tree.

The next patch is needed to call from kexec-tools without supplying an externally generated flat device tree.

kexec.c is added as a library in the belief that it can be used by multiple platforms.

I didn't test any serial drivers in this environment. kexec will clear out the mmu, so relying on initialized translations will fail.

arch/powerpc/boot/kexec.c new file +255 -0
Index: kernel/arch/powerpc/boot/kexec.c
===================================================================
@@ -0,0 +1,255 @@
+/*
+ * 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 (C) 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;
+
+	rc = serial_console_init();
+	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)
+				continue;
+			rp++;
+		}
+		while (--ns) {
+			if (*rp)
+				continue;
+			rp++;
+		}
+	} while (0);
+
+	rmo_end = (void *)*rp;
+}
+
+static void find_dt_initrd(void)
+{
+	int rc;
+	unsigned long long initrd_start, initrd_end;
+	void *devp;
+
+	devp = finddevice("/chosen");
+	if (! devp) {
+		return;
+	}
+
+	/* The properties had to be 8 bytes until 2.6.22  */
+	rc = getprop(devp, "linux,initrd-start", &initrd_start,
+		sizeof(initrd_start));
+	if (rc < 0)
+		return;
+	if (rc == sizeof(unsigned long)) {
+		unsigned long tmp;
+		memcpy(&tmp, &initrd_start, rc);
+		initrd_start = tmp;
+	} else if (rc != sizeof(initrd_start)) {
+		printf("unexpected length of linux,initrd_start in /chosen!\n\r");
+		return;
+	}
+
+	rc = getprop(devp, "linux,initrd-end", &initrd_end, sizeof(initrd_end));
+	if (rc < 0) {
+		printf("chosen has linux,initrd_start but no linux,initrd_end!\n\r");
+		return;
+	}
+	if (rc == sizeof(unsigned long)) {
+		unsigned long tmp;
+		memcpy(&tmp, &initrd_end, rc);
+		initrd_end = tmp;
+	} else if (rc != sizeof(initrd_end)) {
+		printf("unexpected length of linux,initrd_end in /chosen!\n\r");
+		return;
+	}
+
+	if (!initrd_start)
+		return;
+
+	/* if the initrd is above 4G, its untouchable in 32 bit mode */
+	if (initrd_end <= UINT_MAX && initrd_start < initrd_end) {
+		loader_info.initrd_addr = initrd_start;
+		loader_info.initrd_size  = initrd_end - initrd_start;
+	}
+}
+
+/**
+ * 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();
+	find_dt_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 vmsize)
+{
+	unsigned long size = vmsize;
+	void *addr;
+
+	/*
+	 * If we are running where the kernel will decompress itself,
+	 * tack some more space onto the allocations and move the slaves
+	 * there.  This avoids the kernel decompressing before the slaves
+	 * catch on that they should move down to 0x60.
+	 */
+	if (size > (unsigned long)_start)
+		size += SMP_SLAVE_SIZE;
+
+	addr = ranges_vmlinux_alloc(size);
+
+	if (size > vmsize) {
+		if (addr < (void *)_start) {
+			/*
+			 * The kernel will memmove its self down.  The extra
+			 * space is at the end, make sure it is alligned.
+			 * We don't care if the kernel overwrites the first
+			 * instruction, that is the master entry point.
+			 */
+			vmsize &= ~3UL;
+			move_slaves_here(addr + vmsize);
+		} else {
+			/* nice aligned space at the beginning */
+			move_slaves_here(addr);
+			addr += SMP_SLAVE_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 +46 -0

46 lines not on this page · raw message

arch/powerpc/boot/wrapper +9 -0
Index: kernel/arch/powerpc/boot/wrapper
===================================================================
@@ -133,6 +133,12 @@ pmaccoff)
     platformo=$object/of.o
     lds=$object/zImage.coff.lds
     ;;
+kexec)
+    platformo=$object/crt0_kexec.o
+    ;;
+kexec64)
+    platformo="-e _zimage_start64 $object/crt0_kexec.o"
+    ;;
 miboot|uboot)
     # miboot and U-boot want just the bare bits, not an ELF binary
     ext=bin
@@ -216,4 +222,7 @@ pmaccoff)
     ${CROSS}objcopy -O aixcoff-rs6000 --set-start "$entry" "$ofile"
     $object/hack-coff "$ofile"
     ;;
+kexec64)
+    ${CROSS}objcopy -O elf64-powerpc $ofile
+    ;;
 esac
arch/powerpc/boot/Makefile +9 -4
Index: kernel/arch/powerpc/boot/Makefile
===================================================================
@@ -43,10 +43,11 @@ $(addprefix $(obj)/,$(zlib) main.o): $(a
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
 		marshal.c memranges.c misc64.S \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
-		gunzip_util.c $(zlib)
+		gunzip_util.c $(zlib) kexec.c
 src-plat := of.c
-src-boot := $(src-wlib) $(src-plat) empty.c
+src-plat += crt0_kexec.S
 
+src-boot := $(src-wlib) $(src-plat) empty.c
 src-boot := $(addprefix $(obj)/, $(src-boot))
 obj-boot := $(addsuffix .o, $(basename $(src-boot)))
 obj-wlib := $(addsuffix .o, $(basename $(addprefix $(obj)/, $(src-wlib))))
@@ -122,6 +123,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
@@ -131,6 +135,7 @@ image-$(CONFIG_PPC_CHRP)		+= zImage.chrp
 image-$(CONFIG_PPC_EFIKA)		+= zImage.chrp
 image-$(CONFIG_PPC_PMAC)		+= zImage.pmac
 image-$(CONFIG_DEFAULT_UIMAGE)		+= uImage
+image-$(CONFIG_KEXEC)			+= $(kexec-y)
 
 # For 32-bit powermacs, build the COFF and miboot images
 # as well as the ELF images.
@@ -138,7 +143,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%, $(image-y))
 initrd-y := $(filter-out $(image-y), $(initrd-y))
 targets	+= $(image-y) $(initrd-y)
@@ -172,7 +177,7 @@ install: $(CONFIGURE) $(image-y)
 	sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $<
 
 # anything not in $(targets)
-clean-files += $(image-) $(initrd-) zImage zImage.initrd
+clean-files += $(image-) $(initrd-) $(kexec-) zImage zImage.initrd
 
 # clean up files cached by wrapper
 clean-kernel := vmlinux.strip vmlinux.bin
arch/powerpc/boot/ops.h +1 -0
Index: kernel/arch/powerpc/boot/ops.h
===================================================================
@@ -83,6 +83,7 @@ int ns16550_console_init(void *devp, str
 void *simple_alloc_init(char *base, u32 heap_size, u32 granularity,
 		u32 max_allocs);
 void flush_cache(void *, unsigned long);
+void kexec_platform_init(struct boot_param_header *dt_blob);
 
 /* marshal slave cpus around to kernel */
 void move_slaves_up(void);
#20 lore raw

PATCH 6/8 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.

---
Tested on version two and version 3 device-trees. Version 1 code
has not been tested.

arch/powerpc/boot/flatdevtree_conv.c new file +276 -0
Index: kernel/arch/powerpc/boot/flatdevtree_conv.c
===================================================================
@@ -0,0 +1,276 @@
+/*
+ * 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 (C) 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
+ * 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
+ * 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
+ *
+ * converts a v1, 2, 3 device tree (of at least MIN_VERSION)
+ * in place to a v16 one, 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: kernel/arch/powerpc/boot/Makefile
===================================================================
@@ -41,7 +41,7 @@ $(addprefix $(obj)/,$(zlib) main.o): $(a
 		$(addprefix $(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
-		marshal.c memranges.c misc64.S \
+		flatdevtree_conv.c marshal.c memranges.c misc64.S \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
 		gunzip_util.c $(zlib) kexec.c
 src-plat := of.c
arch/powerpc/boot/ops.h +3 -0
Index: kernel/arch/powerpc/boot/ops.h
===================================================================
@@ -76,7 +76,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: kernel/arch/powerpc/boot/kexec.c
===================================================================
@@ -236,6 +236,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
#21 lore raw

Re: [PATCH 6/8] bootwrapper: convert flatdevtree to version 16

Quoted 6 lines · 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.

Why not fix kexec-tools to generate a version 17 device tree?

-Scott

#22 lore raw

Re: [PATCH 6/8] bootwrapper: convert flatdevtree to version 16

Quoted 9 lines · Scott Wood
Quoted 5 lines · 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.

Why not fix kexec-tools to generate a version 17 device tree?

Because that would prevent kexec-tools from starting a kernel between

[PATCH] ppc64: Make early processor spinup based on physical ids
author olof <olof>
Thu, 25 Nov 2004 23:59:21 +0000 (23:59 +0000)
commit e1b47549d1588ccea1fa5726eb430aae4e80f8ed

(linux-bkcvs, 2.6.9 or so) and

[PATCH] flattened device tree changes
author Benjamin Herrenschmidt <[email protected]>
Tue, 9 Aug 2005 08:36:34 +0000 (10:36 +0200)
committer Paul Mackerras <[email protected]>
Mon, 29 Aug 2005 00:53:31 +0000 (10:53 +1000)
commit 34153fa3af45d84f3221d9b67ba2ab7e8a220d28

(2.6.14)

Admittedly they don't all support kexec from them, but they
can be kexeced into. ppc64 kexec support was merged
here:

[PATCH] ppc64: kexec support for ppc64
author R Sharada <[email protected]>
Sat, 25 Jun 2005 21:58:10 +0000 (14:58 -0700)
committer Linus Torvalds <[email protected]>
Sat, 25 Jun 2005 23:24:51 +0000 (16:24 -0700)
commit fce0d5740322b98b863f9e609f5a9bd4c06703af

(2.6.13)

That, and I didn't want to make changes to the code that may be replaced with some other library later.

We could move this from kexec_platform_init to crt0_kexec.S platform_init so it becomes platform dependent if you want to support them.

Are there embedded firmwares that hove old device trees?

milton

#23 lore raw

PATCH 7/8 bootwrapper: rtas support

In reply to Milton Miller

This code provides a console write that calls rtas for the put-term-char service.

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

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

---
I considered making this a client of the serial code, but there are no hooks to init that layer other than through the stdout property pointing to a serial type node with a compatable property, which doesn't match the /rtas node.

arch/powerpc/boot/rtas.c new file +152 -0
Index: kernel/arch/powerpc/boot/rtas.c
===================================================================
@@ -0,0 +1,152 @@
+/*
+ * 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 (C) 2007 IBM Corporation.
+ *
+ * Authors: Milton Miller <[email protected]>
+ *
+ */
+
+#include "ops.h"
+#include "stdio.h"
+#include "flatdevtree.h"
+
+void call_rtas(int args[], int base, int entry);
+
+static void __attribute__((noinline)) use_call_rtas(void)
+{
+	/*
+	 * 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__ __volatile__(
+			"	b 1f \n"
+			"	.globl call_rtas\n"
+			"	call_rtas: mtctr 5\n"
+			"	bctr\n"
+			"1:");
+}
+
+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;
+}
+
+static int put_term_char;
+
+static void rtas_put_term_write(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;
+
+	use_call_rtas();
+
+	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;
+	use_call_rtas();
+	console_ops.write = rtas_put_term_write;
+}
arch/powerpc/boot/ops.h +2 -0
Index: kernel/arch/powerpc/boot/ops.h
===================================================================
@@ -87,6 +87,8 @@ void *simple_alloc_init(char *base, u32 
 		u32 max_allocs);
 void flush_cache(void *, unsigned long);
 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: kernel/arch/powerpc/boot/kexec.c
===================================================================
@@ -33,6 +33,9 @@ static void find_console_from_tree(void)
 {
 	int rc;
 
+	rc = rtas_console_init();
+	if (!rc)
+		return;
 	rc = serial_console_init();
 	if (rc) {
 		/* no console, oh well */
arch/powerpc/boot/Makefile +1 -1
Index: kernel/arch/powerpc/boot/Makefile
===================================================================
@@ -43,7 +43,7 @@ $(addprefix $(obj)/,$(zlib) main.o): $(a
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
 		flatdevtree_conv.c marshal.c memranges.c misc64.S \
 		ns16550.c serial.c simple_alloc.c div64.S util.S \
-		gunzip_util.c $(zlib) kexec.c
+		gunzip_util.c $(zlib) kexec.c rtas.c
 src-plat := of.c
 src-plat += crt0_kexec.S
 
#24 lore raw

PATCH 8/8 bootwrapper: example sreset marshalling

In reply to Milton Miller

An example that uses the marshalling code but expects cpus to be sreset. This reuses the kexec code after finding the device tree (which is magically available at 31K by whatever means loaded this code). With the marshalling code is at 0 in the image and the checks in platform_init, it also works as a kexec target when left as elf.

---
Status: works with sreset on the tested platform when image data loaded at 0 and the device tree loaded at 31MB. Works as kexec called elf image from 64 bit kernel with kexec-supplied device-tree and initrd. The attached device-tree and initrd cases have not been tested.

arch/powerpc/boot/crt0_bml.S new file +73 -0
Index: kernel/arch/powerpc/boot/crt0_bml.S
===================================================================
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 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_32_64
+
+	.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/bml.c new file +42 -0

42 lines not on this page · raw message

arch/powerpc/boot/Makefile +2 -1
Index: kernel/arch/powerpc/boot/Makefile
===================================================================
@@ -46,6 +46,7 @@ src-wlib := string.S crt0.S stdio.c main
 		gunzip_util.c $(zlib) kexec.c rtas.c
 src-plat := of.c
 src-plat += crt0_kexec.S
+src-plat += crt0_bml.S bml.c
 
 src-boot := $(src-wlib) $(src-plat) empty.c
 src-boot := $(addprefix $(obj)/, $(src-boot))
@@ -124,7 +125,7 @@ quiet_cmd_wrap	= WRAP    $@
 		$(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux
 
 kexec-$(CONFIG_PPC32)			+= zImage.kexec
-kexec-$(CONFIG_PPC64)			+= zImage.kexec64
+kexec-$(CONFIG_PPC64)			+= zImage.kexec64 zImage.bml
 
 image-$(CONFIG_PPC_PSERIES)		+= zImage.pseries
 image-$(CONFIG_PPC_MAPLE)		+= zImage.pseries
arch/powerpc/boot/wrapper +3 -0
Index: kernel/arch/powerpc/boot/wrapper
===================================================================
@@ -139,6 +139,9 @@ kexec)
 kexec64)
     platformo="-e _zimage_start64 $object/crt0_kexec.o"
     ;;
+bml)
+    platformo="$object/crt0_bml.o $object/bml.o"
+    ;;
 miboot|uboot)
     # miboot and U-boot want just the bare bits, not an ELF binary
     ext=bin