]> git.proxmox.com Git - grub2.git/blob - grub-core/loader/sparc64/ieee1275/linux.c
Use full initializer for initrd_ctx to avoid fatal warnings with older GCC
[grub2.git] / grub-core / loader / sparc64 / ieee1275 / linux.c
1 /* linux.c - boot Linux */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003, 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/elf.h>
21 #include <grub/elfload.h>
22 #include <grub/loader.h>
23 #include <grub/dl.h>
24 #include <grub/mm.h>
25 #include <grub/misc.h>
26 #include <grub/ieee1275/ieee1275.h>
27 #include <grub/command.h>
28 #include <grub/i18n.h>
29 #include <grub/memory.h>
30 #include <grub/lib/cmdline.h>
31 #include <grub/linux.h>
32
33 GRUB_MOD_LICENSE ("GPLv3+");
34
35 static grub_dl_t my_mod;
36
37 static int loaded;
38
39 /* /virtual-memory/translations property layout */
40 struct grub_ieee1275_translation {
41 grub_uint64_t vaddr;
42 grub_uint64_t size;
43 grub_uint64_t data;
44 };
45
46 static struct grub_ieee1275_translation *of_trans;
47 static int of_num_trans;
48
49 static grub_addr_t phys_base;
50 static grub_addr_t grub_phys_start;
51 static grub_addr_t grub_phys_end;
52
53 static grub_addr_t initrd_addr;
54 static grub_addr_t initrd_paddr;
55 static grub_size_t initrd_size;
56
57 static Elf64_Addr linux_entry;
58 static grub_addr_t linux_addr;
59 static grub_addr_t linux_paddr;
60 static grub_size_t linux_size;
61
62 static char *linux_args;
63
64 struct linux_bootstr_info {
65 int len, valid;
66 char buf[];
67 };
68
69 struct linux_hdrs {
70 /* All HdrS versions support these fields. */
71 unsigned int start_insns[2];
72 char magic[4]; /* "HdrS" */
73 unsigned int linux_kernel_version; /* LINUX_VERSION_CODE */
74 unsigned short hdrs_version;
75 unsigned short root_flags;
76 unsigned short root_dev;
77 unsigned short ram_flags;
78 unsigned int __deprecated_ramdisk_image;
79 unsigned int ramdisk_size;
80
81 /* HdrS versions 0x0201 and higher only */
82 char *reboot_command;
83
84 /* HdrS versions 0x0202 and higher only */
85 struct linux_bootstr_info *bootstr_info;
86
87 /* HdrS versions 0x0301 and higher only */
88 unsigned long ramdisk_image;
89 };
90
91 static grub_err_t
92 grub_linux_boot (void)
93 {
94 struct linux_bootstr_info *bp;
95 struct linux_hdrs *hp;
96 grub_addr_t addr;
97
98 hp = (struct linux_hdrs *) linux_addr;
99
100 /* Any pointer we dereference in the kernel image must be relocated
101 to where we actually loaded the kernel. */
102 addr = (grub_addr_t) hp->bootstr_info;
103 addr += (linux_addr - linux_entry);
104 bp = (struct linux_bootstr_info *) addr;
105
106 /* Set the command line arguments, unless the kernel has been
107 built with a fixed CONFIG_CMDLINE. */
108 if (!bp->valid)
109 {
110 int len = grub_strlen (linux_args) + 1;
111 if (bp->len < len)
112 len = bp->len;
113 memcpy(bp->buf, linux_args, len);
114 bp->buf[len-1] = '\0';
115 bp->valid = 1;
116 }
117
118 if (initrd_addr)
119 {
120 /* The kernel expects the physical address, adjusted relative
121 to the lowest address advertised in "/memory"'s available
122 property.
123
124 The history of this is that back when the kernel only supported
125 specifying a 32-bit ramdisk address, this was the way to still
126 be able to specify the ramdisk physical address even if memory
127 started at some place above 4GB.
128
129 The magic 0x400000 is KERNBASE, I have no idea why SILO adds
130 that term into the address, but it does and thus we have to do
131 it too as this is what the kernel expects. */
132 hp->ramdisk_image = initrd_paddr - phys_base + 0x400000;
133 hp->ramdisk_size = initrd_size;
134 }
135
136 grub_dprintf ("loader", "Entry point: 0x%lx\n", linux_addr);
137 grub_dprintf ("loader", "Initrd at: 0x%lx, size 0x%lx\n", initrd_addr,
138 initrd_size);
139 grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
140 grub_dprintf ("loader", "Jumping to Linux...\n");
141
142 /* Boot the kernel. */
143 asm volatile ("sethi %hi(grub_ieee1275_entry_fn), %o1\n"
144 "ldx [%o1 + %lo(grub_ieee1275_entry_fn)], %o4\n"
145 "sethi %hi(grub_ieee1275_original_stack), %o1\n"
146 "ldx [%o1 + %lo(grub_ieee1275_original_stack)], %o6\n"
147 "sethi %hi(linux_addr), %o1\n"
148 "ldx [%o1 + %lo(linux_addr)], %o5\n"
149 "mov %g0, %o0\n"
150 "mov %g0, %o2\n"
151 "mov %g0, %o3\n"
152 "jmp %o5\n"
153 "mov %g0, %o1\n");
154
155 return GRUB_ERR_NONE;
156 }
157
158 static grub_err_t
159 grub_linux_release_mem (void)
160 {
161 grub_free (linux_args);
162 linux_args = 0;
163 linux_addr = 0;
164 initrd_addr = 0;
165
166 return GRUB_ERR_NONE;
167 }
168
169 static grub_err_t
170 grub_linux_unload (void)
171 {
172 grub_err_t err;
173
174 err = grub_linux_release_mem ();
175 grub_dl_unref (my_mod);
176
177 loaded = 0;
178
179 return err;
180 }
181
182 #define FOUR_MB (4 * 1024 * 1024)
183
184 /* Context for alloc_phys. */
185 struct alloc_phys_ctx
186 {
187 grub_addr_t size;
188 grub_addr_t ret;
189 };
190
191 /* Helper for alloc_phys. */
192 static int
193 alloc_phys_choose (grub_uint64_t addr, grub_uint64_t len,
194 grub_memory_type_t type, void *data)
195 {
196 struct alloc_phys_ctx *ctx = data;
197 grub_addr_t end = addr + len;
198
199 if (type != 1)
200 return 0;
201
202 addr = ALIGN_UP (addr, FOUR_MB);
203 if (addr + ctx->size >= end)
204 return 0;
205
206 if (addr >= grub_phys_start && addr < grub_phys_end)
207 {
208 addr = ALIGN_UP (grub_phys_end, FOUR_MB);
209 if (addr + ctx->size >= end)
210 return 0;
211 }
212 if ((addr + ctx->size) >= grub_phys_start
213 && (addr + ctx->size) < grub_phys_end)
214 {
215 addr = ALIGN_UP (grub_phys_end, FOUR_MB);
216 if (addr + ctx->size >= end)
217 return 0;
218 }
219
220 if (loaded)
221 {
222 grub_addr_t linux_end = ALIGN_UP (linux_paddr + linux_size, FOUR_MB);
223
224 if (addr >= linux_paddr && addr < linux_end)
225 {
226 addr = linux_end;
227 if (addr + ctx->size >= end)
228 return 0;
229 }
230 if ((addr + ctx->size) >= linux_paddr
231 && (addr + ctx->size) < linux_end)
232 {
233 addr = linux_end;
234 if (addr + ctx->size >= end)
235 return 0;
236 }
237 }
238
239 ctx->ret = addr;
240 return 1;
241 }
242
243 static grub_addr_t
244 alloc_phys (grub_addr_t size)
245 {
246 struct alloc_phys_ctx ctx = {
247 .size = size,
248 .ret = (grub_addr_t) -1
249 };
250
251 grub_machine_mmap_iterate (alloc_phys_choose, &ctx);
252
253 return ctx.ret;
254 }
255
256 static grub_err_t
257 grub_linux_load64 (grub_elf_t elf, const char *filename)
258 {
259 grub_addr_t off, paddr, base;
260 int ret;
261
262 linux_entry = elf->ehdr.ehdr64.e_entry;
263 linux_addr = 0x40004000;
264 off = 0x4000;
265 linux_size = grub_elf64_size (elf, 0, 0);
266 if (linux_size == 0)
267 return grub_errno;
268
269 grub_dprintf ("loader", "Attempting to claim at 0x%lx, size 0x%lx.\n",
270 linux_addr, linux_size);
271
272 paddr = alloc_phys (linux_size + off);
273 if (paddr == (grub_addr_t) -1)
274 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
275 "couldn't allocate physical memory");
276 ret = grub_ieee1275_map (paddr, linux_addr - off,
277 linux_size + off, IEEE1275_MAP_DEFAULT);
278 if (ret)
279 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
280 "couldn't map physical memory");
281
282 grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
283 linux_addr, paddr, linux_size);
284
285 linux_paddr = paddr;
286
287 base = linux_entry - off;
288
289 /* Now load the segments into the area we claimed. */
290 return grub_elf64_load (elf, filename, (void *) (linux_addr - off - base), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
291 }
292
293 static grub_err_t
294 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
295 int argc, char *argv[])
296 {
297 grub_file_t file = 0;
298 grub_elf_t elf = 0;
299 int size;
300
301 grub_dl_ref (my_mod);
302
303 if (argc == 0)
304 {
305 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
306 goto out;
307 }
308
309 file = grub_file_open (argv[0]);
310 if (!file)
311 goto out;
312
313 elf = grub_elf_file (file, argv[0]);
314 if (! elf)
315 goto out;
316
317 if (elf->ehdr.ehdr32.e_type != ET_EXEC)
318 {
319 grub_error (GRUB_ERR_UNKNOWN_OS,
320 N_("this ELF file is not of the right type"));
321 goto out;
322 }
323
324 /* Release the previously used memory. */
325 grub_loader_unset ();
326
327 if (grub_elf_is_elf64 (elf))
328 grub_linux_load64 (elf, argv[0]);
329 else
330 {
331 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
332 goto out;
333 }
334
335 size = grub_loader_cmdline_size(argc, argv);
336
337 linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
338 if (! linux_args)
339 goto out;
340
341 /* Create kernel command line. */
342 grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
343 grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
344 size);
345
346 out:
347 if (elf)
348 grub_elf_close (elf);
349 else if (file)
350 grub_file_close (file);
351
352 if (grub_errno != GRUB_ERR_NONE)
353 {
354 grub_linux_release_mem ();
355 grub_dl_unref (my_mod);
356 loaded = 0;
357 }
358 else
359 {
360 grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
361 initrd_addr = 0;
362 loaded = 1;
363 }
364
365 return grub_errno;
366 }
367
368 static grub_err_t
369 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
370 int argc, char *argv[])
371 {
372 grub_size_t size = 0;
373 grub_addr_t paddr;
374 grub_addr_t addr;
375 int ret;
376 struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
377
378 if (argc == 0)
379 {
380 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
381 goto fail;
382 }
383
384 if (!loaded)
385 {
386 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
387 goto fail;
388 }
389
390 if (grub_initrd_init (argc, argv, &initrd_ctx))
391 goto fail;
392
393 size = grub_get_initrd_size (&initrd_ctx);
394
395 addr = 0x60000000;
396
397 paddr = alloc_phys (size);
398 if (paddr == (grub_addr_t) -1)
399 {
400 grub_error (GRUB_ERR_OUT_OF_MEMORY,
401 "couldn't allocate physical memory");
402 goto fail;
403 }
404 ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT);
405 if (ret)
406 {
407 grub_error (GRUB_ERR_OUT_OF_MEMORY,
408 "couldn't map physical memory");
409 goto fail;
410 }
411
412 grub_dprintf ("loader", "Loading initrd at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
413 addr, paddr, size);
414
415 if (grub_initrd_load (&initrd_ctx, argv, (void *) addr))
416 goto fail;
417
418 initrd_addr = addr;
419 initrd_paddr = paddr;
420 initrd_size = size;
421
422 fail:
423 grub_initrd_close (&initrd_ctx);
424
425 return grub_errno;
426 }
427
428 /* Helper for determine_phys_base. */
429 static int
430 get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__ ((unused)),
431 grub_memory_type_t type, void *data __attribute__ ((unused)))
432 {
433 if (type != 1)
434 return 0;
435 if (addr < phys_base)
436 phys_base = addr;
437 return 0;
438 }
439
440 static void
441 determine_phys_base (void)
442 {
443 phys_base = ~(grub_uint64_t) 0;
444 grub_machine_mmap_iterate (get_physbase, NULL);
445 }
446
447 static void
448 fetch_translations (void)
449 {
450 grub_ieee1275_phandle_t node;
451 grub_ssize_t actual;
452 int i;
453
454 if (grub_ieee1275_finddevice ("/virtual-memory", &node))
455 {
456 grub_printf ("Cannot find /virtual-memory node.\n");
457 return;
458 }
459
460 if (grub_ieee1275_get_property_length (node, "translations", &actual))
461 {
462 grub_printf ("Cannot find /virtual-memory/translations size.\n");
463 return;
464 }
465
466 of_trans = grub_malloc (actual);
467 if (!of_trans)
468 {
469 grub_printf ("Cannot allocate translations buffer.\n");
470 return;
471 }
472
473 if (grub_ieee1275_get_property (node, "translations", of_trans, actual, &actual))
474 {
475 grub_printf ("Cannot fetch /virtual-memory/translations property.\n");
476 return;
477 }
478
479 of_num_trans = actual / sizeof(struct grub_ieee1275_translation);
480
481 for (i = 0; i < of_num_trans; i++)
482 {
483 struct grub_ieee1275_translation *p = &of_trans[i];
484
485 if (p->vaddr == 0x2000)
486 {
487 grub_addr_t phys, tte = p->data;
488
489 phys = tte & ~(0xff00000000001fffULL);
490
491 grub_phys_start = phys;
492 grub_phys_end = grub_phys_start + p->size;
493 grub_dprintf ("loader", "Grub lives at phys_start[%lx] phys_end[%lx]\n",
494 (unsigned long) grub_phys_start,
495 (unsigned long) grub_phys_end);
496 break;
497 }
498 }
499 }
500
501 \f
502 static grub_command_t cmd_linux, cmd_initrd;
503
504 GRUB_MOD_INIT(linux)
505 {
506 determine_phys_base ();
507 fetch_translations ();
508
509 cmd_linux = grub_register_command ("linux", grub_cmd_linux,
510 0, N_("Load Linux."));
511 cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
512 0, N_("Load initrd."));
513 my_mod = mod;
514 }
515
516 GRUB_MOD_FINI(linux)
517 {
518 grub_unregister_command (cmd_linux);
519 grub_unregister_command (cmd_initrd);
520 }