aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2007-03-04 22:24:52 -0500
committerPaul Mackerras <paulus@samba.org>2007-03-12 22:35:01 -0400
commit79c8541924a220964f9f2cbed31eaa9fdb042eab (patch)
tree94e75bf65ea5cb0d40dfae7215ae432e1f914296 /arch
parentad9d2716cfc1cda5a7e0d7bc0db45e3af8a4adbb (diff)
[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 <dwg@au1.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/boot/main.c167
-rw-r--r--arch/powerpc/boot/of.c12
-rw-r--r--arch/powerpc/boot/ops.h1
3 files changed, 114 insertions, 66 deletions
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index 404620a9e73..05de6cfafee 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -33,24 +33,21 @@ extern char _dtb_end[];
33static struct gunzip_state gzstate; 33static struct gunzip_state gzstate;
34 34
35struct addr_range { 35struct addr_range {
36 unsigned long addr; 36 void *addr;
37 unsigned long size; 37 unsigned long size;
38 unsigned long memsize;
39}; 38};
40static struct addr_range vmlinux;
41static struct addr_range vmlinuz;
42static struct addr_range initrd;
43
44static unsigned long elfoffset;
45static int is_64bit;
46 39
47static char elfheader[256]; 40struct elf_info {
41 unsigned long loadsize;
42 unsigned long memsize;
43 unsigned long elfoffset;
44};
48 45
49typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); 46typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *);
50 47
51#undef DEBUG 48#undef DEBUG
52 49
53static int is_elf64(void *hdr) 50static int parse_elf64(void *hdr, struct elf_info *info)
54{ 51{
55 Elf64_Ehdr *elf64 = hdr; 52 Elf64_Ehdr *elf64 = hdr;
56 Elf64_Phdr *elf64ph; 53 Elf64_Phdr *elf64ph;
@@ -74,15 +71,14 @@ static int is_elf64(void *hdr)
74 if (i >= (unsigned int)elf64->e_phnum) 71 if (i >= (unsigned int)elf64->e_phnum)
75 return 0; 72 return 0;
76 73
77 elfoffset = (unsigned long)elf64ph->p_offset; 74 info->loadsize = (unsigned long)elf64ph->p_filesz;
78 vmlinux.size = (unsigned long)elf64ph->p_filesz; 75 info->memsize = (unsigned long)elf64ph->p_memsz;
79 vmlinux.memsize = (unsigned long)elf64ph->p_memsz; 76 info->elfoffset = (unsigned long)elf64ph->p_offset;
80 77
81 is_64bit = 1;
82 return 1; 78 return 1;
83} 79}
84 80
85static int is_elf32(void *hdr) 81static int parse_elf32(void *hdr, struct elf_info *info)
86{ 82{
87 Elf32_Ehdr *elf32 = hdr; 83 Elf32_Ehdr *elf32 = hdr;
88 Elf32_Phdr *elf32ph; 84 Elf32_Phdr *elf32ph;
@@ -98,7 +94,6 @@ static int is_elf32(void *hdr)
98 elf32->e_machine == EM_PPC)) 94 elf32->e_machine == EM_PPC))
99 return 0; 95 return 0;
100 96
101 elf32 = (Elf32_Ehdr *)elfheader;
102 elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff); 97 elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff);
103 for (i = 0; i < elf32->e_phnum; i++, elf32ph++) 98 for (i = 0; i < elf32->e_phnum; i++, elf32ph++)
104 if (elf32ph->p_type == PT_LOAD) 99 if (elf32ph->p_type == PT_LOAD)
@@ -106,24 +101,26 @@ static int is_elf32(void *hdr)
106 if (i >= elf32->e_phnum) 101 if (i >= elf32->e_phnum)
107 return 0; 102 return 0;
108 103
109 elfoffset = elf32ph->p_offset; 104 info->loadsize = elf32ph->p_filesz;
110 vmlinux.size = elf32ph->p_filesz; 105 info->memsize = elf32ph->p_memsz;
111 vmlinux.memsize = elf32ph->p_memsz; 106 info->elfoffset = elf32ph->p_offset;
112 return 1; 107 return 1;
113} 108}
114 109
115static void prep_kernel(unsigned long a1, unsigned long a2) 110static struct addr_range prep_kernel(void)
116{ 111{
112 char elfheader[256];
113 void *vmlinuz_addr = _vmlinux_start;
114 unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start;
115 void *addr = 0;
116 struct elf_info ei;
117 int len; 117 int len;
118 118
119 vmlinuz.addr = (unsigned long)_vmlinux_start;
120 vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);
121
122 /* gunzip the ELF header of the kernel */ 119 /* gunzip the ELF header of the kernel */
123 gunzip_start(&gzstate, (void *)vmlinuz.addr, vmlinuz.size); 120 gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
124 gunzip_exactly(&gzstate, elfheader, sizeof(elfheader)); 121 gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
125 122
126 if (!is_elf64(elfheader) && !is_elf32(elfheader)) { 123 if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) {
127 printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); 124 printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
128 exit(); 125 exit();
129 } 126 }
@@ -135,55 +132,92 @@ static void prep_kernel(unsigned long a1, unsigned long a2)
135 * the kernel bss must be claimed (it will be zero'd by the 132 * the kernel bss must be claimed (it will be zero'd by the
136 * kernel itself) 133 * kernel itself)
137 */ 134 */
138 printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); 135 printf("Allocating 0x%lx bytes for kernel ...\n\r", ei.memsize);
139 vmlinux.addr = (unsigned long)malloc(vmlinux.memsize); 136
140 if (vmlinux.addr == 0) { 137 if (platform_ops.vmlinux_alloc) {
141 printf("Can't allocate memory for kernel image !\n\r"); 138 addr = platform_ops.vmlinux_alloc(ei.memsize);
142 exit(); 139 } else {
140 if ((unsigned long)_start < ei.memsize) {
141 printf("Insufficient memory for kernel at address 0!"
142 " (_start=%lx)\n\r", _start);
143 exit();
144 }
143 } 145 }
144 146
147 /* Finally, gunzip the kernel */
148 printf("gunzipping (0x%p <- 0x%p:0x%p)...", addr,
149 vmlinuz_addr, vmlinuz_addr+vmlinuz_size);
150 /* discard up to the actual load data */
151 gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader));
152 len = gunzip_finish(&gzstate, addr, ei.memsize);
153 printf("done 0x%lx bytes\n\r", len);
154
155 flush_cache(addr, ei.loadsize);
156
157 return (struct addr_range){addr, ei.memsize};
158}
159
160static struct addr_range prep_initrd(struct addr_range vmlinux,
161 unsigned long initrd_addr,
162 unsigned long initrd_size)
163{
164 void *devp;
165 u32 initrd_start, initrd_end;
166
167 /* If we have an image attached to us, it overrides anything
168 * supplied by the loader. */
169 if (_initrd_end > _initrd_start) {
170 printf("Attached initrd image at 0x%p-0x%p\n\r",
171 _initrd_start, _initrd_end);
172 initrd_addr = (unsigned long)_initrd_start;
173 initrd_size = _initrd_end - _initrd_start;
174 } else if (initrd_size > 0) {
175 printf("Using loader supplied ramdisk at 0x%lx-0x%lx\n\r",
176 initrd_addr, initrd_addr + initrd_size);
177 }
178
179 /* If there's no initrd at all, we're done */
180 if (! initrd_size)
181 return (struct addr_range){0, 0};
182
145 /* 183 /*
146 * Now find the initrd 184 * If the initrd is too low it will be clobbered when the
147 * 185 * kernel relocates to its final location. In this case,
148 * First see if we have an image attached to us. If so 186 * allocate a safer place and move it.
149 * allocate memory for it and copy it there.
150 */ 187 */
151 initrd.size = (unsigned long)(_initrd_end - _initrd_start); 188 if (initrd_addr < vmlinux.size) {
152 initrd.memsize = initrd.size; 189 void *old_addr = (void *)initrd_addr;
153 if (initrd.size > 0) { 190
154 printf("Allocating 0x%lx bytes for initrd ...\n\r", 191 printf("Allocating 0x%lx bytes for initrd ...\n\r",
155 initrd.size); 192 initrd_size);
156 initrd.addr = (unsigned long)malloc((u32)initrd.size); 193 initrd_addr = (unsigned long)malloc(initrd_size);
157 if (initrd.addr == 0) { 194 if (! initrd_addr) {
158 printf("Can't allocate memory for initial " 195 printf("Can't allocate memory for initial "
159 "ramdisk !\n\r"); 196 "ramdisk !\n\r");
160 exit(); 197 exit();
161 } 198 }
162 printf("initial ramdisk moving 0x%lx <- 0x%lx " 199 printf("Relocating initrd 0x%p <- 0x%p (0x%lx bytes)\n\r",
163 "(0x%lx bytes)\n\r", initrd.addr, 200 initrd_addr, old_addr, initrd_size);
164 (unsigned long)_initrd_start, initrd.size); 201 memmove((void *)initrd_addr, old_addr, initrd_size);
165 memmove((void *)initrd.addr, (void *)_initrd_start,
166 initrd.size);
167 printf("initrd head: 0x%lx\n\r",
168 *((unsigned long *)initrd.addr));
169 } else if (a2 != 0) {
170 /* Otherwise, see if yaboot or another loader gave us an initrd */
171 initrd.addr = a1;
172 initrd.memsize = initrd.size = a2;
173 printf("Using loader supplied initrd at 0x%lx (0x%lx bytes)\n\r",
174 initrd.addr, initrd.size);
175 } 202 }
176 203
177 /* Eventually gunzip the kernel */ 204 printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd_addr));
178 printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", 205
179 vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); 206 /* Tell the kernel initrd address via device tree */
180 /* discard up to the actual load data */ 207 devp = finddevice("/chosen");
181 gunzip_discard(&gzstate, elfoffset - sizeof(elfheader)); 208 if (! devp) {
182 len = gunzip_finish(&gzstate, (void *)vmlinux.addr, 209 printf("Device tree has no chosen node!\n\r");
183 vmlinux.memsize); 210 exit();
184 printf("done 0x%lx bytes\n\r", len); 211 }
212
213 initrd_start = (u32)initrd_addr;
214 initrd_end = (u32)initrd_addr + initrd_size;
215
216 setprop(devp, "linux,initrd-start", &initrd_start,
217 sizeof(initrd_start));
218 setprop(devp, "linux,initrd-end", &initrd_end, sizeof(initrd_end));
185 219
186 flush_cache((void *)vmlinux.addr, vmlinux.size); 220 return (struct addr_range){(void *)initrd_addr, initrd_size};
187} 221}
188 222
189/* A buffer that may be edited by tools operating on a zImage binary so as to 223/* 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;
223 257
224void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) 258void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
225{ 259{
260 struct addr_range vmlinux, initrd;
226 kernel_entry_t kentry; 261 kernel_entry_t kentry;
227 char cmdline[COMMAND_LINE_SIZE]; 262 char cmdline[COMMAND_LINE_SIZE];
228 unsigned long ft_addr = 0; 263 unsigned long ft_addr = 0;
@@ -242,7 +277,8 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
242 printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", 277 printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r",
243 _start, sp); 278 _start, sp);
244 279
245 prep_kernel(a1, a2); 280 vmlinux = prep_kernel();
281 initrd = prep_initrd(vmlinux, a1, a2);
246 282
247 /* If cmdline came from zimage wrapper or if we can edit the one 283 /* If cmdline came from zimage wrapper or if we can edit the one
248 * in the dt, print it out and edit it, if possible. 284 * 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)
271 if (ft_addr) 307 if (ft_addr)
272 kentry(ft_addr, 0, NULL); 308 kentry(ft_addr, 0, NULL);
273 else 309 else
274 /* XXX initrd addr/size should be passed in properties */ 310 kentry((unsigned long)initrd.addr, initrd.size, promptr);
275 kentry(initrd.addr, initrd.size, promptr);
276 311
277 /* console closed so printf below may not work */ 312 /* console closed so printf below may not work */
278 printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); 313 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 0182f384f3e..044f34770b9 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)
208 } 208 }
209} 209}
210 210
211static void *of_vmlinux_alloc(unsigned long size)
212{
213 void *p = malloc(size);
214
215 if (!p) {
216 printf("Can't allocate memory for kernel image!\n\r");
217 exit();
218 }
219 return p;
220}
221
211static void of_exit(void) 222static void of_exit(void)
212{ 223{
213 call_prom("exit", 0, 0); 224 call_prom("exit", 0, 0);
@@ -261,6 +272,7 @@ int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end)
261 platform_ops.image_hdr = of_image_hdr; 272 platform_ops.image_hdr = of_image_hdr;
262 platform_ops.malloc = of_try_claim; 273 platform_ops.malloc = of_try_claim;
263 platform_ops.exit = of_exit; 274 platform_ops.exit = of_exit;
275 platform_ops.vmlinux_alloc = of_vmlinux_alloc;
264 276
265 dt_ops.finddevice = of_finddevice; 277 dt_ops.finddevice = of_finddevice;
266 dt_ops.getprop = of_getprop; 278 dt_ops.getprop = of_getprop;
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h
index 8abb6516bb7..fa62ff223e7 100644
--- a/arch/powerpc/boot/ops.h
+++ b/arch/powerpc/boot/ops.h
@@ -25,6 +25,7 @@ struct platform_ops {
25 void (*free)(void *ptr); 25 void (*free)(void *ptr);
26 void * (*realloc)(void *ptr, unsigned long size); 26 void * (*realloc)(void *ptr, unsigned long size);
27 void (*exit)(void); 27 void (*exit)(void);
28 void * (*vmlinux_alloc)(unsigned long size);
28}; 29};
29extern struct platform_ops platform_ops; 30extern struct platform_ops platform_ops;
30 31