]> git.proxmox.com Git - grub2.git/blame - 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
CommitLineData
d8e1836c 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>
d8e1836c 25#include <grub/misc.h>
26#include <grub/ieee1275/ieee1275.h>
0552ff9f 27#include <grub/command.h>
809bbfeb 28#include <grub/i18n.h>
df3df23d 29#include <grub/memory.h>
25953e10 30#include <grub/lib/cmdline.h>
92750e4c 31#include <grub/linux.h>
d8e1836c 32
e745cf0c
VS
33GRUB_MOD_LICENSE ("GPLv3+");
34
d8e1836c 35static grub_dl_t my_mod;
36
37static int loaded;
38
39/* /virtual-memory/translations property layout */
40struct grub_ieee1275_translation {
41 grub_uint64_t vaddr;
42 grub_uint64_t size;
43 grub_uint64_t data;
44};
45
46static struct grub_ieee1275_translation *of_trans;
47static int of_num_trans;
48
49static grub_addr_t phys_base;
50static grub_addr_t grub_phys_start;
51static grub_addr_t grub_phys_end;
52
53static grub_addr_t initrd_addr;
54static grub_addr_t initrd_paddr;
55static grub_size_t initrd_size;
56
57static Elf64_Addr linux_entry;
58static grub_addr_t linux_addr;
59static grub_addr_t linux_paddr;
60static grub_size_t linux_size;
61
62static char *linux_args;
63
d8e1836c 64struct linux_bootstr_info {
65 int len, valid;
66 char buf[];
67};
68
69struct 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
91static grub_err_t
92grub_linux_boot (void)
93{
94 struct linux_bootstr_info *bp;
d8e1836c 95 struct linux_hdrs *hp;
96 grub_addr_t addr;
97
98 hp = (struct linux_hdrs *) linux_addr;
99
4241d2b1 100 /* Any pointer we dereference in the kernel image must be relocated
d8e1836c 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. */
320dd174
VS
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");
d8e1836c 154
155 return GRUB_ERR_NONE;
156}
157
158static grub_err_t
159grub_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
169static grub_err_t
170grub_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
ed12a003
CW
184/* Context for alloc_phys. */
185struct alloc_phys_ctx
186{
187 grub_addr_t size;
188 grub_addr_t ret;
189};
190
d0d4b8a0
CW
191/* Helper for alloc_phys. */
192static int
193alloc_phys_choose (grub_uint64_t addr, grub_uint64_t len,
194 grub_memory_type_t type, void *data)
d8e1836c 195{
ed12a003 196 struct alloc_phys_ctx *ctx = data;
d0d4b8a0 197 grub_addr_t end = addr + len;
d8e1836c 198
d0d4b8a0
CW
199 if (type != 1)
200 return 0;
d8e1836c 201
d0d4b8a0 202 addr = ALIGN_UP (addr, FOUR_MB);
ed12a003 203 if (addr + ctx->size >= end)
d0d4b8a0 204 return 0;
d8e1836c 205
d0d4b8a0
CW
206 if (addr >= grub_phys_start && addr < grub_phys_end)
207 {
208 addr = ALIGN_UP (grub_phys_end, FOUR_MB);
ed12a003 209 if (addr + ctx->size >= end)
d0d4b8a0
CW
210 return 0;
211 }
ed12a003
CW
212 if ((addr + ctx->size) >= grub_phys_start
213 && (addr + ctx->size) < grub_phys_end)
d0d4b8a0
CW
214 {
215 addr = ALIGN_UP (grub_phys_end, FOUR_MB);
ed12a003 216 if (addr + ctx->size >= end)
d0d4b8a0
CW
217 return 0;
218 }
d8e1836c 219
d0d4b8a0
CW
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;
ed12a003 227 if (addr + ctx->size >= end)
d0d4b8a0
CW
228 return 0;
229 }
ed12a003
CW
230 if ((addr + ctx->size) >= linux_paddr
231 && (addr + ctx->size) < linux_end)
d0d4b8a0
CW
232 {
233 addr = linux_end;
ed12a003 234 if (addr + ctx->size >= end)
d0d4b8a0
CW
235 return 0;
236 }
237 }
238
ed12a003 239 ctx->ret = addr;
d0d4b8a0
CW
240 return 1;
241}
242
243static grub_addr_t
244alloc_phys (grub_addr_t size)
245{
ed12a003
CW
246 struct alloc_phys_ctx ctx = {
247 .size = size,
248 .ret = (grub_addr_t) -1
249 };
d0d4b8a0 250
ed12a003 251 grub_machine_mmap_iterate (alloc_phys_choose, &ctx);
d8e1836c 252
ed12a003 253 return ctx.ret;
d8e1836c 254}
255
256static grub_err_t
9c4b5c13 257grub_linux_load64 (grub_elf_t elf, const char *filename)
d8e1836c 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;
73bf57e2 265 linux_size = grub_elf64_size (elf, 0, 0);
d8e1836c 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,
61ba42be 275 "couldn't allocate physical memory");
904935c3
VS
276 ret = grub_ieee1275_map (paddr, linux_addr - off,
277 linux_size + off, IEEE1275_MAP_DEFAULT);
d8e1836c 278 if (ret)
279 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
61ba42be 280 "couldn't map physical memory");
d8e1836c 281
809bbfeb 282 grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
d8e1836c 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. */
73bf57e2 290 return grub_elf64_load (elf, filename, (void *) (linux_addr - off - base), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
d8e1836c 291}
292
0552ff9f 293static grub_err_t
294grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
295 int argc, char *argv[])
d8e1836c 296{
297 grub_file_t file = 0;
298 grub_elf_t elf = 0;
d8e1836c 299 int size;
d8e1836c 300
301 grub_dl_ref (my_mod);
302
303 if (argc == 0)
304 {
9c4b5c13 305 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
d8e1836c 306 goto out;
307 }
308
fc2ef117 309 file = grub_file_open (argv[0]);
d8e1836c 310 if (!file)
311 goto out;
312
9c4b5c13 313 elf = grub_elf_file (file, argv[0]);
d8e1836c 314 if (! elf)
315 goto out;
316
317 if (elf->ehdr.ehdr32.e_type != ET_EXEC)
318 {
319 grub_error (GRUB_ERR_UNKNOWN_OS,
9c4b5c13 320 N_("this ELF file is not of the right type"));
d8e1836c 321 goto out;
322 }
323
324 /* Release the previously used memory. */
325 grub_loader_unset ();
326
327 if (grub_elf_is_elf64 (elf))
9c4b5c13 328 grub_linux_load64 (elf, argv[0]);
d8e1836c 329 else
330 {
67093bc0 331 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
d8e1836c 332 goto out;
333 }
334
25953e10 335 size = grub_loader_cmdline_size(argc, argv);
d8e1836c 336
25953e10 337 linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
d8e1836c 338 if (! linux_args)
339 goto out;
340
25953e10
SJ
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);
d8e1836c 345
346out:
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 }
0552ff9f 364
365 return grub_errno;
d8e1836c 366}
367
0552ff9f 368static grub_err_t
369grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
370 int argc, char *argv[])
d8e1836c 371{
3c76ea0c 372 grub_size_t size = 0;
d8e1836c 373 grub_addr_t paddr;
374 grub_addr_t addr;
375 int ret;
9a67e1ac 376 struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
d8e1836c 377
378 if (argc == 0)
379 {
9c4b5c13 380 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
d8e1836c 381 goto fail;
382 }
383
384 if (!loaded)
385 {
9c4b5c13 386 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
d8e1836c 387 goto fail;
388 }
389
92750e4c 390 if (grub_initrd_init (argc, argv, &initrd_ctx))
d8e1836c 391 goto fail;
392
92750e4c 393 size = grub_get_initrd_size (&initrd_ctx);
3c76ea0c 394
d8e1836c 395 addr = 0x60000000;
d8e1836c 396
397 paddr = alloc_phys (size);
398 if (paddr == (grub_addr_t) -1)
399 {
400 grub_error (GRUB_ERR_OUT_OF_MEMORY,
61ba42be 401 "couldn't allocate physical memory");
d8e1836c 402 goto fail;
403 }
904935c3 404 ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT);
d8e1836c 405 if (ret)
406 {
407 grub_error (GRUB_ERR_OUT_OF_MEMORY,
61ba42be 408 "couldn't map physical memory");
d8e1836c 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
92750e4c
VS
415 if (grub_initrd_load (&initrd_ctx, argv, (void *) addr))
416 goto fail;
d8e1836c 417
418 initrd_addr = addr;
419 initrd_paddr = paddr;
420 initrd_size = size;
421
422 fail:
92750e4c 423 grub_initrd_close (&initrd_ctx);
0552ff9f 424
425 return grub_errno;
d8e1836c 426}
427
d0d4b8a0
CW
428/* Helper for determine_phys_base. */
429static int
430get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__ ((unused)),
e7b66a28 431 grub_memory_type_t type, void *data __attribute__ ((unused)))
d8e1836c 432{
d0d4b8a0 433 if (type != 1)
d8e1836c 434 return 0;
d0d4b8a0
CW
435 if (addr < phys_base)
436 phys_base = addr;
437 return 0;
438}
d8e1836c 439
d0d4b8a0
CW
440static void
441determine_phys_base (void)
442{
d8e1836c 443 phys_base = ~(grub_uint64_t) 0;
d0d4b8a0 444 grub_machine_mmap_iterate (get_physbase, NULL);
d8e1836c 445}
446
447static void
448fetch_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 {
4241d2b1 475 grub_printf ("Cannot fetch /virtual-memory/translations property.\n");
d8e1836c 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
0552ff9f 502static grub_command_t cmd_linux, cmd_initrd;
503
d8e1836c 504GRUB_MOD_INIT(linux)
505{
506 determine_phys_base ();
507 fetch_translations ();
508
0552ff9f 509 cmd_linux = grub_register_command ("linux", grub_cmd_linux,
809bbfeb 510 0, N_("Load Linux."));
0552ff9f 511 cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
809bbfeb 512 0, N_("Load initrd."));
d8e1836c 513 my_mod = mod;
514}
515
516GRUB_MOD_FINI(linux)
517{
0552ff9f 518 grub_unregister_command (cmd_linux);
519 grub_unregister_command (cmd_initrd);
d8e1836c 520}