From: Milton Miller <hidden> Date: 2007-07-10 22:07:03
This series creates a 32 bit zImage wrapper for a 32 or 64 bit PowerPC
Linux kernel. This allows you to kexec a zImage with its compressed
vmlinux instead of the uncompressed vmlinux elf. The elf is also
packaged as a 64 bit elf for use by kexec-tools for 64 bit kernels.
This series also adds code to extract the vmlinux elf file from an
initramfs (or other platform supplied location). This allows one
to load the elf once (compressed) and use it both for userspace and
for the actual boot.
milton
From: Milton Miller <hidden> Date: 2007-07-10 22:07:38
Some platforms have a boot agent that can create or modify properties in
the device-tree and load images into memory. Provide a helper to set
loader_info used by prep_initrd().
Signed-off-by: Milton Miller <redacted>
---
Moved to devtree.c as suggested.
define UNIT_MAX hardcoded as requested.
Print exactly why we are not propagting loader supplied initrd knowledge to
wrapper code.
Having start_prop and end_prop as variables allows several source lines
to fit in 80 columns.
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
@@ -305,3 +306,68 @@ int dt_xlate_addr(void *node, u32 *buf, memcpy(dt_xlate_buf,buf,buflen);returndt_xlate(node,0,buflen/4,xlated_addr,NULL);}++/**+*dt_find_initrd-setloaderinitrdlocationbasedonexistingproperties+*+*findsthelinux,initrd-startandlinux,initrd-endpropertiesin+*the/chosennodeandsetstheloaderinitrdfieldsaccordingly.+*+*Usethisifyourloadersetsthepropertiestoallowothercodeto+*relocatethetreeand/orcauser3andr4tobesetontrueOF+*platforms.+*/+voiddt_find_initrd(void)+{+intrc;+unsignedlonglonginitrd_start,initrd_end;+void*devp;+staticconstcharstart_prop[]="linux,initrd-start";+staticconstcharend_prop[]="linux,initrd-end";++devp=finddevice("/chosen");+if(!devp){+return;+}++rc=getprop(devp,start_prop,&initrd_start,sizeof(initrd_start));+if(rc<0)+return;/* not found */+/* The properties had to be 8 bytes until 2.6.22 */+if(rc==sizeof(unsignedlong)){+unsignedlongtmp;+memcpy(&tmp,&initrd_start,rc);+initrd_start=tmp;+}elseif(rc!=sizeof(initrd_start)){/* now they can be 4 */+printf("unexpected length of %s in /chosen!\n\r",start_prop);+return;+}++rc=getprop(devp,end_prop,&initrd_end,sizeof(initrd_end));+if(rc<0){+printf("chosen has %s but no %s!\n\r",start_prop,end_prop);+return;+}+if(rc==sizeof(unsignedlong)){+unsignedlongtmp;+memcpy(&tmp,&initrd_end,rc);+initrd_end=tmp;+}elseif(rc!=sizeof(initrd_end)){+printf("unexpected length of %s in /chosen!\n\r",end_prop);+return;+}++/* Check for presence, ignore if (partially) loaded above 32 bits */+if(initrd_start==initrd_end){+printf("ignoring empty device-tree supplied initrd\n");+}elseif(initrd_start>initrd_end){+printf("ignoring device-tree supplied initrd: start 0x%llx"+" > end 0x%llx \n",initrd_start,initrd_end);+}elseif(initrd_end>UINT_MAX){+printf("ignoring device-tree supplied initrd:"+" end 0x%llx > 32 bits\n",initrd_end);+}else{+loader_info.initrd_addr=initrd_start;+loader_info.initrd_size=initrd_end-initrd_start;+}+}
From: Milton Miller <hidden> Date: 2007-07-10 22:08:05
Record the number of header bytes skipped in the total bytes read field.
This is needed for the initramfs parsing code to find the end of the zip file.
Signed-off-by: Milton Miller <redacted>
---
Index: work.git/arch/powerpc/boot/gunzip_util.c
===================================================================
From: Milton Miller <hidden> Date: 2007-07-10 22:08:32
Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.
Signed-off-by: Milton Miller <redacted>
---
Index: work.git/arch/powerpc/boot/gunzip_util.c
===================================================================
From: Milton Miller <hidden> Date: 2007-07-10 22:08:59
Support code to move cpus around, both a spin loop and c code to
move the cpus before uncompressing and copying the kernel to 0.
The low level code is designed to be included in a crt0 or other
assembly file because it may need to be at a fixed location or there
may be other entry point requirements.
Note: this code works with kernel head_64.S. head_6xx.S needs the
0x60 entry point (it currently uses something closer to 0xC0; but
the similar code is at 0xC4); the other heads don't appear to support
SMP.
Signed-off-by: Milton Miller <redacted>
---
Cleaned up documentation. Hopefully the format is correct.
Removed the #if 1 #else #endif
Move hidden asm code outside function instead of branching over it.
This code has previously survied days of kexec stress and also works
when the next stage is itself (ie zBoot to zImage).
Index: work.git/arch/powerpc/boot/marshal_low.S
===================================================================
@@ -0,0 +1,275 @@+/*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram;ifnot,writetotheFreeSoftware+*Foundation,51FranklinStreet,FifthFloor,Boston,MA02110-1301,USA.+*+*Copyright(C)2007IBMCorporation.+*+*Authors:MiltonMiller<miltonm@bga.com>+*/++#include"ops.h"+#include"stdio.h"+#include"reg.h"++externunsignedintgohere[],master[],slave_wait[],slaves[],slaves_end[];+externunsignedintslave_error[1];++staticunsignedintslaves_run_here[SMP_SLAVE_SIZE/sizeof(unsignedint)];+staticunsignedint*slaves_were_here=master;+staticunsignedint*slaves_goto_here=master;++/**+*check_slave_errors-checkiftheslaveshavesettheerrorflag.+*@slaves_here...thelocationthattheslavesshouldbespinning.+*/+staticvoidcheck_slave_errors(unsignedint*slaves_here)+{+unsignedint*error=slave_error-master+slaves_here;++if(*error){+printf("WARNING: error detected by one or more slave cpus!!\n\r"+"WARNING: This probably means you have duplicate cpu ids\n\r");+/* exit() */+}+}++/**+*wait_slaves_moved-waitfortheslavestocatchup+*+*Waituntilallslavesthatcheckedinthepreviouslocationhave+*checkedintothecurrentlocation.Seperatesowecandoother+*workwhilewewaitforthemtocatchup.+*/+voidwait_slaves_moved(void)+{+intoffset=slaves-master;+intlen=sizeof(slaves_end[0])*(slaves_end-slaves);+intprinted=0;+unsignedint*to=slaves_goto_here;+unsignedint*from=slaves_were_here;++from+=offset;+to+=offset;++if(from==to)+return;++while(memcmp(from,to,len)){+if(!printed){+printf("waiting for slave cpus to move...");+printed=1;+HMT_LOW;+barrier();+}+/* check from is superset of to */+}+if(printed){+HMT_MEDIUM;+printf("done.\n\r");+}++slaves_were_here=slaves_goto_here;+}++/**+*move_slaves_here-moveslavestoaspecifiedaddress.+*@addr:locationof%SMP_SLAVE_SIZEbuffertoplacecodeandspin+*+*Tellslavestogofromtheircurrentlocationtoabuffer@addr+*of%SMP_SLAVE_SIZEbytessomewhereinmemory.+*/+voidmove_slaves_here(void*addr)+{+unsignedint*move_slaves_here=addr;+unsignedint*tell_them=gohere-master+slaves_goto_here;+unsignedint*goto_here=slave_wait-master+move_slaves_here;+unsignedint*wait_here=gohere-master+move_slaves_here;++if(move_slaves_here==slaves_goto_here)+return;/* already there */++wait_slaves_moved();/* one move at a time */++printf("moving slave cpus from %p to %p\n\r",slaves_goto_here,+move_slaves_here);++memcpy(move_slaves_here,master,SMP_SLAVE_SIZE);+memset(move_slaves_here+(slaves-master),0,+(slaves_end-slaves)*sizeof(slaves_end[0]));+*wait_here=0;++flush_cache(move_slaves_here,SMP_SLAVE_SIZE);++check_slave_errors(slaves_were_here);++*tell_them=(unsignedint)goto_here;+slaves_goto_here=move_slaves_here;+}++/**+*move_slaves_up-moveslavesfromsomewherelowtoourbss.+*Callbeforedecompressingthekerneltoaddress0.+*/+voidmove_slaves_up(void)+{+move_slaves_here(slaves_run_here);+}++/**+*slaves_are_low-Assertthattheslavesarespinningat0,andmovethem+*Assertthattheslavesarerunninginacopyofthemarshallcode+*thatwascopiedtoaddress0.Askthemtogouptoourbss,aswe+*knowwehavetomovethemawayfrom0.+*/+voidslaves_are_low(void)+{+slaves_goto_here=slaves_were_here=(void*)0;+move_slaves_up();+}++/**+*wait_slave_checkout-waitforslavestoexecutecheckoutstore.+*@checkout-slavecheckoutflagarray+*+*Waitforeveryslavewhocheckedinatslaves_were_hereto+*performthestbto@checkoutbeforethebranchtoselfspinloop.+*/+staticvoidwait_slave_checkout(char*checkout)+{+unsignedint*end=slaves_end-master+slaves_were_here;+unsignedint*from=slaves-master+slaves_were_here;;+unsignedintbit;+inti,ncpus=0;+char*waiting="waiting on slaves to go to kernel...";++for(i=0;from<end;from++)+for(bit=1;bit;i++,bit<<=1)+if(*from&bit){+ncpus++;+while(!checkout[i]){+if(waiting){+printf(waiting);+waiting=NULL;+}+HMT_LOW;+barrier();+}+}++if(waiting==NULL)+printf("done.\n\r");++printf("moved %d slaves to the kernel.\n\r",ncpus);+}++/* The slave checkin code ... used by checkout_slaves_to_kernel below */+externunsignedintslave_checkout_begin[],slave_checkout_spin[];+asm("\+.globlslave_checkout_begin;\+.globlslave_checkout_spin;\+slave_checkout_begin:;\+lwz7,0(0);\+li8,1;\+stbx8,7,3;\+slave_checkout_spin:;\+b$;\+");+++/**+*checkout_slaves_to_kernel-sendSMPslavestothekernel+*@tell_them-theexpectedmarshallingbufferfortheslaves+*+*Activelymoveslavesspinningon@tell_themto0x60.Sincewe+*don'tknowwhatcodeisthere,replaceitwithouronecodethat+*endswithabytestoreandbranchtoself,withthebranchat0x60.+*Afterthestorescomplete,wecanrestoretherestoftheline,+*flush,thenrestoretheremainingline.+*/+staticvoidcheckout_slaves_to_kernel(unsignedint*tell_them)+{+intto,spin;+unsignedint*from,*low,save[SMP_SLAVE_SIZE/sizeof(unsignedint)];+char*checkout;++checkout=malloc(1024);+if(checkout==NULL)+fatal("can't malloc slave checkout buffer");+memset(checkout,0,1024);++low=(unsignedint*)0;+memcpy(save,low,SMP_SLAVE_SIZE);++to=spin=0x60/sizeof(int);++to++;+from=slave_checkout_spin;+while(from>=slave_checkout_begin)+low[--to]=*from--;++low[0]=(unsignedint)checkout;+flush_cache(low,SMP_SLAVE_SIZE);++*tell_them=(unsignedint)(low+to);++wait_slave_checkout(checkout);++/* at this point, all have completed the store at %0x5c and are at+*thebranchtoselfat%0x60.Restoretherestofthevector,+*flushcache,thendothefinalstorereplacingthespinand+*flushagain.+*/+low[0]=save[0];+for(;to<spin;to++)+low[to]=save[to];+flush_cache(low,SMP_SLAVE_SIZE);+low[to]=save[to];+flush_cache(low,SMP_SLAVE_SIZE);++}++/**+*send_slaves_to_kernel-sendSMPslavestothekernel+*@vmlinux_addr:addressvmlinuxwasdecompressedto(wheretogetslaveloop)+*+*Sendslavescurrentlyrunninginthemarshallingsystemtotheslavecode+*inthenextkernelwhichhasbeenuncompressedataddress@vmlinux_addr.+*Copiesthefirst%SMP_SLAVE_SIZEbytesoftheimagetoaddress%0and+*thentellstheslavestogoto%0x60.+*/+voidsend_slaves_to_kernel(void*vmlinux_addr)+{+unsignedint*tell_them=gohere-master+slaves_goto_here;++if((unsignedlong)slaves_goto_here<SMP_SLAVE_SIZE){+if((unsignedlong)vmlinux_addr<SMP_SLAVE_SIZE)+fatal("ERROR: slaves were not marshaled before "+"decompressing the kernel to 0!\n");+move_slaves_up();+send_slaves_to_kernel(vmlinux_addr);+return;+}++wait_slaves_moved();++if(vmlinux_addr){+memcpy((void*)0,vmlinux_addr,SMP_SLAVE_SIZE);+flush_cache((void*)0,SMP_SLAVE_SIZE);+}else{+printf("kernel was decompressed to 0\n\r");+}+check_slave_errors(slaves_goto_here);++checkout_slaves_to_kernel(tell_them);+}
@@ -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 */typedefvoid(*kernel_entry_t)(unsignedlongr3,unsignedlongr4,void*r5);
@@ -84,10 +85,17 @@ int ns16550_console_init(void *devp, strintmpsc_console_init(void*devp,structserial_console_data*scdp);void*simple_alloc_init(char*base,unsignedlongheap_size,unsignedlonggranularity,unsignedlongmax_allocs);-externvoidflush_cache(void*,unsignedlong);+voidflush_cache(void*,unsignedlong);intdt_xlate_reg(void*node,intres,unsignedlong*addr,unsignedlong*size);intdt_xlate_addr(void*node,u32*buf,intbuflen,unsignedlong*xlated_addr);+/* marshal slave cpus around to kernel */+voidmove_slaves_up(void);+voidmove_slaves_here(void*where);+voidsend_slaves_to_kernel(void*vmlinux_addr);+voidslaves_are_low(void);+voidwait_slaves_moved(void);+staticinlinevoid*finddevice(constchar*name){return(dt_ops.finddevice)?dt_ops.finddevice(name):NULL;
From: Milton Miller <hidden> Date: 2007-07-10 22:09:26
Add a set of library routines to manage gross memory allocations.
This code uses an array in bss to store upto 32 entrys with merging
representing a range of memory below rmo_end (aka end of real mode
memory at 0).
To use this code, a platform would set rmo_end, call occupy_memory,
then then call init_malloc for fine grain allocation.
Also, an optional vmlinux_alloc.
Signed-off-by: Milton Miller <redacted>
---
Fixed a bug when extending across multiple ranges.
Cleaned up documentation
Index: work.git/arch/powerpc/boot/memranges.c
===================================================================
@@ -0,0 +1,242 @@+/*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram;ifnot,writetotheFreeSoftware+*Foundation,51FranklinStreet,FifthFloor,Boston,MA02110-1301,USA.+*+*CopyrightIBMCorporation2007+*+*Authors:MiltonMiller<miltonm@bga.com>+*/++#include"ops.h"+#include"stdio.h"+#include"flatdevtree.h"+#include"page.h"+#include"types.h"++externchar_start[],_end[];++void*rmo_end;++staticstruct{+void*start,*end;+}ranges[32];+staticintnum_ranges;++/**+*add_occupied_range-markarangeasoccupied+*@start:startofrangepointer+*@end:endofrangepointer+*+*Marktherangefrom@startto@endasoccupied.+*Ignoreanythingabovermo_end.+*/+voidadd_occupied_range(void*start,void*end)+{+inti,j;++if(start==end)+return;+if(start>rmo_end)+return;+if(end>rmo_end)+end=rmo_end;+if(start>end)+fatal("%s: BUG: start %p > end %p\n\r",__FUNCTION__,+start,end);++printf("add %p %p: ",start,end);++for(i=0;i<num_ranges;i++)+if(start<=ranges[i].end)+break;++/* extend and merge any overlapping ranges */+if(i<num_ranges&&end>=ranges[i].start){+ranges[i].start=min(start,ranges[i].start);+for(j=i;j<num_ranges;j++)+if(end>=ranges[j].start)+end=max(end,ranges[j].end);+else+break;+ranges[i].end=end;++if(j==i+1){+printf("extending range %d to %p %p\n\r",i,+ranges[i].start,ranges[i].end);+}else{+printf("merged ranges %d to %d now %p %p\n\r",i,j,+ranges[i].start,ranges[i].end);++++i;+memmove(&ranges[i],&ranges[j],+(num_ranges-j)*sizeof(ranges[0]));+num_ranges-=j-i;+}+}else{+/* insert a new range */+if(num_ranges>=ARRAY_SIZE(ranges)-1)+fatal("Too many memory ranges to track\n");++printf("inserting range %d between %p and %p\n\r",+i,i?ranges[i-1].end:0,+i==num_ranges?rmo_end:ranges[i].start);++memmove(&ranges[i+1],&ranges[i],+(num_ranges-i)*sizeof(ranges[0]));+num_ranges++;++ranges[i].start=start;+ranges[i].end=end;+}+}++/**+*add_occupied_range_ulong-markarangeasoccupied+*@start:startofblocktooccupy+*@end:startofblocktooccupy+*+*Calladd_occupied_range()aftercastingtoulong@startand@endto+*void*pointers.+*/+voidadd_occupied_range_ulong(unsignedlongstart,unsignedlongend)+{+add_occupied_range((void*)start,(void*)end);+}++/**+*add_known_ranges-occupysomeknownregions+*@dt_blob:aflattenddevicetreetooccupy,orNULLtoskip+*+*calladd_occupied_range()forthewrapper,loadersuppliedinitrd,+*and,ifnot%NULL,thedevicetreeblob@dt_blobandanyreserved+*memoryrangestherein.+*/+voidadd_known_ranges(structboot_param_header*dt_blob)+{+unsignedlonglongrstart,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-initializemallocheapinafreememoryrange.+*+*Callsimple_alloc_initusingthelargestgapbetweenoccupiedranges.+*Doesnotconsiderbeforethefirstorafterthelastrange.+*/+voidranges_init_malloc(void)+{+inti;+unsignedlongsize=0;+void*heap_start,*heap_end;++/*+*Allowthebeginningforthekernelandtheendfor+*otherthingstheplatformmightwanttohavereserved.+*/++heap_start=NULL;/* avoid gcc warning */+for(i=1;i<num_ranges;i++){+unsignedlongnewsize;++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-anoptonalkernelallocator.+*@size:theimagesizeofthekernel+*+*Searchesforalocationtoputthekernel,thenreservethatrange+*andtheareatowhichthekernelwillrelocateitself.Firsttry+*address%0.Ifthatisblockedbyaprevioscalltoadd_occupied_range(),+*trymalloc().Ifthatalsofailssearchforfreespacebetweenthe+*occupiedrangesorbetweenthelastrangeandrmo_end.+*/+void*ranges_vmlinux_alloc(unsignedlongsize)+{+void*addr;+inti;++/* Assume _start to _end is occupied */+addr=(void*)0;+if(addr+size<ranges[0].start)+gotooccupy;++addr=malloc(size);+if(addr)+gotoout;++for(i=1;i<num_ranges;i++){+if(size<ranges[i].start-ranges[i-1].end)+gotooccupy_range;+}+if(size<rmo_end-ranges[i-1].end)+gotooccupy_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:+/*+*Assumethekernelwilldecompressto0,butdon'timplicity+*createanewgapbelowthecurrentfirstrange.+*/+if((unsignedlong)ranges[0].end<size)+add_occupied_range_ulong(0,size);++returnaddr;+}
From: Milton Miller <hidden> Date: 2007-07-10 22:09:53
Add code to check if the processor is in 64 or 32 bit mode using
only instructions from the 32 bit subset. If the processor is in
64 bit mode, switch to 32 bit mode by clearing MSR[SF].
Also add a 64 bit procedure descriptor to use as a elf64 entry
point.
Signed-off-by: Milton Miller <redacted>
---
Merged into crt0.S instead of a separate file with a separate entry
point.
Index: kernel/arch/powerpc/boot/crt0.S
===================================================================
From: Milton Miller <hidden> Date: 2007-07-10 22:10:20
This code creates a 32 bit zImage wrapper for a 32 or 64 bit PowerPC
Linux kernel. This allows you to kexec a zImage with its compressed
vmlinux instead of the uncompressed vmlinux elf. The elf is also
packaged as a 64 bit elf for use by kexec-tools for 64 bit kernels.
Limitations:
The memory node off the root with a name starting with "memory" must
contain enough free memory (not in the reserved ranges) in the first
reg range to uncompress the the kernel with padding.
Signed-off-by: Milton Miller <redacted>
---
I split a few more routines out from this file.
I removed the call serial_console_init because some of the serial
console require a loader supplied virtural address mapping which
will not exist when called from kexec.
Successfully boots from and to a 64 bit kernel when loaded at 0 and
when loaded after the kernel _end on machines with with initrds,
rtas, and various other data reserved.
The next patch needed to load using kexec-tools without an externally
generated flat device tree.
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -0,0 +1,181 @@+/*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram;ifnot,writetotheFreeSoftware+*Foundation,51FranklinStreet,FifthFloor,Boston,MA02110-1301,USA.+*+*CopyrightIBMCorporation2007+*+*Authors:MiltonMiller<miltonm@bga.com>+*/++#include"ops.h"+#include"stdio.h"+#include"flatdevtree.h"+#include"page.h"+#include"types.h"++externchar_start[];+externchar_end[];++BSS_STACK(16*1024);++staticvoidfind_console_from_tree(void)+{+intrc=-1;++if(rc){+/* no console, oh well */+}+}++/* fixme: use find_device_by_type "memory" and xlate_reg */+staticvoidfind_rmo_end(void)+{+unsignedintna,ns,reg[4],*rp;+void*devp;+intrc;++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)+fatal("didn't find memory at 0");+rp++;+}+while(--ns){+if(*rp){+/* first range >= 4G , use 2G */+rmo_end=(void*)0x80000000;+return;+}+rp++;+}+}while(0);++rmo_end=(void*)*rp;+}++/**+*setup_initial_heap-setupasmallheapinthebss+*Usingapreallocatedheap,setupforscanningthedevicetree.+*Intendedfortheinitialreadwhilethetreewillremainread-onlyso+*aminimalmallocandsearchlimitcanbeused.Thiswaywedon'thave+*lotsofdataorbsstoclear.+*/+staticvoidsetup_initial_heap(void)+{+staticcharinitial_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");+}++staticvoidearly_scan_flat_tree(structboot_param_header*dt_blob)+{+intrc;++rc=ft_init(dt_blob,dt_blob->totalsize,50);+if(rc)+fatal("couldn't initialize device-tree\n\r");++find_rmo_end();+dt_find_initrd();+}++staticvoidinit_flat_tree(structboot_param_header*dt_blob)+{+intrc;++rc=ft_init(dt_blob,dt_blob->totalsize,/* max_finddevice */1024);+if(rc)+fatal("Unable to initialize device_tree library!\n\r");+}++staticvoid*saved_vmlinux_addr;++staticvoid*kexec_vmlinux_alloc(unsignedlongsize)+{+void*addr;++addr=ranges_vmlinux_alloc(size);++saved_vmlinux_addr=addr;+returnaddr;+}++staticvoidkexec_fixups(void)+{+wait_slaves_moved();+}++staticunsignedlong(*finalize_chain)(void);++staticunsignedlongkexec_finalize(void)+{+send_slaves_to_kernel(saved_vmlinux_addr);++returnfinalize_chain();+}++voidkexec_platform_init(structboot_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+*ormodifythetreenow.+*/+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;+}
@@ -133,6 +133,12 @@ coff) platformo=$object/of.o lds=$object/zImage.coff.lds ;;+kexec)+ platformo=$object/crt0_kexec.o+ ;;+kexec64)+ platformo="-e _zimage_start_64 $object/crt0_kexec.o"+ ;; miboot|uboot) # miboot and U-boot want just the bare bits, not an ELF binary ext=bin
@@ -219,8 +223,8 @@ install: $(CONFIGURE) $(addprefix $(obj)sh-x$(srctree)/$(src)/install.sh"$(KERNELRELEASE)"vmlinuxSystem.map"$(INSTALL_PATH)"$<# anything not in $(targets)-clean-files+=$(image-)$(initrd-)zImagezImage.initrdcuImage.*treeImage.*\-otheros.bld+clean-files+=$(image-)$(initrd-)zImagezImage.initrdcuImage.*\+treeImage.*$(kexec-)# clean up files cached by wrapperclean-kernel:=vmlinux.stripvmlinux.bin
From: Milton Miller <hidden> Date: 2007-07-10 22:10:47
kexec-tools still produces a version 2 device tree, while the
libraries in the wrapper only support version 16 and later.
Add a routine to convert a v2 flat device tree to a v16 one inplace
by inserting OF_DT_NOP and chomping full path. Make space for new
headers by moving and then chomping the OF_DT_NOPs.
Signed-off-by: Milton Miller <redacted>
---
Rediffed Makefile
Index: work.git/arch/powerpc/boot/flatdevtree_conv.c
===================================================================
@@ -0,0 +1,280 @@+/*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram;ifnot,writetotheFreeSoftware+*Foundation,51FranklinStreet,FifthFloor,Boston,MA02110-1301,USA.+*+*CopyrightIBMCorporation2007+*+*Authors:MiltonMiller<miltonm@bga.com>+*/+#include"flatdevtree.h"+#include"stdio.h"+#include"ops.h"++#define MIN_VERSION 2+#define OUT_VERSION 16+#define OUT_COMPAT 16++#ifdef NO_CHECK+staticintcheck_v123_tree(u32*start,u32*limit)+{+return0;+}+#else+/**+*check_v123_tree-checkintegretyofaversion1,2,or3tree+*@start:thestartofthedevicetreestruct+*@limit:theendoftheregionforthestruct+*structuralchecksondevice_tree+*/+staticintcheck_v123_tree(u32*start,u32*limit)+{+u32len;+intdepth=0;+u32*dtp=start;++while(dtp<limit)+switch(*dtp){+caseOF_DT_END:+if(depth)+return-1;+return++dtp-start;+caseOF_DT_NOP:+dtp++;+break;+caseOF_DT_END_NODE:+dtp++;+depth--;+break;+caseOF_DT_BEGIN_NODE:+len=strlen((char*)(++dtp));+/* check path is suffix to previous? */+dtp+=1+(len/4);+depth++;+break;+caseOF_DT_PROP:+len=dtp[1];+dtp+=3;+if((len>=8)&&((long)dtp&4))+dtp++;+dtp+=(len+3)/4;+break;+default:+return-1;+}+return-1;/* no OF_DT_END */+}+#endif++/**+*nop_to_v16-add%OF_DT_NOPtohidealignmentdifferences+*@dtp:pointertothebeginningofthestructareatomodify+*insert%OF_DT_NOPintothedt_struct@dtptomakeitv16fromv1,2,or3.+*/+staticintnop_to_v16(u32*dtp)+{+intnops=0;+char*p,*s;+intlen;+u32*next;++while(*dtp!=OF_DT_END)+switch(*dtp){+caseOF_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;+caseOF_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);+caseOF_DT_NOP:+nops++;+/* fall through */+caseOF_DT_END_NODE:+dtp++;+break;+}+returnnops;+}++#if MIN_VERSION < 3 || OUT_VERSION > 16+/**+*move_nops_fwd-movenopsinav16dt_structtothebeginning+*@start-devicetreestartingaddress+*@count-numberof%OF_DT_NOPcellstomove+*/+staticvoidmove_nops_fwd(u32*start,intcount)+{+u32*dtp=start;+intlen;+while(count)+switch(*dtp){+caseOF_DT_NOP:+memmove(start+1,start,(dtp-start)*4);+*start++=OF_DT_NOP;+dtp++;+count--;+break;+caseOF_DT_END_NODE:+dtp++;+break;+caseOF_DT_BEGIN_NODE:+len=strlen((char*)(++dtp));+dtp+=1+len/4;+break;+caseOF_DT_PROP:+len=dtp[1];+dtp+=3+(len+3)/4;+break;+caseOF_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_inplaceupgradetheversionofaboot_param_header+*@tree:pointertothedevicetreeheadertoconvert+*+*Convertsav1,2,3devicetree(ofatleastMIN_VERSION)+*inplacetoOUT_VERSION(16)format,usablebyflatdevtree.c+*/+voidconv_flattree_inplace(structboot_param_header*tree)+{+u32*dtp;+u32need=0,nops;+intslen;++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;++/*+*movemem_rsvmapanddt_stringsiftheyarebeforedt_struct+*ontoournops.Adjuststartaddressesforthe3sections.+*/+if((tree->off_mem_rsvmap<tree->off_dt_struct)||+(tree->off_dt_strings<tree->off_dt_struct)){+intstart,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){+intend=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;+}
@@ -162,6 +162,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
From: Milton Miller <hidden> Date: 2007-07-10 22:11:15
This code provides a console write that calls put-term-char.
To avoid PIC relocation of the absolute rtas addresses, hide the
actual call to rtas in assembly and declare all variables as int.
An instantiated rtas will be protected by a reserved range in the
device tree, so no explicit call to add_occupied_range is needed
here.
Signed-off-by: Milton Miller <redacted>
---
Removed use_call_rtas(); instead put asm() at the file level.
Index: work.git/arch/powerpc/boot/rtas.c
===================================================================
From: Milton Miller <hidden> Date: 2007-07-10 22:11:42
Add a library to search through a cpio or initramfs to a specified
path contained in a cpio.
Signed-off-by: Milton Miller <redacted>
---
Status: tested and working.
This file is designed to also be usable in a stand alone user space
application.
Index: work.git/arch/powerpc/boot/cpio.c
===================================================================
@@ -0,0 +1,306 @@+/*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram;ifnot,writetotheFreeSoftware+*Foundation,51FranklinStreet,FifthFloor,Boston,MA02110-1301,USA.+*+*Copyright2007IBMCorporation.+*+*Authors:MiltonMiller<miltonm@bga.com>+*+*/++#include"cpio.h"+#include"string.h"++structcpio_header{+charino[8];+charmode[8];+charuid[8];+chargid[8];+charnlink[8];+charmtime[8];+charfilesize[8];+charmaj[8];+charmin[8];+charrmaj[8];+charrmin[8];+charnamesize[8];+charchksum[8];+}cpio_header_buf;++staticintcheck_magic(charmagic[6])+{+return!memcmp(magic,"070701",6)||!memcmp(magic,"070702",6);+}++staticintread_magic(structgunzip_state*stream)+{+intlen;+charmagic[6];++len=gunzip_partial(stream,magic,sizeof(magic));+if(len==0)+return0;++if(len==sizeof(magic)&&check_magic(magic))+returnlen;+++/* Not the right magic or short read. We might have stumbled+*ontoacompressedarchiveimmediatelyfollowingan+*uncompressedone,orjustsomeNULbytesattheendofthe+*archive.Informthehigherlayersbythenegativelength.+*/+return-len;+}++staticintget_cpio_header(structgunzip_state*stream)+{+intlen;++len=read_magic(stream);+if(len<=0)+returnlen;++gunzip_exactly(stream,&cpio_header_buf,sizeof(cpio_header_buf));+len+=sizeof(cpio_header_buf);++returnlen;+}++staticunsignedintcpio_str_to_num(charhexascii[8])+{+unsignedintnum=0;+charc;+intd;++for(d=0;d<8;d++){+c=hexascii[d];+num<<=4;+if(c>='0'&&c<='9'){+num+=c-'0';+}elseif(c>='A'&&c<='F'){+num+=c-'A'+10;+}elseif(c>='a'&&c<='f'){+num+=c-'a'+10;+}else{+cpio_error("bad cpio archive header: "+"invalid a hex digit");+}+}++returnnum;+}++staticcharname_buf[MAX_PATH+1];+staticconstcharcpio_end[]="TRAILER!!!";+#define CPIO_END_LEN sizeof(cpio_end)++/* check_next_file+*Lookfor@pathin@stream.Set@consumedtothenumberofbytes+*succesfullyreadandprocessed.return1onmatch,0fordiscarding+*anunmatchedfile,-1onendofarchive(eitherdetectedtraileror+*EOFonstream),or-(1+bytesread)forashortreadorbadmagic+*number.(Fortheshortorbadread,theconsumedisnotchanged).+*/+staticintcheck_next_file(char*path,intpathlen,+structgunzip_state*stream,int*consumed)+{+intlen,total,match;++if(pathlen>MAX_PATH){+cpio_error("path too long to search\n");+}+total=get_cpio_header(stream);+if(total<=0)+returntotal-1;++len=cpio_str_to_num(cpio_header_buf.namesize);++if(len==pathlen||len==CPIO_END_LEN){+gunzip_exactly(stream,name_buf,len);+total+=len;+match=!strcmp(name_buf,path);+if(!match&&!cpio_str_to_num(cpio_header_buf.filesize))+match=-!strcmp(name_buf,cpio_end);+}else{+gunzip_discard(stream,len);+total+=len;+name_buf[0]='\0';+match=0;+}++len=total%4;+if(len){+gunzip_discard(stream,4-len);+total+=4-len;+}++if(!match){+len=cpio_str_to_num(cpio_header_buf.filesize);+gunzip_discard(stream,len);+total+=len;++len=total%4;+if(len){+gunzip_discard(stream,4-len);+total+=4-len;+}+}++*consumed+=total;+returnmatch;+}++staticchar*this_buf;+staticintthis_archive;++/* find_in_cpio.+*findapathname@pathinasinglecpioarchivedescribedby@stream.+*Returnisthesameascheck_next_file.+*/+intfind_in_cpio(char*path,structgunzip_state*stream)+{+intfound;+intpathlen=strlen(path)+1;++this_archive=0;+do{+found=check_next_file(path,pathlen,stream,&this_archive);+}while(found==0);++returnfound;+}++/* find_in_initramfs+*Searchainitramfsbufferforagivenpathname.Returns0on+*notfound,or1with@statereadytoreadthefile.+*+*Note:thisreturnsthefirstmatch,notthelast.Thekernel+*decompressoreffectivlyusesthelastmatch.Thiscodealso+*doesn'tworryaboutthedirectoriesinthepathexisting.+*/+intfind_in_initramfs(char*path,char*buf,intlen,+structgunzip_state*stream)+{+intfound,total=0;+intpathlen=strlen(path)+1;+int*ibuf;++do{+/* get to word boundary, but stop if not NUL */+for(;total%4&&total<len-4;total++)+if(buf[total])+break;++if((total%4)==0){+/* fast forward over NUL words. */+for(ibuf=(int*)&buf[total];+total<len-sizeof(*ibuf)&&!*ibuf;+ibuf++)+total+=sizeof(*ibuf);+}++/* check remainder of a short archive -- it must all be+*zeroasbothgzipheaderandcpioheaderarebigger+*thanthis.+*/+if(total>=len-4){+for(;total<len;total++){+if(buf[len]){+cpio_error("Junk at end of buffer");+}+}+break;+}elseif(total%4){+/*+*Ifweareunalingedandnotattheendofthebuffer+*wemustbefollowingacompressedarchive.Only+*NULandgzipheadersareallowed.WeskippedNUL.+*/+if(!(buf[total]==0x1b&&buf[total+1]==0x8b))+cpio_error("unalinged junk in buffer");+}++this_buf=buf+total;+this_archive=0;++gunzip_start(stream,this_buf,len-total);++do{+found=check_next_file(path,pathlen,stream,+&this_archive);+}while(!found);++if(found>0)+returnfound;++if(stream->s.workspace){+intdiscard;++if(found<-1){+cpio_error("Junk in compressed archive");+}++/* we either found EOF or TRAILER!!!. In the later+*caseweneedtodiscardtotheendofthegzip+*contents.+*/+do{+discard=gunzip_partial(stream,name_buf,+MAX_PATH);+this_archive+=discard;+}while(discard);++/*+*Peekathowmanyinputbyteswereconsumed.+*resetourconsumedinput.+*/+total+=stream->s.total_in+8;++/* clean up zlib */+discard=gunzip_finish(stream,name_buf,0);+}else{+if(this_archive%4){+cpio_error("Archive not multiple of 4?");+}+total+=this_archive;++if(!this_archive){+cpio_error("junk between archives");+}+/* don't check the < -1 of found, it might be an+*archive.Thiswillbecaughtbythe!this_archive+*checkonthenextloop.+*/+}+}while(total<len);++return0;+}++voidget_cpio_info(void**archive_start,int*consumed)+{+*archive_start=this_buf;+*consumed=this_archive;+}++intget_cpio_file_mode(void)+{+returncpio_str_to_num(cpio_header_buf.mode);+}++intget_cpio_file_size(void)+{+returncpio_str_to_num(cpio_header_buf.filesize);+}
From: Milton Miller <hidden> Date: 2007-07-10 22:12:09
Allow the boot wrapper to obtain the vmlinux elf from an external
platform supplied location instead of embedding it at link time.
When used with the cpio library, the kernel can be stored as a file
an file in the initramfs, allowing the same (compressed) copy of
the vmlinux elf to be used for both the kernel to execute and any
user space tools such as kexec (for reboot), crash dump, or oprofile.
Another use would be to uncompress directly from a memory mapped
region such as a flash partition.
Signed-off-by: Milton Miller <redacted>
---
Check for no find_vmlinuz method before calling it.
The source and source len varables are passed to the routine only
to allow them to be printed later.
This patch follows the current policy of using linker attached data
in preference to platform supplied data (matching the initrd and
command line).
While the platform could supply archive-start, length, and offset,
in loader data, it would require decompressing the skiped portion
twice when the offset is not known in advance.
The prpmc2800 code shows that this hook to find the kernel and the
the code to uncompress the header may want to be separated from the
vmlinux alloc, decompress, and flush code. I haven't explored an
actual split.
Index: work.git/arch/powerpc/boot/main.c
===================================================================
@@ -47,10 +47,19 @@ static struct addr_range prep_kernel(voistructelf_infoei;intlen;-/* gunzip the ELF header of the kernel */-gunzip_start(&gzstate,vmlinuz_addr,vmlinuz_size);-gunzip_exactly(&gzstate,elfheader,sizeof(elfheader));+/* Initialze zlib. Any attached kernel overrides find_vmlinuz */+if(vmlinuz_size)+gunzip_start(&gzstate,vmlinuz_addr,vmlinuz_size);+else+if(platform_ops.find_vmlinuz)+platform_ops.find_vmlinuz(&gzstate,&vmlinuz_addr,+&vmlinuz_size);+else+fatal("Can't find a kernel to boot: no attached "+"vmlinuz and no find_vmlinuz method\n\r");+/* gunzip and parse the ELF header of the kernel */+gunzip_exactly(&gzstate,elfheader,sizeof(elfheader));if(!parse_elf64(elfheader,&ei)&&!parse_elf32(elfheader,&ei))fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
From: Milton Miller <hidden> Date: 2007-07-10 22:12:36
Teach the kexec platform to find the vmlinux from the initramfs.
The implementation searches the initramfs for a file specified
by the boot-file property in chosen.
Signed-off-by: Milton Miller <redacted>
---
Status: working.
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -92,6 +94,51 @@ static void find_rmo_end(void)rmo_end=(void*)*rp;}+voidfind_vmlinux_in_initramfs(structgunzip_state*state,+void**srcp,unsignedlong*lenp)+{+void*devp;+intrc;+charpath[MAX_PATH];++if(!loader_info.initrd_size)+dt_find_initrd();+if(!loader_info.initrd_size)+fatal("find_vmlinux: no initramfs");+devp=finddevice("/chosen");+if(!devp)+fatal("find_vmlinux: no /chosen to find vmlinux");+rc=getprop(devp,"boot-file",path,sizeof(path));+if(rc<0)+fatal("find_vmlinux: no boot-file property in /chosen")+elseif(rc==0||rc>MAX_PATH)+fatal("boot-file too long in /chosen")++rc=find_in_initramfs(path,(void*)loader_info.initrd_addr,+loader_info.initrd_size,state);+if(!rc)+fatal("find_vmlinux: couldn't find boot-file %s in initramfs",+path);++rc=get_cpio_file_mode();+if(!S_ISREG(rc))+fatal("find_vmlinux: boot-file %s is not a regular file",+path)++get_cpio_info(srcp,&rc);+*lenp=get_cpio_file_size();+}++voidcpio_error(char*msg)+{+void*srcp;+intread;++get_cpio_info(&srcp,&read);++fatal("cpio_error at %p + 0x%x(%d): %s",srcp,read,read,msg);+}+/***setup_initial_heap-setupasmallheapinthebss*Usingapreallocatedheap,setupforscanningthedevicetree.
From: Milton Miller <hidden> Date: 2007-07-10 22:12:43
Allow the boot wrapper code to be linked without an attached vmlinux.
Rather than invent a new syntax to invoke the wrapper, attach the
stripped version of empty.o, which produces the same result.
This new intermediary is called zBoot.
Signed-off-by: Milton Miller <redacted>
---
Initial attempts to remove the dependency on vmlinux in the arch Makefile
have lead to multiple make commands competing in the boot directory and
destroying each other.
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
@@ -227,7 +232,7 @@ clean-files += $(image-) $(initrd-) zImatreeImage.*$(kexec-)# clean up files cached by wrapper-clean-kernel:=vmlinux.stripvmlinux.bin+clean-kernel:=vmlinux.stripvmlinux.binempty.o.binclean-kernel+=$(addsuffix.gz,$(clean-kernel))# If not absolute clean-files are relative to $(obj).clean-files+=$(addprefix$(objtree)/,$(clean-kernel))
From: Milton Miller <hidden> Date: 2007-07-10 22:12:52
The kexec code is doing strange contortions with dtops.finalize and
platform_ops.vmlinux_alloc to manage the slave cpus. Add a hook
with the needed information.
Signed-off-by: Milton Miller <redacted>
---
Index: work.git/arch/powerpc/boot/kexec.c
===================================================================
@@ -212,6 +212,9 @@ void start(void)elseprintf(" using OF tree (promptr=%p)\n\r",loader_info.promptr);+if(platform_ops.start_smp)+platform_ops.start_smp(vmlinux.addr);+if(console_ops.close)console_ops.close();
From: Milton Miller <hidden> Date: 2007-07-10 22:12:59
An example using the marshalling code that can be entered by sreset.
By linking the marshalling code is at 0 and differentiating a cpu
id from a device tree, it also works for kexec.
Signed-off-by: Milton Miller <redacted>
---
For reference only, not intended to be merged.
Index: work.git/arch/powerpc/boot/crt0_sample.S
===================================================================
@@ -0,0 +1,62 @@+/*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,+*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe+*GNUGeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense+*alongwiththisprogram;ifnot,writetotheFreeSoftware+*Foundation,51FranklinStreet,FifthFloor,Boston,MA02110-1301,USA.+*+*CopyrightIBMCorporation2007+*+*Authors:MiltonMiller<miltonm@bga.com>+*/++#include"ops.h"+#include"reg.h"+#include"flatdevtree.h"++externstructboot_param_header_dtb_start[],_dtb_end[];+structboot_param_header*sample_dt_blob;++externunsignedintget_cpu_id(void);+externunsignedintcpus_found,cpus_expected;++voidplatform_init(unsignedlongboot_cpu_id)+{+if(boot_cpu_id>1024){+sample_dt_blob=(void*)boot_cpu_id;+boot_cpu_id=get_cpu_id();+}++if(!sample_dt_blob)+if(_dtb_start!=_dtb_end)+sample_dt_blob=_dtb_start;++if(!sample_dt_blob)+sample_dt_blob=31*1024*1024+(void*)0;++if(sample_dt_blob->magic!=OF_DT_HEADER)+fatal("No device tree at %p\n",sample_dt_blob);++if(sample_dt_blob->version<2)+conv_flattree_inplace(sample_dt_blob);++sample_dt_blob->boot_cpuid_phys=boot_cpu_id;++if(cpus_found&&cpus_found<cpus_expected){+HMT_LOW;+while(cpus_found<cpus_expected){+barrier();+}+HMT_MEDIUM;+}++kexec_platform_init(sample_dt_blob);+}
@@ -139,6 +139,9 @@ kexec) kexec64) platformo="-e _zimage_start_64 $object/crt0_kexec.o" ;;+sample)+ platformo="$object/crt0_sample.o $object/sample.o"+ ;; miboot|uboot) # miboot and U-boot want just the bare bits, not an ELF binary ext=bin
+ /* Check if the processor is running in 32 bit mode, using
+ * only 32 bit instructions which should be safe on 32 and
+ * 64 bit processors.
+ *
+ * Subtract the bottom 32 bits of MSR from the full value
+ * recording the result. Since MSR[SF] is in the high word,
+ * the result will be not-equal iff in 32 bit mode (either
+ * the processor is a 32 bit processor or MSR[SF] = 0).
+ */
The other way around -- the processor is 64-bit and MSR[SF]=1.
Great trick btw! :-)
+ mfmsr r0 /* grab whole msr */
+ rlwinm r8,r0,0,0,31 /* extract bottom word */
+ subf. r8,r8,r0 /* subtract, same? */
+ beq 0f /* yes: we are 32 bit mode */
Allow the boot wrapper to obtain the vmlinux elf from an external
platform supplied location instead of embedding it at link time.
When used with the cpio library, the kernel can be stored as a file
an file in the initramfs, allowing the same (compressed) copy of
the vmlinux elf to be used for both the kernel to execute and any
user space tools such as kexec (for reboot), crash dump, or oprofile.
The memory node off the root with a name starting with "memory" must
contain enough free memory (not in the reserved ranges) in the first
reg range to uncompress the the kernel with padding.
There can be many nodes called "/memory". You probably
want to use the node pointed to by /chosen/memory instead?
Segher
From: David Gibson <hidden> Date: 2007-07-19 02:10:59
On Tue, Jul 10, 2007 at 05:07:38PM -0500, Milton Miller wrote:
Some platforms have a boot agent that can create or modify properties in
the device-tree and load images into memory. Provide a helper to set
loader_info used by prep_initrd().
Signed-off-by: Milton Miller <redacted>
Looks reasonable.
Acked-by: David Gibson <redacted>
--
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
From: David Gibson <hidden> Date: 2007-07-19 02:11:38
On Tue, Jul 10, 2007 at 05:08:05PM -0500, Milton Miller wrote:
Record the number of header bytes skipped in the total bytes read field.
This is needed for the initramfs parsing code to find the end of the zip file.
Signed-off-by: Milton Miller <redacted>
Ok... I assume you've checked that this is actually the correct
semantics for that field of the zlib structure?
--
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
From: David Gibson <hidden> Date: 2007-07-19 02:39:17
On Tue, Jul 10, 2007 at 05:08:32PM -0500, Milton Miller wrote:
Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.
Signed-off-by: Milton Miller <redacted>
Hrm... I guess this is sufficient. Since we'll shortly be blowing
away the state anyway, the lack of call to inflateEnd doesn't really
matter. Originally I was going to make this function call fatal() if
the destination didn't have enough space to contain the decompressed
tail of the image, but obviously I decided aginst that.
But, if you're going to do this, you might as well get rid of
gunzip_finish() entirely, and have the callers using gunzip_partial
instead.
--
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
From: Milton Miller <hidden> Date: 2007-07-19 04:01:47
On Jul 18, 2007, at 9:39 PM, David Gibson wrote:
On Tue, Jul 10, 2007 at 05:08:32PM -0500, Milton Miller wrote:
quoted
Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.
Signed-off-by: Milton Miller <redacted>
Hrm... I guess this is sufficient. Since we'll shortly be blowing
away the state anyway, the lack of call to inflateEnd doesn't really
matter. Originally I was going to make this function call fatal() if
the destination didn't have enough space to contain the decompressed
tail of the image, but obviously I decided aginst that.
But, if you're going to do this, you might as well get rid of
gunzip_finish() entirely, and have the callers using gunzip_partial
instead.
Huh? I stilll call inflateEnd if s.workspace is not NULL, I just
don't duplicate gunzip_partial in gunzip_finish. Calling inflateEnd
will mean a future call would return an error in the compressed case
instead of the behavior in the non-compressed case that changes with
this patch from get same data again to get further data that may exist
after the patch.
Yes, one can argue that we don't need to call inflateEnd, but I didn't
make that change in this patch.
Or were you suggesting that we really don't need the finish function?
milton
From: David Gibson <hidden> Date: 2007-07-19 04:31:25
On Wed, Jul 18, 2007 at 11:01:37PM -0500, Milton Miller wrote:
On Jul 18, 2007, at 9:39 PM, David Gibson wrote:
quoted
On Tue, Jul 10, 2007 at 05:08:32PM -0500, Milton Miller wrote:
quoted
Call gunzip_partial to calculate the remaining length and copy the
data to the user buffer. This makes it shorter and reduces
duplication.
Signed-off-by: Milton Miller <redacted>
Hrm... I guess this is sufficient. Since we'll shortly be blowing
away the state anyway, the lack of call to inflateEnd doesn't really
matter. Originally I was going to make this function call fatal() if
the destination didn't have enough space to contain the decompressed
tail of the image, but obviously I decided aginst that.
But, if you're going to do this, you might as well get rid of
gunzip_finish() entirely, and have the callers using gunzip_partial
instead.
Huh? I stilll call inflateEnd if s.workspace is not NULL, I just
don't duplicate gunzip_partial in gunzip_finish. Calling inflateEnd
Oh, yes, oops. Missed the couple of lines there that didn't have a
'-' in front.
will mean a future call would return an error in the compressed case
instead of the behavior in the non-compressed case that changes with
this patch from get same data again to get further data that may exist
after the patch.
Yes, one can argue that we don't need to call inflateEnd, but I didn't
make that change in this patch.
Or were you suggesting that we really don't need the finish function?
Well, we probably don't. But for the time being.
Acked-by: David Gibson <redacted>
--
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
From: Milton Miller <hidden> Date: 2007-07-19 04:49:19
On Jul 18, 2007, at 9:11 PM, David Gibson wrote:
On Tue, Jul 10, 2007 at 05:08:05PM -0500, Milton Miller wrote:
quoted
Record the number of header bytes skipped in the total bytes read
field.
This is needed for the initramfs parsing code to find the end of the
zip file.
Signed-off-by: Milton Miller <redacted>
Ok... I assume you've checked that this is actually the correct
semantics for that field of the zlib structure?
From looking at the comments in include/linux/zlib.h, I think its
consistent. It talks about looking at that field for statistics and
for the application to record how many bytes were read during
compression. It mentions it brifely in the inflateSync to show how
many bytes have been processed. So I say its consistent to say we
have processed the header bytes in the input file.
It appears to be for recording the number of bytes processed over all
iterations.
I dig into zlib to see if this matches the beahvior of decompressing a
zlib header, but expect it would match.
milton