[patch 30/33] PS3: Bootwrapper support.
Milton Miller
miltonm at bga.com
Tue Jun 19 00:20:10 EST 2007
On Sat Jun 16 08:07:06 EST 2007, Geoff Levand wrote:
> zImage.ps3 is a wrapped image that contains a flat device tree, an lv1
> compatible entry point, a wrapper program with a standard entry point,
> and an optional initrd. otheros.bld is the gzip compresed rom image
> built
> from zImage.ps3. otheros.bld is suitable for programming into the PS3
> boot
> flash memory.
>
> Also remove the now unneeded lmb calls in the platform code.
>
> Signed-off-by: Geoff Levand <geoffrey.levand at am.sony.com>
> ---
> arch/powerpc/boot/Makefile | 21 ++--
> arch/powerpc/boot/ps3-head.S | 84 ++++++++++++++++
> arch/powerpc/boot/ps3-hvcall.S | 184
> +++++++++++++++++++++++++++++++++++++
> arch/powerpc/boot/ps3.c | 158
> +++++++++++++++++++++++++++++++
> arch/powerpc/boot/wrapper | 55 +++++++++++
> arch/powerpc/boot/zImage.ps3.lds.S | 50 ++++++++++
> arch/powerpc/platforms/ps3/mm.c | 2
> 7 files changed, 543 insertions(+), 11 deletions(-)
>
> --- a/arch/powerpc/boot/Makefile
> +++ b/arch/powerpc/boot/Makefile
> @@ -41,11 +41,13 @@ zliblinuxheader := zlib.h zconf.h zutil.
> $(addprefix $(obj)/,$(zlib) gunzip_util.o main.o): \
> $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix
> $(obj)/,$(zlibheader))
>
> +src-plat-$(CONFIG_PPC_PS3) += ps3-head.S ps3-hvcall.S ps3.c
> +
> src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c
> flatdevtree_misc.c \
> ns16550.c serial.c simple_alloc.c div64.S util.S \
> gunzip_util.c elf_util.c $(zlib) devtree.c \
> 44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c
> -src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c \
> +src-plat := $(src-plat-y) of.c cuboot-83xx.c cuboot-85xx.c holly.c \
> cuboot-ebony.c treeboot-ebony.c prpmc2800.c
> src-boot := $(src-wlib) $(src-plat) empty.c
The policy so far has been code for all platforms is compiled for all
invocations of make in the boot directory. Therefore just add your
files directly to src-plat. If needed, add a flags override to set the
cpu type.
>
> @@ -75,11 +77,11 @@ $(addprefix $(obj)/,$(zliblinuxheader)):
> $(obj)/empty.c:
> @touch $@
>
> -$(obj)/zImage.lds $(obj)/zImage.coff.lds: $(obj)/%:
> $(srctree)/$(src)/%.S
> +$(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds:
> $(obj)/%: $(srctree)/$(src)/%.S
> @cp $< $@
>
> clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \
> - empty.c zImage.coff.lds zImage.lds
> + empty.c zImage zImage.coff.lds zImage.ps3.lds
> zImage.lds
>
> quiet_cmd_bootcc = BOOTCC $@
> cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c
> -o $@ $<
> @@ -102,7 +104,7 @@ hostprogs-y := addnote addRamDisk hack-c
>
> targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a)
> extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o
> \
> - $(obj)/zImage.lds $(obj)/zImage.coff.lds
> + $(obj)/zImage.lds $(obj)/zImage.coff.lds
> $(obj)/zImage.ps3.lds
>
> wrapper :=$(srctree)/$(src)/wrapper
> wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff
> mktree) \
> @@ -187,11 +189,12 @@ $(obj)/zImage.%: vmlinux $(wrapperbits)
> $(obj)/zImage.iseries: vmlinux
> $(STRIP) -s -R .comment $< -o $@
>
> -$(obj)/zImage.ps3: vmlinux
> - $(STRIP) -s -R .comment $< -o $@
> +$(obj)/zImage.ps3: vmlinux $(wrapper) $(wrapperbits)
> $(srctree)/$(src)/dts/ps3.dts
> + $(STRIP) -s -R .comment $< -o vmlinux.strip
> + $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,)
>
> -$(obj)/zImage.initrd.ps3: vmlinux
> - @echo " WARNING zImage.initrd.ps3 not supported (yet)"
> +$(obj)/zImage.initrd.ps3: vmlinux $(wrapper) $(wrapperbits)
> $(srctree)/$(src)/dts/ps3.dts $(obj)/ramdisk.image.gz
> + $(call
> cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,$(obj)/ramdisk.image.gz)
The separate rule here is to pick up ps3.dts, correct?
After Marc's 3 patch series this would be handled by the config file.
>
> $(obj)/zImage.holly-elf: vmlinux $(wrapperbits)
> $(call if_changed,wrap,holly,$(obj)/dts/holly.dts,,)
> @@ -230,7 +233,7 @@ install: $(CONFIGURE) $(addprefix $(obj)
>
> # anything not in $(targets)
> clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* \
> - treeImage.* zImage.dts zImage.dts_initrd
> + treeImage.* zImage.dts zImage.dts_initrd otheros.bld
>
> # clean up files cached by wrapper
> clean-kernel := vmlinux.strip vmlinux.bin
> --- /dev/null
> +++ b/arch/powerpc/boot/ps3-head.S
> @@ -0,0 +1,84 @@
> +/*
> + * PS3 bootwrapper entry.
> + *
> + * Copyright (C) 2007 Sony Computer Entertainment Inc.
> + * Copyright 2007 Sony Corp.
> + *
> + * This program is free software; you can redistribute it and/or
> modify
> + * it under the terms of the GNU General Public License as published
> by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> 02111-1307 USA
> + */
> +
> +#include "ppc_asm.h"
> +
> + .text
> +
> +/*
> + * __system_reset_overlay - The PS3 first stage entry.
> + *
> + * The bootwraper build script copies the 0x100 bytes at symbol
> + * __system_reset_overlay to offset 0x100 of the rom image.
> + */
> +
> + .globl __system_reset_overlay
> +__system_reset_overlay:
> +
> + /* Switch to 32-bit mode. */
> +
> + mfmsr r9
> + clrldi r9,r9,1
> + mtmsrd r9
> + nop
> +
> + /* Get thread number in r3 and branch. */
> +
> + mfspr r3, 0x88
> + cntlzw. r3, r3
> + li r4, 0
> + li r5, 0
> + beq 1f
> +
> + /* Secondary goes to __secondary_hold in kernel. */
> +
> + li r4, 0x60
> + mtctr r4
> + bctr
> +
> + /* Primary waits for __secondary_hold_acknowledge. */
> +1:
> + li r5, 0x10 /* __secondary_hold_acknowledge */
> + or 28, 28, 28 /* db8cyc */
> +
> + ld r4, 0(r5)
> + cmpdi r4, 0
> + beq 1b
(1) The address of __secondary_hold_acknowledge is not stable. It has
changed in the past and may change in the future. (2) This works
because you always have exactly 2 cpus, and your secondary cpu is not
id 0.
Because this is part of the kernel source tree, I'm not opposed to this
usage, but I think some checking would be in order. A comment that
this only works for two cpus with the slave id not being 0, and some
check that nm on the kernel image results in the expected symbol and
value.
Since I'm here, I'll mention that the li to r5 doesn't need to be in
the loop. In fact, the load to r4 could be expressed as 0x10(0) or
better create a symbol and use secondary_ack at l(0) eliminating the need
for r5 and easing the above check.
> +
> + /* Primary goes to _zimage_start in wrapper. */
> +
> + lis r4, _zimage_start at ha
> + addi r4, r4, _zimage_start at l
> + mtctr r4
> + bctr
> +
> +/*
> + * __system_reset_kernel - Place holder for the kernel reset vector.
> + *
> + * The bootwrapper build script copies 0x100 bytes from offset 0x100
> + * of the rom image to the symbol __system_reset_kernel. At runtime
> + * the bootwrapper program copies the 0x100 bytes at
> __system_reset_kernel
> + * to ram address 0x100. This symbol must occupy 0x100 bytes.
> + */
> +
> + .globl __system_reset_kernel
> +__system_reset_kernel:
> +
> + . = __system_reset_kernel + 0x100
Not bad. Since this code doesn't have to be at any particular
location when it is built (you have no .org or similar, its all PIC), I
don't see anything that couldn't be done from an asm block in the
platform .c file, like the entry point in prpmc2800.c file; that would
save a file in the boot directory. In that case __system_reset_kernel
would be an array of 256 bytes.
[removed ps3-hvcall.S and lmb hack removal from followup]
> --- /dev/null
> +++ b/arch/powerpc/boot/ps3.c
> @@ -0,0 +1,158 @@
> +/*
> + * PS3 bootwrapper support.
> + *
> + * Copyright (C) 2007 Sony Computer Entertainment Inc.
> + * Copyright 2007 Sony Corp.
> + *
> + * This program is free software; you can redistribute it and/or
> modify
> + * it under the terms of the GNU General Public License as published
> by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> 02111-1307 USA
> + */
> +
> +#include <stdarg.h>
> +#include <stddef.h>
> +#include "types.h"
> +#include "elf.h"
> +#include "string.h"
> +#include "stdio.h"
> +#include "page.h"
> +#include "ops.h"
> +
> +extern s64 lv1_panic(u64 in_1);
> +extern s64 lv1_get_logical_partition_id(u64 *out_1);
> +extern s64 lv1_get_logical_ppe_id(u64 *out_1);
> +extern s64 lv1_get_repository_node_value(u64 in_1, u64 in_2, u64 in_3,
> + u64 in_4, u64 in_5, u64 *out_1, u64 *out_2);
> +
> +#ifdef DEBUG
> +#define DBG(fmt...) printf(fmt)
> +#else
> +static inline int __attribute__ ((format (printf, 1, 2))) DBG(
> + const char *fmt, ...) {return 0;}
> +#endif
This should be in stdio.h for all. Maybe ops, but that is a bit of a
catch-all and printf is not there.
> +
> +BSS_STACK(4096);
> +
> +/* A buffer that may be edited by tools operating on a zImage binary
> so as to
> + * edit the command line passed to vmlinux (by setting
> /chosen/bootargs).
> + * The buffer is put in it's own section so that tools may locate it
> easier.
> + */
> +static char cmdline[COMMAND_LINE_SIZE]
> + __attribute__((__section__("__builtin_cmdline")));
> +
> +static void prep_cmdline(void *chosen)
> +{
> + if (cmdline[0] == '\0')
> + getprop(chosen, "bootargs", cmdline,
> COMMAND_LINE_SIZE-1);
> + else
> + setprop_str(chosen, "bootargs", cmdline);
> +
> + printf("cmdline: '%s'\n", cmdline);
> +}
> +
> +static void ps3_console_write(const char *buf, int len)
> +{
> +}
I'm guessing your testing was done with some non-empty function that
you are not prepared to release. Otherwise you would not have added
all the printf code. I'm not complaining, just noting for my
observations below.
> +
> +static void ps3_exit(void)
> +{
> + printf("ps3_exit\n");
> + lv1_panic(0); /* zero = no reboot */
> + while (1);
> +}
Does the hypervisor give some kind of indication that the code paniced?
A comment telling us users what to expect might be nice.
> +
> +static int ps3_repository_read_rm_size(u64 *rm_size)
> +{
> + s64 result;
> + u64 lpar_id;
> + u64 ppe_id;
> + u64 v2;
> +
> + result = lv1_get_logical_partition_id(&lpar_id);
> +
> + if (result)
> + return -1;
> +
> + result = lv1_get_logical_ppe_id(&ppe_id);
> +
> + if (result)
> + return -1;
> +
> + /*
> + * n1: 0000000062690000 : ....bi..
> + * n2: 7075000000000000 : pu......
> + * n3: 0000000000000001 : ........
> + * n4: 726d5f73697a6500 : rm_size.
> + */
> +
> + result = lv1_get_repository_node_value(lpar_id,
> 0x0000000062690000ULL,
> + 0x7075000000000000ULL, ppe_id, 0x726d5f73697a6500ULL,
> rm_size,
> + &v2);
> +
> + printf("%s:%d: ppe_id %lu \n", __func__, __LINE__,
> + (unsigned long)ppe_id);
> + printf("%s:%d: lpar_id %lu \n", __func__, __LINE__,
> + (unsigned long)lpar_id);
> + printf("%s:%d: rm_size %llxh \n", __func__, __LINE__,
> *rm_size);
> +
> + return result ? -1 : 0;
> +}
> +
> +void ps3_copy_vectors(void)
> +{
> + extern char __system_reset_kernel[];
> +
> + memcpy((void *)0x100, __system_reset_kernel, 0x100);
> + flush_cache((void *)0x100, 0x100);
> +}
> +
> +void platform_init(void)
> +{
> + extern char _end[];
> + extern char _dtb_start[];
> + extern char _initrd_start[];
> + extern char _initrd_end[];
> + const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */
Ok your heap is from (wrapper) _end to 16MiB.
> + void *chosen;
> + unsigned long ft_addr;
> + u64 rm_size;
> +
> + console_ops.write = ps3_console_write;
> + platform_ops.exit = ps3_exit;
> +
> + printf("\n-- PS3 bootwrapper --\n");
> +
> + simple_alloc_init(_end, heapsize, 32, 64);
> + ft_init(_dtb_start, 0, 4);
Ok this is the end of platform_init for most platforms.
> +
> + chosen = finddevice("/chosen");
> +
> + ps3_repository_read_rm_size(&rm_size);
> + dt_fixup_memory(0, rm_size);
this is dt_fixups.
> +
> + if (_initrd_end > _initrd_start) {
> + setprop_val(chosen, "linux,initrd-start",
> (u32)(_initrd_start));
> + setprop_val(chosen, "linux,initrd-end",
> (u32)(_initrd_end));
> + }
This is part of prep_initrd.
> +
> + prep_cmdline(chosen);
> +
> + ft_addr = dt_ops.finalize();
> +
> + ps3_copy_vectors();
> +
> + printf(" flat tree at 0x%lx\n\r", ft_addr);
> +
> + ((kernel_entry_t)0)(ft_addr, 0, NULL);
> +
> + ps3_exit();
> +}
Based on past comments, I belive that you are not calling the normal
_start() specifically to avoid dragging in zlib. Oh, but you call
prep_cmdline, which means you still get main.c and it drags zlib. I
guess we should move prep_initrd and prep_cmdline. Were you just
skipping prep_kernel?
This code and the concept in your .lds file looks like it could be used
by other platforms in a generic way
(except for the call to ps3_exit which should be exit, and the call to
ps3_copy_vectors, which can be moved earlier. While its likely similar
functionality would be needed, the details of what to patch and copy
may vary).
> --- a/arch/powerpc/boot/wrapper
> +++ b/arch/powerpc/boot/wrapper
> @@ -144,6 +144,15 @@ miboot|uboot)
> cuboot*)
> gzip=
> ;;
> +ps3)
> + platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o"
> + lds=$object/zImage.ps3.lds
> + gzip=
> + ext=bin
> + objflags="-O binary"
> + ksection=.kernel:vmlinux.bin
> + isection=.kernel:initrd
> + ;;
> esac
>
> vmz="$tmpdir/`basename \"$kernel\"`.$ext"
> @@ -239,4 +248,50 @@ treeboot*)
> fi
> exit 0
> ;;
> +ps3)
> + # The ps3's loader supports loading gzipped binary images from
> flash
> + # rom to addr zero. The loader enters the image at addr 0x100. A
> + # bootwrapper overlay is use to arrange for the kernel to be
> loaded
> + # to addr zero and to have a suitable bootwrapper entry at 0x100.
> + # To construct the rom image, 0x100 bytes from offset 0x100 in the
> + # kernel is copied to the bootwrapper symbol
> __system_reset_kernel.
> + # The 0x100 bytes at the bootwrapper symbol
> __system_reset_overlay is
> + # then copied to offset 0x100. At runtime the bootwrapper program
> + # copies the 0x100 bytes at __system_reset_kernel to addr 0x100.
> +
> + system_reset_overlay=0x`${CROSS}nm "$ofile" \
> + | grep ' __system_reset_overlay$' \
> + | cut -d' ' -f1`
> + system_reset_overlay=`printf "%d" $system_reset_overlay`
> + system_reset_kernel=0x`${CROSS}nm "$ofile" \
> + | grep ' __system_reset_kernel$' \
> + | cut -d' ' -f1`
> + system_reset_kernel=`printf "%d" $system_reset_kernel`
> + overlay_dest="256"
> + overlay_size="256"
> +
> + rm -f "$object/otheros.bld"
> +
> + ${CROSS}objcopy -O binary "$ofile" "$ofile.bin"
> +
> + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \
> + skip=$overlay_dest seek=$system_reset_kernel \
> + count=$overlay_size bs=1 2>&1)
> +
> + if [ $? -ne "0" ]; then
> + echo $msg
> + exit 1
> + fi
> +
> + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \
> + skip=$system_reset_overlay seek=$overlay_dest \
> + count=$overlay_size bs=1 2>&1)
> +
> + if [ $? -ne "0" ]; then
> + echo $msg
> + exit 2
> + fi
> +
> + gzip --force -9 --stdout "$ofile.bin" > "$object/otheros.bld"
> + ;;
> esac
> --- /dev/null
> +++ b/arch/powerpc/boot/zImage.ps3.lds.S
> @@ -0,0 +1,50 @@
> +OUTPUT_ARCH(powerpc:common)
> +ENTRY(_zimage_start)
> +EXTERN(_zimage_start)
> +SECTIONS
> +{
> + _vmlinux_start = .;
> + .kernel:vmlinux.bin : { *(.kernel:vmlinux.bin) }
> + _vmlinux_end = .;
> +
> + . = ALIGN(8);
> + _dtb_start = .;
> + .kernel:dtb : { *(.kernel:dtb) }
> + _dtb_end = .;
> +
> + . = ALIGN(4096);
> + _initrd_start = .;
> + .kernel:initrd : { *(.kernel:initrd) }
> + _initrd_end = .;
> +
> + _start = .;
> + .text :
> + {
> + *(.text)
> + *(.fixup)
> + }
> + _etext = .;
> + . = ALIGN(4096);
> + .data :
> + {
> + *(.rodata*)
> + *(.data*)
> + *(.sdata*)
> + __got2_start = .;
> + *(.got2)
> + __got2_end = .;
> + }
> +
> + . = ALIGN(4096);
> + _edata = .;
> +
> + . = ALIGN(4096);
> + __bss_start = .;
> + .bss :
> + {
> + *(.sbss)
> + *(.bss)
> + }
> + . = ALIGN(4096);
> + _end = . ;
> +}
You don't seem to relocate the initrd. Your lds file has the initrd on
the 4k page boundary after the dtb, which is after the kernel as copied
by objcopy -O binary. As far as I know, objcopy only copies the load
sections; it doesn't create space in the output binary image for alloc
only sections. What prevents the initrd from being in the kernel bss,
which it clears before even looking at the device tree? For that
matter, what prevents the device tree from being allocated in this bss
range?
I'm guessing that you tested with some additional code to display the
console output, which made _end be above the kernel _end, or close
enough that the finalized bss was above the kernel _end? Did you test
an initrd? I guess an attached initrd would also solve the problem of
the final dtb being overwritten, but not the start of the initrd.
I think your approach to the image layout has promise but still needs
some refinement. To solve the problem of where is the kernel end, I
would create a section to hold the kernel elf header and use the
elf_parse routines that are now conveniently in a separate file. The
wrapper script would dd off the header and then it would be linked into
the wrapper like the other sections. At that point the initrd could be
moved before creating the malloc pool above it.
This approach can be used whenever there is something capable of
installing an image at address 0 in ram before starting its execution,
and is useful when the time to install that image is less than the time
to decompress the kernel (and copy either it or the zImage, for the
case where the entry point is fixed in low memory). I can think of
other platforms where this holds true. In your case, this is true
because the hypervisor decompresses the image.
What should this type of image be called? Its not really a zImage,
since there is nothing compressed (well, maybe the initrd/initramfs).
uImage is already taken (u would have stood for uncompressed).
Something like wrapped image? flat image? combined image? We'd still
do make zImage to invoke make for it.
One more point. You indicated that this image had a standard entry
point. Were you thinking it will be used for kexec? Or were you just
trying to state you used the library _zimage_start? While the current
kexec-tools for 64-bit assumes the entry point to be image offset 0,
the same address is used for both the boot cpu entry point and the
address of the slave code to copy to address 0. If you want the master
to execute the wrapper code, you will need to patch the branch at
address 0 in the kernel image to go to _zimage_start in the wrapper,
and then from the wrapper patch it back before starting the kernel. Or
perhaps you don't plan to run the wrapper on the kexec path? In that
case the system reset handler would retained the patched handler and
the initrd ignored would be ignored. While you usage may not care,
other platforms use the system reset vector. In either case,
_zimage_start doesn't have the necessary slave code, and therefore
shouldn't be the entry point for the final image when we fix that bug
in kexec-tools.
milton
More information about the Linuxppc-dev
mailing list