powerpc zImage kexec
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]>
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 ; }
PATCH 1/1 ppc64: correct @ha relocation
In reply to Milton MillerThe @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_ehbreak ;case R_PPC64_ADDR16_HA : - *(uint16_t *)location = ((value>>16 ) &0xffff ); + *(uint16_t *)location = (((value+0x8000 )>>16 ) &0xffff );break ;case R_PPC64_ADDR16_HIGHEST :
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
0x8000stolen from the linux kernel filearch/powerpc/kernel/module_64.cwhich 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/
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
0x8000stolen from the linux kernel filearch/powerpc/kernel/module_64.cwhich 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/
Re: [PATCH kexec-tools] ppc64: correct @ha relocation
In reply to Milton MillerQuoted 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
0x8000stolen from the linux kernel filearch/powerpc/kernel/module_64.cwhich credits binutils.Signed-off-by Milton Miller <[email protected]>
Cross checked with PPC64 ELF ABI also.
Quoted 17 lines
---
This relocation is used inv2wrap.Sto 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_ehbreak ;case R_PPC64_ADDR16_HA : - *(uint16_t *)location = ((value>>16 ) &0xffff ); + *(uint16_t *)location = (((value+0x8000 )>>16 ) &0xffff );break ;case R_PPC64_ADDR16_HIGHEST :
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
0x8000stolen from the linux kernel filearch/powerpc/kernel/module_64.cwhich credits binutils.
Quoted 6 lines
Quoted 2 lines
Signed-off-by Milton Miller <[email protected]>Acked-by Mohan Kumar M <[email protected]>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/
PATCH 1/1 ppc64: use kernels slave loop for purgatory
In reply to Milton MillerPurgatory 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 **argvunsigned 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 **argvelf_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 */
Simon Horman
AppliedRe: [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.
Signed-off-by Milton Miller <[email protected]>
Thanks, applied.
Signature · 3 lines
Horms H: http://www.vergenet.net/~horms/ W: http://www.valinux.co.jp/en/
PATCH 1/1 export retained initrd in debugfs
In reply to Milton MillerExport 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
@@ -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
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_startandinitrd_endare 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).
Signed-off-by Milton Miller <[email protected]>---
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
PATCH 1/1 kexec: send slaves to new kernel earlier
In reply to Milton MillerCopy 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
@@ -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
PATCH 1/8 boot: more verbose gunzip error message
In reply to Milton MillerChange 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
@@ -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); }/**
Re: [PATCH 1/8] boot: more verbose gunzip error message
Quoted 7 lines · Milton Miller
Change the error message in
gunzip_exactlyto 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.
Signed-off-by Milton Miller <[email protected]>
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
PATCH 2/8 bootwrapper: smp support code
In reply to Milton MillerAdd 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
@@ -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
@@ -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
@@ -19,4 +19,9 @@ static inline u32 mfpvr(void) registervoid *__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
@@ -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) {
PATCH 3/8 bootwrapper: occuppied memory ranges
In reply to Milton MillerHere 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
@@ -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
@@ -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 *vmlinuxvoid 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
@@ -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
@@ -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 ;
PATCH 4/8 bootwrapper: help for 64 bit cpus
In reply to Milton MillerA 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
@@ -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
@@ -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
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
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 popI'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.
PATCH 5/8 bootwrapper: Add kexec callable zImage wrapper
In reply to Milton MillerThis 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
@@ -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 = ®[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
@@ -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
@@ -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
@@ -83,6 +83,7 @@ int ns16550_console_init(void *devp, strvoid *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 );
PATCH 6/8 bootwrapper: convert flatdevtree to version 16
In reply to Milton Millerkexec-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
@@ -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
@@ -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
@@ -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
@@ -236,6 +236,7 @@ void kexec_platform_init(struct boot_parmove_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
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_NOPand chomping full path. Make space for new headers by moving and then chomping theOF_DT_NOPs.
Why not fix kexec-tools to generate a version 17 device tree?
-Scott
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_NOPand chomping full path. Make space for new headers by moving and then chomping theOF_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
PATCH 7/8 bootwrapper: rtas support
In reply to Milton MillerThis 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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH 8/8 bootwrapper: example sreset marshalling
In reply to Milton MillerAn 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
@@ -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
@@ -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
@@ -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