Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / loader / sparc64 / ieee1275 / linux.c
blob06d1df6ae67d91c91fd0dde530ea47f6aeed3172
1 /* linux.c - boot Linux */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003, 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
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.
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.
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/>.
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>
32 GRUB_MOD_LICENSE ("GPLv3+");
34 static grub_dl_t my_mod;
36 static int loaded;
38 /* /virtual-memory/translations property layout */
39 struct grub_ieee1275_translation {
40 grub_uint64_t vaddr;
41 grub_uint64_t size;
42 grub_uint64_t data;
45 static struct grub_ieee1275_translation *of_trans;
46 static int of_num_trans;
48 static grub_addr_t phys_base;
49 static grub_addr_t grub_phys_start;
50 static grub_addr_t grub_phys_end;
52 static grub_addr_t initrd_addr;
53 static grub_addr_t initrd_paddr;
54 static grub_size_t initrd_size;
56 static Elf64_Addr linux_entry;
57 static grub_addr_t linux_addr;
58 static grub_addr_t linux_paddr;
59 static grub_size_t linux_size;
61 static char *linux_args;
63 struct linux_bootstr_info {
64 int len, valid;
65 char buf[];
68 struct linux_hdrs {
69 /* All HdrS versions support these fields. */
70 unsigned int start_insns[2];
71 char magic[4]; /* "HdrS" */
72 unsigned int linux_kernel_version; /* LINUX_VERSION_CODE */
73 unsigned short hdrs_version;
74 unsigned short root_flags;
75 unsigned short root_dev;
76 unsigned short ram_flags;
77 unsigned int __deprecated_ramdisk_image;
78 unsigned int ramdisk_size;
80 /* HdrS versions 0x0201 and higher only */
81 char *reboot_command;
83 /* HdrS versions 0x0202 and higher only */
84 struct linux_bootstr_info *bootstr_info;
86 /* HdrS versions 0x0301 and higher only */
87 unsigned long ramdisk_image;
90 static grub_err_t
91 grub_linux_boot (void)
93 struct linux_bootstr_info *bp;
94 struct linux_hdrs *hp;
95 grub_addr_t addr;
97 hp = (struct linux_hdrs *) linux_addr;
99 /* Any pointer we dereference in the kernel image must be relocated
100 to where we actually loaded the kernel. */
101 addr = (grub_addr_t) hp->bootstr_info;
102 addr += (linux_addr - linux_entry);
103 bp = (struct linux_bootstr_info *) addr;
105 /* Set the command line arguments, unless the kernel has been
106 built with a fixed CONFIG_CMDLINE. */
107 if (!bp->valid)
109 int len = grub_strlen (linux_args) + 1;
110 if (bp->len < len)
111 len = bp->len;
112 memcpy(bp->buf, linux_args, len);
113 bp->buf[len-1] = '\0';
114 bp->valid = 1;
117 if (initrd_addr)
119 /* The kernel expects the physical address, adjusted relative
120 to the lowest address advertised in "/memory"'s available
121 property.
123 The history of this is that back when the kernel only supported
124 specifying a 32-bit ramdisk address, this was the way to still
125 be able to specify the ramdisk physical address even if memory
126 started at some place above 4GB.
128 The magic 0x400000 is KERNBASE, I have no idea why SILO adds
129 that term into the address, but it does and thus we have to do
130 it too as this is what the kernel expects. */
131 hp->ramdisk_image = initrd_paddr - phys_base + 0x400000;
132 hp->ramdisk_size = initrd_size;
135 grub_dprintf ("loader", "Entry point: 0x%lx\n", linux_addr);
136 grub_dprintf ("loader", "Initrd at: 0x%lx, size 0x%lx\n", initrd_addr,
137 initrd_size);
138 grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
139 grub_dprintf ("loader", "Jumping to Linux...\n");
141 /* Boot the kernel. */
142 asm volatile ("sethi %hi(grub_ieee1275_entry_fn), %o1\n"
143 "ldx [%o1 + %lo(grub_ieee1275_entry_fn)], %o4\n"
144 "sethi %hi(grub_ieee1275_original_stack), %o1\n"
145 "ldx [%o1 + %lo(grub_ieee1275_original_stack)], %o6\n"
146 "sethi %hi(linux_addr), %o1\n"
147 "ldx [%o1 + %lo(linux_addr)], %o5\n"
148 "mov %g0, %o0\n"
149 "mov %g0, %o2\n"
150 "mov %g0, %o3\n"
151 "jmp %o5\n"
152 "mov %g0, %o1\n");
154 return GRUB_ERR_NONE;
157 static grub_err_t
158 grub_linux_release_mem (void)
160 grub_free (linux_args);
161 linux_args = 0;
162 linux_addr = 0;
163 initrd_addr = 0;
165 return GRUB_ERR_NONE;
168 static grub_err_t
169 grub_linux_unload (void)
171 grub_err_t err;
173 err = grub_linux_release_mem ();
174 grub_dl_unref (my_mod);
176 loaded = 0;
178 return err;
181 #define FOUR_MB (4 * 1024 * 1024)
183 static grub_addr_t
184 alloc_phys (grub_addr_t size)
186 grub_addr_t ret = (grub_addr_t) -1;
188 auto int NESTED_FUNC_ATTR choose (grub_uint64_t addr, grub_uint64_t len,
189 grub_memory_type_t type);
190 int NESTED_FUNC_ATTR choose (grub_uint64_t addr, grub_uint64_t len,
191 grub_memory_type_t type)
193 grub_addr_t end = addr + len;
195 if (type != 1)
196 return 0;
198 addr = ALIGN_UP (addr, FOUR_MB);
199 if (addr + size >= end)
200 return 0;
202 if (addr >= grub_phys_start && addr < grub_phys_end)
204 addr = ALIGN_UP (grub_phys_end, FOUR_MB);
205 if (addr + size >= end)
206 return 0;
208 if ((addr + size) >= grub_phys_start
209 && (addr + size) < grub_phys_end)
211 addr = ALIGN_UP (grub_phys_end, FOUR_MB);
212 if (addr + size >= end)
213 return 0;
216 if (loaded)
218 grub_addr_t linux_end = ALIGN_UP (linux_paddr + linux_size, FOUR_MB);
220 if (addr >= linux_paddr && addr < linux_end)
222 addr = linux_end;
223 if (addr + size >= end)
224 return 0;
226 if ((addr + size) >= linux_paddr
227 && (addr + size) < linux_end)
229 addr = linux_end;
230 if (addr + size >= end)
231 return 0;
235 ret = addr;
236 return 1;
239 grub_machine_mmap_iterate (choose);
241 return ret;
244 static grub_err_t
245 grub_linux_load64 (grub_elf_t elf, const char *filename)
247 grub_addr_t off, paddr, base;
248 int ret;
250 linux_entry = elf->ehdr.ehdr64.e_entry;
251 linux_addr = 0x40004000;
252 off = 0x4000;
253 linux_size = grub_elf64_size (elf, filename, 0, 0);
254 if (linux_size == 0)
255 return grub_errno;
257 grub_dprintf ("loader", "Attempting to claim at 0x%lx, size 0x%lx.\n",
258 linux_addr, linux_size);
260 paddr = alloc_phys (linux_size + off);
261 if (paddr == (grub_addr_t) -1)
262 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
263 "couldn't allocate physical memory");
264 ret = grub_ieee1275_map (paddr, linux_addr - off,
265 linux_size + off, IEEE1275_MAP_DEFAULT);
266 if (ret)
267 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
268 "couldn't map physical memory");
270 grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
271 linux_addr, paddr, linux_size);
273 linux_paddr = paddr;
275 base = linux_entry - off;
277 /* Now load the segments into the area we claimed. */
278 auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load);
279 grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load)
281 if (phdr->p_type != PT_LOAD)
283 *do_load = 0;
284 return 0;
286 *do_load = 1;
288 /* Adjust the program load address to linux_addr. */
289 *addr = (phdr->p_paddr - base) + (linux_addr - off);
290 return 0;
292 return grub_elf64_load (elf, filename, offset_phdr, 0, 0);
295 static grub_err_t
296 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
297 int argc, char *argv[])
299 grub_file_t file = 0;
300 grub_elf_t elf = 0;
301 int size;
303 grub_dl_ref (my_mod);
305 if (argc == 0)
307 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
308 goto out;
311 file = grub_file_open (argv[0]);
312 if (!file)
313 goto out;
315 elf = grub_elf_file (file, argv[0]);
316 if (! elf)
317 goto out;
319 if (elf->ehdr.ehdr32.e_type != ET_EXEC)
321 grub_error (GRUB_ERR_UNKNOWN_OS,
322 N_("this ELF file is not of the right type"));
323 goto out;
326 /* Release the previously used memory. */
327 grub_loader_unset ();
329 if (grub_elf_is_elf64 (elf))
330 grub_linux_load64 (elf, argv[0]);
331 else
333 grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
334 goto out;
337 size = grub_loader_cmdline_size(argc, argv);
339 linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
340 if (! linux_args)
341 goto out;
343 /* Create kernel command line. */
344 grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
345 grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
346 size);
348 out:
349 if (elf)
350 grub_elf_close (elf);
351 else if (file)
352 grub_file_close (file);
354 if (grub_errno != GRUB_ERR_NONE)
356 grub_linux_release_mem ();
357 grub_dl_unref (my_mod);
358 loaded = 0;
360 else
362 grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
363 initrd_addr = 0;
364 loaded = 1;
367 return grub_errno;
370 static grub_err_t
371 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
372 int argc, char *argv[])
374 grub_file_t *files = 0;
375 grub_size_t size = 0;
376 grub_addr_t paddr;
377 grub_addr_t addr;
378 int ret;
379 int i;
380 int nfiles = 0;
381 grub_uint8_t *ptr;
383 if (argc == 0)
385 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
386 goto fail;
389 if (!loaded)
391 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
392 goto fail;
395 files = grub_zalloc (argc * sizeof (files[0]));
396 if (!files)
397 goto fail;
399 for (i = 0; i < argc; i++)
401 grub_file_filter_disable_compression ();
402 files[i] = grub_file_open (argv[i]);
403 if (! files[i])
404 goto fail;
405 nfiles++;
406 size += ALIGN_UP(grub_file_size (files[i]), 4);
409 addr = 0x60000000;
411 paddr = alloc_phys (size);
412 if (paddr == (grub_addr_t) -1)
414 grub_error (GRUB_ERR_OUT_OF_MEMORY,
415 "couldn't allocate physical memory");
416 goto fail;
418 ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT);
419 if (ret)
421 grub_error (GRUB_ERR_OUT_OF_MEMORY,
422 "couldn't map physical memory");
423 goto fail;
426 grub_dprintf ("loader", "Loading initrd at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
427 addr, paddr, size);
429 ptr = (void *) addr;
430 for (i = 0; i < nfiles; i++)
432 grub_ssize_t cursize = grub_file_size (files[i]);
433 if (grub_file_read (files[i], ptr, cursize) != cursize)
435 if (!grub_errno)
436 grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
437 argv[i]);
438 goto fail;
440 ptr += cursize;
441 grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4));
442 ptr += ALIGN_UP_OVERHEAD (cursize, 4);
445 initrd_addr = addr;
446 initrd_paddr = paddr;
447 initrd_size = size;
449 fail:
450 for (i = 0; i < nfiles; i++)
451 grub_file_close (files[i]);
452 grub_free (files);
454 return grub_errno;
457 static void
458 determine_phys_base (void)
460 auto int NESTED_FUNC_ATTR get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__((unused)), grub_uint32_t type);
461 int NESTED_FUNC_ATTR get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__((unused)), grub_uint32_t type)
463 if (type != 1)
464 return 0;
465 if (addr < phys_base)
466 phys_base = addr;
467 return 0;
470 phys_base = ~(grub_uint64_t) 0;
471 grub_machine_mmap_iterate (get_physbase);
474 static void
475 fetch_translations (void)
477 grub_ieee1275_phandle_t node;
478 grub_ssize_t actual;
479 int i;
481 if (grub_ieee1275_finddevice ("/virtual-memory", &node))
483 grub_printf ("Cannot find /virtual-memory node.\n");
484 return;
487 if (grub_ieee1275_get_property_length (node, "translations", &actual))
489 grub_printf ("Cannot find /virtual-memory/translations size.\n");
490 return;
493 of_trans = grub_malloc (actual);
494 if (!of_trans)
496 grub_printf ("Cannot allocate translations buffer.\n");
497 return;
500 if (grub_ieee1275_get_property (node, "translations", of_trans, actual, &actual))
502 grub_printf ("Cannot fetch /virtual-memory/translations property.\n");
503 return;
506 of_num_trans = actual / sizeof(struct grub_ieee1275_translation);
508 for (i = 0; i < of_num_trans; i++)
510 struct grub_ieee1275_translation *p = &of_trans[i];
512 if (p->vaddr == 0x2000)
514 grub_addr_t phys, tte = p->data;
516 phys = tte & ~(0xff00000000001fffULL);
518 grub_phys_start = phys;
519 grub_phys_end = grub_phys_start + p->size;
520 grub_dprintf ("loader", "Grub lives at phys_start[%lx] phys_end[%lx]\n",
521 (unsigned long) grub_phys_start,
522 (unsigned long) grub_phys_end);
523 break;
529 static grub_command_t cmd_linux, cmd_initrd;
531 GRUB_MOD_INIT(linux)
533 determine_phys_base ();
534 fetch_translations ();
536 cmd_linux = grub_register_command ("linux", grub_cmd_linux,
537 0, N_("Load Linux."));
538 cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
539 0, N_("Load initrd."));
540 my_mod = mod;
543 GRUB_MOD_FINI(linux)
545 grub_unregister_command (cmd_linux);
546 grub_unregister_command (cmd_initrd);