From ad9d2716cfc1cda5a7e0d7bc0db45e3af8a4adbb Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 5 Mar 2007 14:24:52 +1100 Subject: [POWERPC] zImage: Add more flexible gunzip convenience functions At present, arch/powerpc/boot/main.c includes a gunzip() function which is a convenient wrapper around zlib. However, it doesn't conveniently allow decompressing part of an image to one location, then the remainder to a different address. This patch adds a new set of more flexible convenience wrappers around zlib, moving them to their own file, gunzip_util.c, in the process. These wrappers allow decompressing sections of the compressed image to different locations. In addition, they transparently handle uncompressed data, avoiding special case code to handle uncompressed vmlinux images. The patch also converts main.c to use the new wrappers, using the new flexibility to avoid decompressing the vmlinux's ELF header twice as we did previously. That in turn means we avoid extending our allocations for the vmlinux to allow space for the extra copy of the ELF header. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/main.c | 113 +++++++++-------------------------------------- 1 file changed, 20 insertions(+), 93 deletions(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 6f6b50d238b6..404620a9e733 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -14,8 +14,8 @@ #include "page.h" #include "string.h" #include "stdio.h" -#include "zlib.h" #include "ops.h" +#include "gunzip_util.h" #include "flatdevtree.h" extern void flush_cache(void *, unsigned long); @@ -30,6 +30,8 @@ extern char _initrd_end[]; extern char _dtb_start[]; extern char _dtb_end[]; +static struct gunzip_state gzstate; + struct addr_range { unsigned long addr; unsigned long size; @@ -42,71 +44,12 @@ static struct addr_range initrd; static unsigned long elfoffset; static int is_64bit; -/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */ -static char scratch[46912]; static char elfheader[256]; typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); #undef DEBUG -#define HEAD_CRC 2 -#define EXTRA_FIELD 4 -#define ORIG_NAME 8 -#define COMMENT 0x10 -#define RESERVED 0xe0 - -static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) -{ - z_stream s; - int r, i, flags; - - /* skip header */ - i = 10; - flags = src[3]; - if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) { - printf("bad gzipped data\n\r"); - exit(); - } - if ((flags & EXTRA_FIELD) != 0) - i = 12 + src[10] + (src[11] << 8); - if ((flags & ORIG_NAME) != 0) - while (src[i++] != 0) - ; - if ((flags & COMMENT) != 0) - while (src[i++] != 0) - ; - if ((flags & HEAD_CRC) != 0) - i += 2; - if (i >= *lenp) { - printf("gunzip: ran out of data in header\n\r"); - exit(); - } - - if (zlib_inflate_workspacesize() > sizeof(scratch)) { - printf("gunzip needs more mem\n"); - exit(); - } - memset(&s, 0, sizeof(s)); - s.workspace = scratch; - r = zlib_inflateInit2(&s, -MAX_WBITS); - if (r != Z_OK) { - printf("inflateInit2 returned %d\n\r", r); - exit(); - } - s.next_in = src + i; - s.avail_in = *lenp - i; - s.next_out = dst; - s.avail_out = dstlen; - r = zlib_inflate(&s, Z_FULL_FLUSH); - if (r != Z_OK && r != Z_STREAM_END) { - printf("inflate returned %d msg: %s\n\r", r, s.msg); - exit(); - } - *lenp = s.next_out - (unsigned char *) dst; - zlib_inflateEnd(&s); -} - static int is_elf64(void *hdr) { Elf64_Ehdr *elf64 = hdr; @@ -132,8 +75,8 @@ static int is_elf64(void *hdr) return 0; elfoffset = (unsigned long)elf64ph->p_offset; - vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset; - vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset; + vmlinux.size = (unsigned long)elf64ph->p_filesz; + vmlinux.memsize = (unsigned long)elf64ph->p_memsz; is_64bit = 1; return 1; @@ -164,8 +107,8 @@ static int is_elf32(void *hdr) return 0; elfoffset = elf32ph->p_offset; - vmlinux.size = elf32ph->p_filesz + elf32ph->p_offset; - vmlinux.memsize = elf32ph->p_memsz + elf32ph->p_offset; + vmlinux.size = elf32ph->p_filesz; + vmlinux.memsize = elf32ph->p_memsz; return 1; } @@ -177,13 +120,8 @@ static void prep_kernel(unsigned long a1, unsigned long a2) vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); /* gunzip the ELF header of the kernel */ - if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { - len = vmlinuz.size; - gunzip(elfheader, sizeof(elfheader), - (unsigned char *)vmlinuz.addr, &len); - } else - memcpy(elfheader, (const void *)vmlinuz.addr, - sizeof(elfheader)); + gunzip_start(&gzstate, (void *)vmlinuz.addr, vmlinuz.size); + gunzip_exactly(&gzstate, elfheader, sizeof(elfheader)); if (!is_elf64(elfheader) && !is_elf32(elfheader)) { printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); @@ -192,10 +130,10 @@ static void prep_kernel(unsigned long a1, unsigned long a2) if (platform_ops.image_hdr) platform_ops.image_hdr(elfheader); - /* We need to alloc the memsize plus the file offset since gzip - * will expand the header (file offset), then the kernel, then - * possible rubbish we don't care about. But the kernel bss must - * be claimed (it will be zero'd by the kernel itself) + /* We need to alloc the memsize: gzip will expand the kernel + * text/data, then possible rubbish we don't care about. But + * the kernel bss must be claimed (it will be zero'd by the + * kernel itself) */ printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); vmlinux.addr = (unsigned long)malloc(vmlinux.memsize); @@ -237,24 +175,13 @@ static void prep_kernel(unsigned long a1, unsigned long a2) } /* Eventually gunzip the kernel */ - if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { - printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", - vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); - len = vmlinuz.size; - gunzip((void *)vmlinux.addr, vmlinux.memsize, - (unsigned char *)vmlinuz.addr, &len); - printf("done 0x%lx bytes\n\r", len); - } else { - memmove((void *)vmlinux.addr,(void *)vmlinuz.addr, - vmlinuz.size); - } - - /* Skip over the ELF header */ -#ifdef DEBUG - printf("... skipping 0x%lx bytes of ELF header\n\r", - elfoffset); -#endif - vmlinux.addr += elfoffset; + printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", + vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); + /* discard up to the actual load data */ + gunzip_discard(&gzstate, elfoffset - sizeof(elfheader)); + len = gunzip_finish(&gzstate, (void *)vmlinux.addr, + vmlinux.memsize); + printf("done 0x%lx bytes\n\r", len); flush_cache((void *)vmlinux.addr, vmlinux.size); } -- cgit From 79c8541924a220964f9f2cbed31eaa9fdb042eab Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 5 Mar 2007 14:24:52 +1100 Subject: [POWERPC] zImage: Cleanup and improve prep_kernel() This patch rewrites prep_kernel() in the zImage wrapper code to be clearer and more flexible. Notable changes: - Handling of the initrd image from prep_kernel() has moved into a new prep_initrd() function. - The address of the initrd image is now added as device tree properties, as the kernel expects. - We only copy a packaged initrd image to a new location if it is in danger of being clobbered when the kernel moves to its final location, instead of always. - By default we decompress the kernel directly to address 0, instead of requiring it to relocate itself. Platforms (such as OF) where doing this could clobber still-live firmware data structures can override the vmlinux_alloc hook to provide an alternate place to decompress the kernel. - We no longer pass lots of information between functions in global variables. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/main.c | 167 ++++++++++++++++++++++++++++------------------- arch/powerpc/boot/of.c | 12 ++++ arch/powerpc/boot/ops.h | 1 + 3 files changed, 114 insertions(+), 66 deletions(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 404620a9e733..05de6cfafeeb 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -33,24 +33,21 @@ extern char _dtb_end[]; static struct gunzip_state gzstate; struct addr_range { - unsigned long addr; + void *addr; unsigned long size; - unsigned long memsize; }; -static struct addr_range vmlinux; -static struct addr_range vmlinuz; -static struct addr_range initrd; - -static unsigned long elfoffset; -static int is_64bit; -static char elfheader[256]; +struct elf_info { + unsigned long loadsize; + unsigned long memsize; + unsigned long elfoffset; +}; typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); #undef DEBUG -static int is_elf64(void *hdr) +static int parse_elf64(void *hdr, struct elf_info *info) { Elf64_Ehdr *elf64 = hdr; Elf64_Phdr *elf64ph; @@ -74,15 +71,14 @@ static int is_elf64(void *hdr) if (i >= (unsigned int)elf64->e_phnum) return 0; - elfoffset = (unsigned long)elf64ph->p_offset; - vmlinux.size = (unsigned long)elf64ph->p_filesz; - vmlinux.memsize = (unsigned long)elf64ph->p_memsz; + info->loadsize = (unsigned long)elf64ph->p_filesz; + info->memsize = (unsigned long)elf64ph->p_memsz; + info->elfoffset = (unsigned long)elf64ph->p_offset; - is_64bit = 1; return 1; } -static int is_elf32(void *hdr) +static int parse_elf32(void *hdr, struct elf_info *info) { Elf32_Ehdr *elf32 = hdr; Elf32_Phdr *elf32ph; @@ -98,7 +94,6 @@ static int is_elf32(void *hdr) elf32->e_machine == EM_PPC)) return 0; - elf32 = (Elf32_Ehdr *)elfheader; elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff); for (i = 0; i < elf32->e_phnum; i++, elf32ph++) if (elf32ph->p_type == PT_LOAD) @@ -106,24 +101,26 @@ static int is_elf32(void *hdr) if (i >= elf32->e_phnum) return 0; - elfoffset = elf32ph->p_offset; - vmlinux.size = elf32ph->p_filesz; - vmlinux.memsize = elf32ph->p_memsz; + info->loadsize = elf32ph->p_filesz; + info->memsize = elf32ph->p_memsz; + info->elfoffset = elf32ph->p_offset; return 1; } -static void prep_kernel(unsigned long a1, unsigned long a2) +static struct addr_range prep_kernel(void) { + char elfheader[256]; + void *vmlinuz_addr = _vmlinux_start; + unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start; + void *addr = 0; + struct elf_info ei; int len; - vmlinuz.addr = (unsigned long)_vmlinux_start; - vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); - /* gunzip the ELF header of the kernel */ - gunzip_start(&gzstate, (void *)vmlinuz.addr, vmlinuz.size); + gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size); gunzip_exactly(&gzstate, elfheader, sizeof(elfheader)); - if (!is_elf64(elfheader) && !is_elf32(elfheader)) { + if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) { printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); exit(); } @@ -135,55 +132,92 @@ static void prep_kernel(unsigned long a1, unsigned long a2) * the kernel bss must be claimed (it will be zero'd by the * kernel itself) */ - printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); - vmlinux.addr = (unsigned long)malloc(vmlinux.memsize); - if (vmlinux.addr == 0) { - printf("Can't allocate memory for kernel image !\n\r"); - exit(); + printf("Allocating 0x%lx bytes for kernel ...\n\r", ei.memsize); + + if (platform_ops.vmlinux_alloc) { + addr = platform_ops.vmlinux_alloc(ei.memsize); + } else { + if ((unsigned long)_start < ei.memsize) { + printf("Insufficient memory for kernel at address 0!" + " (_start=%lx)\n\r", _start); + exit(); + } } + /* Finally, gunzip the kernel */ + printf("gunzipping (0x%p <- 0x%p:0x%p)...", addr, + vmlinuz_addr, vmlinuz_addr+vmlinuz_size); + /* discard up to the actual load data */ + gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader)); + len = gunzip_finish(&gzstate, addr, ei.memsize); + printf("done 0x%lx bytes\n\r", len); + + flush_cache(addr, ei.loadsize); + + return (struct addr_range){addr, ei.memsize}; +} + +static struct addr_range prep_initrd(struct addr_range vmlinux, + unsigned long initrd_addr, + unsigned long initrd_size) +{ + void *devp; + u32 initrd_start, initrd_end; + + /* If we have an image attached to us, it overrides anything + * supplied by the loader. */ + if (_initrd_end > _initrd_start) { + printf("Attached initrd image at 0x%p-0x%p\n\r", + _initrd_start, _initrd_end); + initrd_addr = (unsigned long)_initrd_start; + initrd_size = _initrd_end - _initrd_start; + } else if (initrd_size > 0) { + printf("Using loader supplied ramdisk at 0x%lx-0x%lx\n\r", + initrd_addr, initrd_addr + initrd_size); + } + + /* If there's no initrd at all, we're done */ + if (! initrd_size) + return (struct addr_range){0, 0}; + /* - * Now find the initrd - * - * First see if we have an image attached to us. If so - * allocate memory for it and copy it there. + * If the initrd is too low it will be clobbered when the + * kernel relocates to its final location. In this case, + * allocate a safer place and move it. */ - initrd.size = (unsigned long)(_initrd_end - _initrd_start); - initrd.memsize = initrd.size; - if (initrd.size > 0) { + if (initrd_addr < vmlinux.size) { + void *old_addr = (void *)initrd_addr; + printf("Allocating 0x%lx bytes for initrd ...\n\r", - initrd.size); - initrd.addr = (unsigned long)malloc((u32)initrd.size); - if (initrd.addr == 0) { + initrd_size); + initrd_addr = (unsigned long)malloc(initrd_size); + if (! initrd_addr) { printf("Can't allocate memory for initial " - "ramdisk !\n\r"); + "ramdisk !\n\r"); exit(); } - printf("initial ramdisk moving 0x%lx <- 0x%lx " - "(0x%lx bytes)\n\r", initrd.addr, - (unsigned long)_initrd_start, initrd.size); - memmove((void *)initrd.addr, (void *)_initrd_start, - initrd.size); - printf("initrd head: 0x%lx\n\r", - *((unsigned long *)initrd.addr)); - } else if (a2 != 0) { - /* Otherwise, see if yaboot or another loader gave us an initrd */ - initrd.addr = a1; - initrd.memsize = initrd.size = a2; - printf("Using loader supplied initrd at 0x%lx (0x%lx bytes)\n\r", - initrd.addr, initrd.size); + printf("Relocating initrd 0x%p <- 0x%p (0x%lx bytes)\n\r", + initrd_addr, old_addr, initrd_size); + memmove((void *)initrd_addr, old_addr, initrd_size); } - /* Eventually gunzip the kernel */ - printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", - vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); - /* discard up to the actual load data */ - gunzip_discard(&gzstate, elfoffset - sizeof(elfheader)); - len = gunzip_finish(&gzstate, (void *)vmlinux.addr, - vmlinux.memsize); - printf("done 0x%lx bytes\n\r", len); + printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd_addr)); + + /* Tell the kernel initrd address via device tree */ + devp = finddevice("/chosen"); + if (! devp) { + printf("Device tree has no chosen node!\n\r"); + exit(); + } + + initrd_start = (u32)initrd_addr; + initrd_end = (u32)initrd_addr + initrd_size; + + setprop(devp, "linux,initrd-start", &initrd_start, + sizeof(initrd_start)); + setprop(devp, "linux,initrd-end", &initrd_end, sizeof(initrd_end)); - flush_cache((void *)vmlinux.addr, vmlinux.size); + return (struct addr_range){(void *)initrd_addr, initrd_size}; } /* A buffer that may be edited by tools operating on a zImage binary so as to @@ -223,6 +257,7 @@ struct console_ops console_ops; void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) { + struct addr_range vmlinux, initrd; kernel_entry_t kentry; char cmdline[COMMAND_LINE_SIZE]; unsigned long ft_addr = 0; @@ -242,7 +277,8 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start, sp); - prep_kernel(a1, a2); + vmlinux = prep_kernel(); + initrd = prep_initrd(vmlinux, a1, a2); /* If cmdline came from zimage wrapper or if we can edit the one * in the dt, print it out and edit it, if possible. @@ -271,8 +307,7 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) if (ft_addr) kentry(ft_addr, 0, NULL); else - /* XXX initrd addr/size should be passed in properties */ - kentry(initrd.addr, initrd.size, promptr); + kentry((unsigned long)initrd.addr, initrd.size, promptr); /* console closed so printf below may not work */ printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index 0182f384f3e6..044f34770b96 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c @@ -208,6 +208,17 @@ static void of_image_hdr(const void *hdr) } } +static void *of_vmlinux_alloc(unsigned long size) +{ + void *p = malloc(size); + + if (!p) { + printf("Can't allocate memory for kernel image!\n\r"); + exit(); + } + return p; +} + static void of_exit(void) { call_prom("exit", 0, 0); @@ -261,6 +272,7 @@ int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end) platform_ops.image_hdr = of_image_hdr; platform_ops.malloc = of_try_claim; platform_ops.exit = of_exit; + platform_ops.vmlinux_alloc = of_vmlinux_alloc; dt_ops.finddevice = of_finddevice; dt_ops.getprop = of_getprop; diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 8abb6516bb7c..fa62ff223e70 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -25,6 +25,7 @@ struct platform_ops { void (*free)(void *ptr); void * (*realloc)(void *ptr, unsigned long size); void (*exit)(void); + void * (*vmlinux_alloc)(unsigned long size); }; extern struct platform_ops platform_ops; -- cgit From cd197ffcf10bcc1a260efe5c09a3188fd9228c83 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 5 Mar 2007 14:24:52 +1100 Subject: [POWERPC] zImage: Cleanup and improve zImage entry point This patch re-organises the way the zImage wrapper code is entered, to allow more flexibility on platforms with unusual entry conditions. After this patch, a platform .o file has two options: 1) It can define a _zimage_start, in which case the platform code gets control from the very beginning of execution. In this case the platform code is responsible for relocating the zImage if necessary, clearing the BSS, performing any platform specific initialization, and finally calling start() to load and enter the kernel. 2) It can define platform_init(). In this case the generic crt0.S handles initial entry, and calls platform_init() before calling start(). The signature of platform_init() is changed, however, to take up to 5 parameters (in r3..r7) as they come from the platform's initial loader, instead of a fixed set of parameters based on OF's usage. When using the generic crt0.S, the platform .o can optionally supply a custom stack to use, using the BSS_STACK() macro. If this is not supplied, the crt0.S will assume that the loader has supplied a usable stack. In either case, the platform code communicates information to the generic code (specifically, a PROM pointer for OF systems, and/or an initrd image address supplied by the bootloader) via a global structure "loader_info". In addition the wrapper script is rearranged to ensure that the platform .o is always linked first. This means that platforms where the zImage entry point is at a fixed address or offset, rather than being encoded in the binary header can be supported using option (1). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/Makefile | 6 +++--- arch/powerpc/boot/crt0.S | 32 +++++++++++++++++++++++++++++--- arch/powerpc/boot/main.c | 18 +++++++----------- arch/powerpc/boot/of.c | 6 ++++-- arch/powerpc/boot/ops.h | 12 +++++++++++- arch/powerpc/boot/wrapper | 6 ++++-- arch/powerpc/boot/zImage.coff.lds.S | 3 ++- arch/powerpc/boot/zImage.lds.S | 1 + 8 files changed, 61 insertions(+), 23 deletions(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 3628d8681844..b1fc029e5ea7 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -40,11 +40,11 @@ zliblinuxheader := zlib.h zconf.h zutil.h $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ $(addprefix $(obj)/,$(zlibheader)) -src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.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 $(zlib) src-plat := of.c -src-boot := crt0.S $(src-wlib) $(src-plat) empty.c +src-boot := $(src-wlib) $(src-plat) empty.c src-boot := $(addprefix $(obj)/, $(src-boot)) obj-boot := $(addsuffix .o, $(basename $(src-boot))) @@ -97,7 +97,7 @@ $(obj)/wrapper.a: $(obj-wlib) hostprogs-y := addnote addRamDisk hack-coff mktree -extra-y := $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ +extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ $(obj)/zImage.lds $(obj)/zImage.coff.lds wrapper :=$(srctree)/$(src)/wrapper diff --git a/arch/powerpc/boot/crt0.S b/arch/powerpc/boot/crt0.S index 70e65b13e033..3dc8d8f78499 100644 --- a/arch/powerpc/boot/crt0.S +++ b/arch/powerpc/boot/crt0.S @@ -16,6 +16,7 @@ _zimage_start_opd: .long _zimage_start, 0, 0, 0 + .weak _zimage_start .globl _zimage_start _zimage_start: /* Work out the offset between the address we were linked at @@ -44,7 +45,7 @@ _zimage_start: addi r9,r9,4 bdnz 2b - /* Do a cache flush for our text, in case OF didn't */ + /* Do a cache flush for our text, in case the loader didn't */ 3: lis r9,_start@ha addi r9,r9,_start@l add r9,r0,r9 @@ -59,6 +60,31 @@ _zimage_start: sync isync - mr r6,r1 - b start + /* Clear the BSS */ + lis r9,__bss_start@ha + addi r9,r9,__bss_start@l + lis r8,_end@ha + addi r8,r8,_end@l + li r0,0 +5: stw r0,0(r9) + addi r9,r9,4 + cmplw cr0,r9,r8 + blt 5b + /* Possibly set up a custom stack */ +.weak _platform_stack_top + lis r8,_platform_stack_top@ha + addi r8,r8,_platform_stack_top@l + cmpwi r8,0 + beq 6f + lwz r1,0(r8) + li r0,0 + stwu r0,-16(r1) /* establish a stack frame */ +6: + + /* Call platform_init() */ + bl platform_init + + /* Call start */ + mr r3,r1 + b start diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 05de6cfafeeb..8a60e13777d7 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -254,21 +254,15 @@ static void set_cmdline(char *buf) struct platform_ops platform_ops; struct dt_ops dt_ops; struct console_ops console_ops; +struct loader_info loader_info; -void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) +void start(void *sp) { struct addr_range vmlinux, initrd; kernel_entry_t kentry; char cmdline[COMMAND_LINE_SIZE]; unsigned long ft_addr = 0; - memset(__bss_start, 0, _end - __bss_start); - memset(&platform_ops, 0, sizeof(platform_ops)); - memset(&dt_ops, 0, sizeof(dt_ops)); - memset(&console_ops, 0, sizeof(console_ops)); - - if (platform_init(promptr, _dtb_start, _dtb_end)) - exit(); if (console_ops.open && (console_ops.open() < 0)) exit(); if (platform_ops.fixups) @@ -278,7 +272,8 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) _start, sp); vmlinux = prep_kernel(); - initrd = prep_initrd(vmlinux, a1, a2); + initrd = prep_initrd(vmlinux, loader_info.initrd_addr, + loader_info.initrd_size); /* If cmdline came from zimage wrapper or if we can edit the one * in the dt, print it out and edit it, if possible. @@ -298,7 +293,7 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) if (ft_addr) printf(" flat tree at 0x%lx\n\r", ft_addr); else - printf(" using OF tree (promptr=%p)\n\r", promptr); + printf(" using OF tree (promptr=%p)\n\r", loader_info.promptr); if (console_ops.close) console_ops.close(); @@ -307,7 +302,8 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) if (ft_addr) kentry(ft_addr, 0, NULL); else - kentry((unsigned long)initrd.addr, initrd.size, promptr); + kentry((unsigned long)initrd.addr, initrd.size, + loader_info.promptr); /* console closed so printf below may not work */ printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index 044f34770b96..c6f0d9701485 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c @@ -267,7 +267,7 @@ static void of_console_write(char *buf, int len) call_prom("write", 3, 1, of_stdout_handle, buf, len); } -int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end) +void platform_init(unsigned long a1, unsigned long a2, void *promptr) { platform_ops.image_hdr = of_image_hdr; platform_ops.malloc = of_try_claim; @@ -282,5 +282,7 @@ int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end) console_ops.write = of_console_write; prom = (int (*)(void *))promptr; - return 0; + loader_info.promptr = promptr; + loader_info.initrd_addr = a1; + loader_info.initrd_size = a2; } diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index fa62ff223e70..cad4eee599fb 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -59,7 +59,13 @@ struct serial_console_data { void (*close)(void); }; -int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end); +struct loader_info { + void *promptr; + unsigned long initrd_addr, initrd_size; +}; +extern struct loader_info loader_info; + +void start(void *sp); 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); @@ -100,4 +106,8 @@ static inline void exit(void) for(;;); } +#define BSS_STACK(size) \ + static char _bss_stack[size]; \ + void *_platform_stack_top = _bss_stack + sizeof(_bss_stack); + #endif /* _PPC_BOOT_OPS_H_ */ diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index 024e4d425c59..157d8c89e138 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -191,7 +191,7 @@ fi if [ "$platform" != "miboot" ]; then ${CROSS}ld -m elf32ppc -T $lds -o "$ofile" \ - $object/crt0.o $platformo $tmp $object/wrapper.a + $platformo $tmp $object/wrapper.a rm $tmp fi @@ -201,7 +201,9 @@ pseries|chrp) $object/addnote "$ofile" ;; pmaccoff) - ${CROSS}objcopy -O aixcoff-rs6000 --set-start 0x500000 "$ofile" + entry=`objdump -f "$ofile" | grep '^start address ' | \ + cut -d' ' -f3` + ${CROSS}objcopy -O aixcoff-rs6000 --set-start "$entry" "$ofile" $object/hack-coff "$ofile" ;; esac diff --git a/arch/powerpc/boot/zImage.coff.lds.S b/arch/powerpc/boot/zImage.coff.lds.S index a360905e5428..fe87a90ce7f1 100644 --- a/arch/powerpc/boot/zImage.coff.lds.S +++ b/arch/powerpc/boot/zImage.coff.lds.S @@ -1,5 +1,6 @@ OUTPUT_ARCH(powerpc:common) -ENTRY(_start) +ENTRY(_zimage_start_opd) +EXTERN(_zimage_start_opd) SECTIONS { . = (5*1024*1024); diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S index 4be3c6414b04..f6e380fdb388 100644 --- a/arch/powerpc/boot/zImage.lds.S +++ b/arch/powerpc/boot/zImage.lds.S @@ -1,5 +1,6 @@ OUTPUT_ARCH(powerpc:common) ENTRY(_zimage_start) +EXTERN(_zimage_start) SECTIONS { . = (4*1024*1024); -- cgit From 0e6806734fd861c360ecbb4262d3d5678cea7faf Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Mon, 19 Mar 2007 14:58:06 -0600 Subject: [POWERPC] boot: export flush_cache Move the declaration of flush_cache to ops.h for use by platform code. Signed-off-by: Milton Miller Acked-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/main.c | 2 -- arch/powerpc/boot/ops.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 8a60e13777d7..d872b758ef14 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -18,8 +18,6 @@ #include "gunzip_util.h" #include "flatdevtree.h" -extern void flush_cache(void *, unsigned long); - extern char _start[]; extern char __bss_start[]; extern char _end[]; diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 4d0cfd733985..93608b772db5 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -79,7 +79,7 @@ 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); static inline void *finddevice(const char *name) { -- cgit From 6a923216aac01d0f3eeea606377b81541f1a2773 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Wed, 21 Mar 2007 09:02:44 -0600 Subject: [POWERPC] bootwrapper: Add a fatal error helper Add a macro fatal that calls printf then exit. User must include stdio.h. Typically replaces 3 lines with 1, although I added back some whitespace. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras --- arch/powerpc/boot/gunzip_util.c | 36 ++++++++++++------------------------ arch/powerpc/boot/main.c | 30 +++++++++++------------------- arch/powerpc/boot/of.c | 7 +++---- arch/powerpc/boot/ops.h | 2 ++ 4 files changed, 28 insertions(+), 47 deletions(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/gunzip_util.c b/arch/powerpc/boot/gunzip_util.c index f7c95f24fcdd..8a97adfac659 100644 --- a/arch/powerpc/boot/gunzip_util.c +++ b/arch/powerpc/boot/gunzip_util.c @@ -52,18 +52,14 @@ void gunzip_start(struct gunzip_state *state, void *src, int srclen) int r, flags; state->s.workspace = state->scratch; - if (zlib_inflate_workspacesize() > sizeof(state->scratch)) { - printf("insufficient scratch space for gunzip\n\r"); - exit(); - } + if (zlib_inflate_workspacesize() > sizeof(state->scratch)) + fatal("insufficient scratch space for gunzip\n\r"); /* skip header */ hdrlen = 10; flags = hdr[3]; - if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0) { - printf("bad gzipped data\n\r"); - exit(); - } + if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0) + fatal("bad gzipped data\n\r"); if ((flags & EXTRA_FIELD) != 0) hdrlen = 12 + hdr[10] + (hdr[11] << 8); if ((flags & ORIG_NAME) != 0) @@ -74,16 +70,12 @@ void gunzip_start(struct gunzip_state *state, void *src, int srclen) ; if ((flags & HEAD_CRC) != 0) hdrlen += 2; - if (hdrlen >= srclen) { - printf("gunzip_start: ran out of data in header\n\r"); - exit(); - } + if (hdrlen >= srclen) + fatal("gunzip_start: ran out of data in header\n\r"); r = zlib_inflateInit2(&state->s, -MAX_WBITS); - if (r != Z_OK) { - printf("inflateInit2 returned %d\n\r", r); - exit(); - } + if (r != Z_OK) + fatal("inflateInit2 returned %d\n\r", r); } state->s.next_in = src + hdrlen; @@ -117,10 +109,8 @@ int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen) state->s.next_out = dst; state->s.avail_out = dstlen; r = zlib_inflate(&state->s, Z_FULL_FLUSH); - if (r != Z_OK && r != Z_STREAM_END) { - printf("inflate returned %d msg: %s\n\r", r, state->s.msg); - exit(); - } + if (r != Z_OK && r != Z_STREAM_END) + fatal("inflate returned %d msg: %s\n\r", r, state->s.msg); len = state->s.next_out - (unsigned char *)dst; } else { /* uncompressed image */ @@ -151,10 +141,8 @@ void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) int len; len = gunzip_partial(state, dst, dstlen); - if (len < dstlen) { - printf("gunzip_block: ran out of data\n\r"); - exit(); - } + if (len < dstlen) + fatal("gunzip_block: ran out of data\n\r"); } /** diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index d872b758ef14..df9e95a84015 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -118,10 +118,9 @@ static struct addr_range prep_kernel(void) gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size); gunzip_exactly(&gzstate, elfheader, sizeof(elfheader)); - if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) { - printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); - exit(); - } + if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) + fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); + if (platform_ops.image_hdr) platform_ops.image_hdr(elfheader); @@ -135,11 +134,9 @@ static struct addr_range prep_kernel(void) if (platform_ops.vmlinux_alloc) { addr = platform_ops.vmlinux_alloc(ei.memsize); } else { - if ((unsigned long)_start < ei.memsize) { - printf("Insufficient memory for kernel at address 0!" + if ((unsigned long)_start < ei.memsize) + fatal("Insufficient memory for kernel at address 0!" " (_start=%lx)\n\r", _start); - exit(); - } } /* Finally, gunzip the kernel */ @@ -189,11 +186,9 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd_size); initrd_addr = (unsigned long)malloc(initrd_size); - if (! initrd_addr) { - printf("Can't allocate memory for initial " + if (! initrd_addr) + fatal("Can't allocate memory for initial " "ramdisk !\n\r"); - exit(); - } printf("Relocating initrd 0x%p <- 0x%p (0x%lx bytes)\n\r", initrd_addr, old_addr, initrd_size); memmove((void *)initrd_addr, old_addr, initrd_size); @@ -203,10 +198,8 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, /* Tell the kernel initrd address via device tree */ devp = finddevice("/chosen"); - if (! devp) { - printf("Device tree has no chosen node!\n\r"); - exit(); - } + if (! devp) + fatal("Device tree has no chosen node!\n\r"); initrd_start = (u32)initrd_addr; initrd_end = (u32)initrd_addr + initrd_size; @@ -303,7 +296,6 @@ void start(void *sp) kentry((unsigned long)initrd.addr, initrd.size, loader_info.promptr); - /* console closed so printf below may not work */ - printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); - exit(); + /* console closed so printf in fatal below may not work */ + fatal("Error: Linux kernel returned to zImage boot wrapper!\n\r"); } diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index c6f0d9701485..2cec5c17fb60 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c @@ -212,10 +212,9 @@ static void *of_vmlinux_alloc(unsigned long size) { void *p = malloc(size); - if (!p) { - printf("Can't allocate memory for kernel image!\n\r"); - exit(); - } + if (!p) + fatal("Can't allocate memory for kernel image!\n\r"); + return p; } diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 93608b772db5..ea5368caca59 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -158,6 +158,8 @@ static inline void exit(void) platform_ops.exit(); for(;;); } +#define fatal(args...) { printf(args); exit(); } + #define BSS_STACK(size) \ static char _bss_stack[size]; \ -- cgit From fae59c39e885148acf42320fe0d4ebf4cb3e9231 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 22 Mar 2007 16:59:13 +1100 Subject: [POWERPC] Add gcc format warnings to zImage printf() This patch adds the correct attributes to the zImage's versions of printf to make gcc generate format string mismatch warnings. It also corrects several minor problems with format strings in the zImage thus discovered. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/main.c | 6 +++--- arch/powerpc/boot/stdio.h | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index df9e95a84015..33c73295acf3 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -136,7 +136,7 @@ static struct addr_range prep_kernel(void) } else { if ((unsigned long)_start < ei.memsize) fatal("Insufficient memory for kernel at address 0!" - " (_start=%lx)\n\r", _start); + " (_start=%p)\n\r", _start); } /* Finally, gunzip the kernel */ @@ -145,7 +145,7 @@ static struct addr_range prep_kernel(void) /* discard up to the actual load data */ gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader)); len = gunzip_finish(&gzstate, addr, ei.memsize); - printf("done 0x%lx bytes\n\r", len); + printf("done 0x%x bytes\n\r", len); flush_cache(addr, ei.loadsize); @@ -189,7 +189,7 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, if (! initrd_addr) fatal("Can't allocate memory for initial " "ramdisk !\n\r"); - printf("Relocating initrd 0x%p <- 0x%p (0x%lx bytes)\n\r", + printf("Relocating initrd 0x%lx <- 0x%p (0x%lx bytes)\n\r", initrd_addr, old_addr, initrd_size); memmove((void *)initrd_addr, old_addr, initrd_size); } diff --git a/arch/powerpc/boot/stdio.h b/arch/powerpc/boot/stdio.h index 73b8a91bfb34..adffc58412d4 100644 --- a/arch/powerpc/boot/stdio.h +++ b/arch/powerpc/boot/stdio.h @@ -7,11 +7,12 @@ #define EINVAL 22 /* Invalid argument */ #define ENOSPC 28 /* No space left on device */ -extern int printf(const char *fmt, ...); +extern int printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); #define fprintf(fmt, args...) printf(args) -extern int sprintf(char *buf, const char *fmt, ...); +extern int sprintf(char *buf, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); extern int vsprintf(char *buf, const char *fmt, va_list args); -- cgit From e5a2072bd48eb4a35c57a8ec45897ac2db3a3f82 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 22 Mar 2007 17:02:21 +1100 Subject: [POWERPC] New reg.h for the zImage This patch adds a reg.h to the zImage code, with common definitions for accessing system registers. For now, this includes functions for retrieving the PVR and the stack pointer. This patch then uses the new reg.h to let start() display the running stack address without having to explicitly pass the stack as a parameter from the asm code. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/crt0.S | 1 - arch/powerpc/boot/main.c | 5 +++-- arch/powerpc/boot/ops.h | 2 +- arch/powerpc/boot/reg.h | 22 ++++++++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 arch/powerpc/boot/reg.h (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/crt0.S b/arch/powerpc/boot/crt0.S index 25ad7453531a..bd7770b60730 100644 --- a/arch/powerpc/boot/crt0.S +++ b/arch/powerpc/boot/crt0.S @@ -88,5 +88,4 @@ _zimage_start_lib: bl platform_init /* Call start */ - mr r3,r1 b start diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 33c73295acf3..e1df8feaf16d 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -17,6 +17,7 @@ #include "ops.h" #include "gunzip_util.h" #include "flatdevtree.h" +#include "reg.h" extern char _start[]; extern char __bss_start[]; @@ -247,7 +248,7 @@ struct dt_ops dt_ops; struct console_ops console_ops; struct loader_info loader_info; -void start(void *sp) +void start(void) { struct addr_range vmlinux, initrd; kernel_entry_t kentry; @@ -260,7 +261,7 @@ void start(void *sp) platform_ops.fixups(); printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", - _start, sp); + _start, get_sp()); vmlinux = prep_kernel(); initrd = prep_initrd(vmlinux, loader_info.initrd_addr, diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index ea5368caca59..592dc6c20bdb 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -73,7 +73,7 @@ struct loader_info { }; extern struct loader_info loader_info; -void start(void *sp); +void start(void); 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); diff --git a/arch/powerpc/boot/reg.h b/arch/powerpc/boot/reg.h new file mode 100644 index 000000000000..d3cd9ee98afb --- /dev/null +++ b/arch/powerpc/boot/reg.h @@ -0,0 +1,22 @@ +#ifndef _PPC_BOOT_REG_H +#define _PPC_BOOT_REG_H +/* + * Copyright 2007 Davud Gibson, 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. + */ + +static inline u32 mfpvr(void) +{ + u32 pvr; + asm volatile ("mfpvr %0" : "=r"(pvr)); + return pvr; +} + +register void *__stack_pointer asm("r1"); +#define get_sp() (__stack_pointer) + +#endif /* _PPC_BOOT_REG_H */ -- cgit From 27fbaa9702e548e74dffd21855769f6cedad42bd Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 22 Mar 2007 17:02:21 +1100 Subject: [POWERPC] Add device tree utility functions to zImage This patch adds a library of useful device tree manipulation functions to the zImage library, for use by platform code. These functions are based on the hooks already in dt_ops, so they're not dependent on a particular device tree implementation. This patch also slightly streamlines the code in main.c using these new functions. This is a consolidation of my work in this area with Scott Wood's patches to a very similar end. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/Makefile | 2 +- arch/powerpc/boot/devtree.c | 111 ++++++++++++++++++++++++++++++++++++++++++++ arch/powerpc/boot/main.c | 27 +++++------ arch/powerpc/boot/ops.h | 13 ++++++ 4 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 arch/powerpc/boot/devtree.c (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index de80e47d1171..fac6ed0c5bcd 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -42,7 +42,7 @@ $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ 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 $(zlib) + gunzip_util.c $(zlib) devtree.c src-plat := of.c src-boot := $(src-wlib) $(src-plat) empty.c diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c new file mode 100644 index 000000000000..708cadebeb46 --- /dev/null +++ b/arch/powerpc/boot/devtree.c @@ -0,0 +1,111 @@ +/* + * devtree.c - convenience functions for device tree manipulation + * Copyright 2007 David Gibson, IBM Corporation. + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * + * Authors: David Gibson + * Scott Wood + * + * 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. + */ +#include +#include +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "ops.h" + +void dt_fixup_memory(u64 start, u64 size) +{ + void *root, *memory; + int naddr, nsize, i; + u32 memreg[4]; + + root = finddevice("/"); + if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0) + naddr = 2; + if (naddr < 1 || naddr > 2) + fatal("Can't cope with #address-cells == %d in /\n\r", naddr); + + if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0) + nsize = 1; + if (nsize < 1 || nsize > 2) + fatal("Can't cope with #size-cells == %d in /\n\r", nsize); + + i = 0; + if (naddr == 2) + memreg[i++] = start >> 32; + memreg[i++] = start & 0xffffffff; + if (nsize == 2) + memreg[i++] = size >> 32; + memreg[i++] = size & 0xffffffff; + + memory = finddevice("/memory"); + if (! memory) { + memory = create_node(NULL, "memory"); + setprop_str(memory, "device_type", "memory"); + } + + printf("Memory <- <0x%x", memreg[0]); + for (i = 1; i < (naddr + nsize); i++) + printf(" 0x%x", memreg[i]); + printf("> (%ldMB)\n\r", (unsigned long)(size >> 20)); + + setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32)); +} + +#define MHZ(x) ((x + 500000) / 1000000) + +void dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus) +{ + void *devp = NULL; + + printf("CPU clock-frequency <- 0x%x (%dMHz)\n\r", cpu, MHZ(cpu)); + printf("CPU timebase-frequency <- 0x%x (%dMHz)\n\r", tb, MHZ(tb)); + if (bus > 0) + printf("CPU bus-frequency <- 0x%x (%dMHz)\n\r", bus, MHZ(bus)); + + while ((devp = find_node_by_devtype(devp, "cpu"))) { + setprop_val(devp, "clock-frequency", cpu); + setprop_val(devp, "timebase-frequency", tb); + if (bus > 0) + setprop_val(devp, "bus-frequency", bus); + } +} + +void dt_fixup_clock(const char *path, u32 freq) +{ + void *devp = finddevice(path); + + if (devp) { + printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq)); + setprop_val(devp, "clock-frequency", freq); + } +} + +void __dt_fixup_mac_addresses(u32 startindex, ...) +{ + va_list ap; + u32 index = startindex; + void *devp; + const u8 *addr; + + va_start(ap, startindex); + while ((addr = va_arg(ap, const u8 *))) { + devp = find_node_by_prop_value(NULL, "linux,network-index", + (void*)&index, sizeof(index)); + + printf("ENET%d: local-mac-address <-" + " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index, + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + if (devp) + setprop(devp, "local-mac-address", addr, 6); + + index++; + } + va_end(ap); +} diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index e1df8feaf16d..ab9dfe2b38f1 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -153,13 +153,10 @@ static struct addr_range prep_kernel(void) return (struct addr_range){addr, ei.memsize}; } -static struct addr_range prep_initrd(struct addr_range vmlinux, +static struct addr_range prep_initrd(struct addr_range vmlinux, void *chosen, unsigned long initrd_addr, unsigned long initrd_size) { - void *devp; - u32 initrd_start, initrd_end; - /* If we have an image attached to us, it overrides anything * supplied by the loader. */ if (_initrd_end > _initrd_start) { @@ -198,16 +195,8 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd_addr)); /* Tell the kernel initrd address via device tree */ - devp = finddevice("/chosen"); - if (! devp) - fatal("Device tree has no chosen node!\n\r"); - - initrd_start = (u32)initrd_addr; - initrd_end = (u32)initrd_addr + initrd_size; - - setprop(devp, "linux,initrd-start", &initrd_start, - sizeof(initrd_start)); - setprop(devp, "linux,initrd-end", &initrd_end, sizeof(initrd_end)); + setprop_val(chosen, "linux,initrd-start", (u32)(initrd_addr)); + setprop_val(chosen, "linux,initrd-end", (u32)(initrd_addr+initrd_size)); return (struct addr_range){(void *)initrd_addr, initrd_size}; } @@ -254,6 +243,7 @@ void start(void) kernel_entry_t kentry; char cmdline[COMMAND_LINE_SIZE]; unsigned long ft_addr = 0; + void *chosen; if (console_ops.open && (console_ops.open() < 0)) exit(); @@ -263,9 +253,14 @@ void start(void) printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start, get_sp()); + /* Ensure that the device tree has a /chosen node */ + chosen = finddevice("/chosen"); + if (!chosen) + chosen = create_node(NULL, "chosen"); + vmlinux = prep_kernel(); - initrd = prep_initrd(vmlinux, loader_info.initrd_addr, - loader_info.initrd_size); + initrd = prep_initrd(vmlinux, chosen, + loader_info.initrd_addr, loader_info.initrd_size); /* If cmdline came from zimage wrapper or if we can edit the one * in the dt, print it out and edit it, if possible. diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 592dc6c20bdb..cc191e8e147f 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -96,6 +96,11 @@ static inline int setprop(void *devp, const char *name, { return (dt_ops.setprop) ? dt_ops.setprop(devp, name, buf, buflen) : -1; } +#define setprop_val(devp, name, val) \ + do { \ + typeof(val) x = (val); \ + setprop((devp), (name), &x, sizeof(x)); \ + } while (0) static inline int setprop_str(void *devp, const char *name, const char *buf) { @@ -141,6 +146,14 @@ static inline void *find_node_by_devtype(const void *prev, return find_node_by_prop_value_str(prev, "device_type", type); } +void dt_fixup_memory(u64 start, u64 size); +void dt_fixup_cpu_clocks(u32 cpufreq, u32 tbfreq, u32 busfreq); +void dt_fixup_clock(const char *path, u32 freq); +void __dt_fixup_mac_addresses(u32 startindex, ...); +#define dt_fixup_mac_addresses(...) \ + __dt_fixup_mac_addresses(0, __VA_ARGS__, NULL) + + static inline void *malloc(u32 size) { return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL; -- cgit From 3af82a8b00f98ca54e4c860eeb2b9ede6d8cadf4 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 22 Mar 2007 17:02:21 +1100 Subject: [POWERPC] Clean up zImage handling of the command line This cleans up how the zImage code manipulates the kernel command line. Notable improvements from the old handling: - Command line manipulation is consolidated into a new prep_cmdline() function, rather than being scattered across start() and some helper functions - Less stack space use: we use just a single global command line buffer, which can be initialized by an external tool as before, we no longer need another command line sized buffer on the stack. - Easier to support platforms whose firmware passes a commandline, but not a device tree. Platform code can now point new loader_info fields to the firmware's command line, rather than having to do early manipulation of the /chosen bootargs property which may then be rewritten again by the core. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/main.c | 52 ++++++++++++++++++------------------------------ arch/powerpc/boot/ops.h | 2 ++ 2 files changed, 21 insertions(+), 33 deletions(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index ab9dfe2b38f1..03c0ccaecf29 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -205,31 +205,22 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, void *chosen, * 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 builtin_cmdline[COMMAND_LINE_SIZE] +static char cmdline[COMMAND_LINE_SIZE] __attribute__((__section__("__builtin_cmdline"))); -static void get_cmdline(char *buf, int size) +static void prep_cmdline(void *chosen) { - void *devp; - int len = strlen(builtin_cmdline); + if (cmdline[0] == '\0') + getprop(chosen, "bootargs", cmdline, COMMAND_LINE_SIZE-1); - buf[0] = '\0'; + printf("\n\rLinux/PowerPC load: %s", cmdline); + /* If possible, edit the command line */ + if (console_ops.edit_cmdline) + console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE); + printf("\n\r"); - if (len > 0) { /* builtin_cmdline overrides dt's /chosen/bootargs */ - len = min(len, size-1); - strncpy(buf, builtin_cmdline, len); - buf[len] = '\0'; - } - else if ((devp = finddevice("/chosen"))) - getprop(devp, "bootargs", buf, size); -} - -static void set_cmdline(char *buf) -{ - void *devp; - - if ((devp = finddevice("/chosen"))) - setprop(devp, "bootargs", buf, strlen(buf) + 1); + /* Put the command line back into the devtree for the kernel */ + setprop_str(chosen, "bootargs", cmdline); } struct platform_ops platform_ops; @@ -241,10 +232,16 @@ void start(void) { struct addr_range vmlinux, initrd; kernel_entry_t kentry; - char cmdline[COMMAND_LINE_SIZE]; unsigned long ft_addr = 0; void *chosen; + /* Do this first, because malloc() could clobber the loader's + * command line. Only use the loader command line if a + * built-in command line wasn't set by an external tool */ + if ((loader_info.cmdline_len > 0) && (cmdline[0] == '\0')) + memmove(cmdline, loader_info.cmdline, + min(loader_info.cmdline_len, COMMAND_LINE_SIZE-1)); + if (console_ops.open && (console_ops.open() < 0)) exit(); if (platform_ops.fixups) @@ -261,18 +258,7 @@ void start(void) vmlinux = prep_kernel(); initrd = prep_initrd(vmlinux, chosen, loader_info.initrd_addr, loader_info.initrd_size); - - /* If cmdline came from zimage wrapper or if we can edit the one - * in the dt, print it out and edit it, if possible. - */ - if ((strlen(builtin_cmdline) > 0) || console_ops.edit_cmdline) { - get_cmdline(cmdline, COMMAND_LINE_SIZE); - printf("\n\rLinux/PowerPC load: %s", cmdline); - if (console_ops.edit_cmdline) - console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE); - printf("\n\r"); - set_cmdline(cmdline); - } + prep_cmdline(chosen); printf("Finalizing device tree..."); if (dt_ops.finalize) diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index cc191e8e147f..8008d612402e 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -70,6 +70,8 @@ struct serial_console_data { struct loader_info { void *promptr; unsigned long initrd_addr, initrd_size; + char *cmdline; + int cmdline_len; }; extern struct loader_info loader_info; -- cgit From 88e687313e683ee006152d611b95f40900e3bce0 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 27 Mar 2007 15:29:50 -0700 Subject: [POWERPC] Move bootwrapper ELF parsing routines to a file The ELF parsing routines local to arch/powerpc/boot/main.c are useful to other callers therefore move them to their own file. Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras --- arch/powerpc/boot/Makefile | 2 +- arch/powerpc/boot/elf.h | 8 +++++ arch/powerpc/boot/elf_util.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ arch/powerpc/boot/main.c | 66 -------------------------------------- 4 files changed, 85 insertions(+), 67 deletions(-) create mode 100644 arch/powerpc/boot/elf_util.c (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index fac6ed0c5bcd..be001d923d3f 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -42,7 +42,7 @@ $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ 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 $(zlib) devtree.c + gunzip_util.c elf_util.c $(zlib) devtree.c src-plat := of.c src-boot := $(src-wlib) $(src-plat) empty.c diff --git a/arch/powerpc/boot/elf.h b/arch/powerpc/boot/elf.h index d4828fcf1cb9..1941bc50d4c5 100644 --- a/arch/powerpc/boot/elf.h +++ b/arch/powerpc/boot/elf.h @@ -146,4 +146,12 @@ typedef struct elf64_phdr { #define ELFOSABI_NONE 0 #define ELFOSABI_LINUX 3 +struct elf_info { + unsigned long loadsize; + unsigned long memsize; + unsigned long elfoffset; +}; +int parse_elf64(void *hdr, struct elf_info *info); +int parse_elf32(void *hdr, struct elf_info *info); + #endif /* _PPC_BOOT_ELF_H_ */ diff --git a/arch/powerpc/boot/elf_util.c b/arch/powerpc/boot/elf_util.c new file mode 100644 index 000000000000..7454aa4cc20c --- /dev/null +++ b/arch/powerpc/boot/elf_util.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner. + * + * 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. + */ +#include +#include +#include "elf.h" +#include "page.h" +#include "string.h" +#include "stdio.h" + +int parse_elf64(void *hdr, struct elf_info *info) +{ + Elf64_Ehdr *elf64 = hdr; + Elf64_Phdr *elf64ph; + unsigned int i; + + if (!(elf64->e_ident[EI_MAG0] == ELFMAG0 && + elf64->e_ident[EI_MAG1] == ELFMAG1 && + elf64->e_ident[EI_MAG2] == ELFMAG2 && + elf64->e_ident[EI_MAG3] == ELFMAG3 && + elf64->e_ident[EI_CLASS] == ELFCLASS64 && + elf64->e_ident[EI_DATA] == ELFDATA2MSB && + elf64->e_type == ET_EXEC && + elf64->e_machine == EM_PPC64)) + return 0; + + elf64ph = (Elf64_Phdr *)((unsigned long)elf64 + + (unsigned long)elf64->e_phoff); + for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++) + if (elf64ph->p_type == PT_LOAD) + break; + if (i >= (unsigned int)elf64->e_phnum) + return 0; + + info->loadsize = (unsigned long)elf64ph->p_filesz; + info->memsize = (unsigned long)elf64ph->p_memsz; + info->elfoffset = (unsigned long)elf64ph->p_offset; + + return 1; +} + +int parse_elf32(void *hdr, struct elf_info *info) +{ + Elf32_Ehdr *elf32 = hdr; + Elf32_Phdr *elf32ph; + unsigned int i; + + if (!(elf32->e_ident[EI_MAG0] == ELFMAG0 && + elf32->e_ident[EI_MAG1] == ELFMAG1 && + elf32->e_ident[EI_MAG2] == ELFMAG2 && + elf32->e_ident[EI_MAG3] == ELFMAG3 && + elf32->e_ident[EI_CLASS] == ELFCLASS32 && + elf32->e_ident[EI_DATA] == ELFDATA2MSB && + elf32->e_type == ET_EXEC && + elf32->e_machine == EM_PPC)) + return 0; + + elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff); + for (i = 0; i < elf32->e_phnum; i++, elf32ph++) + if (elf32ph->p_type == PT_LOAD) + break; + if (i >= elf32->e_phnum) + return 0; + + info->loadsize = elf32ph->p_filesz; + info->memsize = elf32ph->p_memsz; + info->elfoffset = elf32ph->p_offset; + return 1; +} diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 03c0ccaecf29..30390621203d 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -36,76 +36,10 @@ struct addr_range { unsigned long size; }; -struct elf_info { - unsigned long loadsize; - unsigned long memsize; - unsigned long elfoffset; -}; - typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); #undef DEBUG -static int parse_elf64(void *hdr, struct elf_info *info) -{ - Elf64_Ehdr *elf64 = hdr; - Elf64_Phdr *elf64ph; - unsigned int i; - - if (!(elf64->e_ident[EI_MAG0] == ELFMAG0 && - elf64->e_ident[EI_MAG1] == ELFMAG1 && - elf64->e_ident[EI_MAG2] == ELFMAG2 && - elf64->e_ident[EI_MAG3] == ELFMAG3 && - elf64->e_ident[EI_CLASS] == ELFCLASS64 && - elf64->e_ident[EI_DATA] == ELFDATA2MSB && - elf64->e_type == ET_EXEC && - elf64->e_machine == EM_PPC64)) - return 0; - - elf64ph = (Elf64_Phdr *)((unsigned long)elf64 + - (unsigned long)elf64->e_phoff); - for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++) - if (elf64ph->p_type == PT_LOAD) - break; - if (i >= (unsigned int)elf64->e_phnum) - return 0; - - info->loadsize = (unsigned long)elf64ph->p_filesz; - info->memsize = (unsigned long)elf64ph->p_memsz; - info->elfoffset = (unsigned long)elf64ph->p_offset; - - return 1; -} - -static int parse_elf32(void *hdr, struct elf_info *info) -{ - Elf32_Ehdr *elf32 = hdr; - Elf32_Phdr *elf32ph; - unsigned int i; - - if (!(elf32->e_ident[EI_MAG0] == ELFMAG0 && - elf32->e_ident[EI_MAG1] == ELFMAG1 && - elf32->e_ident[EI_MAG2] == ELFMAG2 && - elf32->e_ident[EI_MAG3] == ELFMAG3 && - elf32->e_ident[EI_CLASS] == ELFCLASS32 && - elf32->e_ident[EI_DATA] == ELFDATA2MSB && - elf32->e_type == ET_EXEC && - elf32->e_machine == EM_PPC)) - return 0; - - elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff); - for (i = 0; i < elf32->e_phnum; i++, elf32ph++) - if (elf32ph->p_type == PT_LOAD) - break; - if (i >= elf32->e_phnum) - return 0; - - info->loadsize = elf32ph->p_filesz; - info->memsize = elf32ph->p_memsz; - info->elfoffset = elf32ph->p_offset; - return 1; -} - static struct addr_range prep_kernel(void) { char elfheader[256]; -- cgit From 02cc51149f99e3c6c106e1e16dcc2e016b1bc3b5 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Thu, 29 Mar 2007 07:31:41 -0600 Subject: [POWERPC] bootwrapper: Decompress less, check more Our kernels put everything in the first load segment, and we read that. Instead of decompressing to the end of the gzip stream or supplied image and hoping we get it all, decompress the expected size and complain if it is not available. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras --- arch/powerpc/boot/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/boot/main.c') diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 30390621203d..56b56a8d4b23 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -79,7 +79,10 @@ static struct addr_range prep_kernel(void) vmlinuz_addr, vmlinuz_addr+vmlinuz_size); /* discard up to the actual load data */ gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader)); - len = gunzip_finish(&gzstate, addr, ei.memsize); + len = gunzip_finish(&gzstate, addr, ei.loadsize); + if (len != ei.loadsize) + fatal("ran out of data! only got 0x%x of 0x%lx bytes.\n\r", + len, ei.loadsize); printf("done 0x%x bytes\n\r", len); flush_cache(addr, ei.loadsize); -- cgit