Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / loader / mips / linux.c
blob5f383be3d076985199474410f7a09fbb5c3e581b
1 /* linux.c - boot Linux */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2007,2009,2010 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/command.h>
27 #include <grub/mips/relocator.h>
28 #include <grub/memory.h>
29 #include <grub/i18n.h>
30 #include <grub/lib/cmdline.h>
31 #include <grub/linux.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 #pragma GCC diagnostic ignored "-Wcast-align"
37 /* For frequencies. */
38 #include <grub/machine/time.h>
40 #ifdef GRUB_MACHINE_MIPS_LOONGSON
41 #include <grub/pci.h>
42 #include <grub/machine/kernel.h>
44 const char loongson_machtypes[][60] =
46 [GRUB_ARCH_MACHINE_YEELOONG] = "machtype=lemote-yeeloong-2f-8.9inches",
47 [GRUB_ARCH_MACHINE_FULOONG2F] = "machtype=lemote-fuloong-2f-box",
48 [GRUB_ARCH_MACHINE_FULOONG2E] = "machtype=lemote-fuloong-2e-unknown"
50 #endif
52 static grub_dl_t my_mod;
54 static int loaded;
56 static grub_size_t linux_size;
58 static struct grub_relocator *relocator;
59 static grub_uint8_t *playground;
60 static grub_addr_t target_addr, entry_addr;
61 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
62 static char *params;
63 #else
64 static int linux_argc;
65 static grub_off_t argv_off;
66 #ifdef GRUB_MACHINE_MIPS_LOONGSON
67 static grub_off_t envp_off;
68 #endif
69 static grub_off_t rd_addr_arg_off, rd_size_arg_off;
70 #endif
71 static int initrd_loaded = 0;
73 static grub_err_t
74 grub_linux_boot (void)
76 struct grub_relocator32_state state;
78 grub_memset (&state, 0, sizeof (state));
80 /* Boot the kernel. */
81 state.gpr[1] = entry_addr;
83 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
85 grub_err_t err;
86 grub_relocator_chunk_t ch;
87 grub_uint32_t *memsize;
88 grub_uint32_t *magic;
89 char *str;
91 err = grub_relocator_alloc_chunk_addr (relocator, &ch,
92 ((16 << 20) - 264),
93 grub_strlen (params) + 1 + 8);
94 if (err)
95 return err;
96 memsize = get_virtual_current_address (ch);
97 magic = memsize + 1;
98 *memsize = grub_mmap_get_lower ();
99 *magic = 0x12345678;
100 str = (char *) (magic + 1);
101 grub_strcpy (str, params);
103 #endif
105 #ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
106 state.gpr[4] = linux_argc;
107 state.gpr[5] = target_addr + argv_off;
108 #ifdef GRUB_MACHINE_MIPS_LOONGSON
109 state.gpr[6] = target_addr + envp_off;
110 #else
111 state.gpr[6] = 0;
112 #endif
113 state.gpr[7] = 0;
114 #endif
115 state.jumpreg = 1;
116 grub_relocator32_boot (relocator, state);
118 return GRUB_ERR_NONE;
121 static grub_err_t
122 grub_linux_unload (void)
124 grub_relocator_unload (relocator);
125 grub_dl_unref (my_mod);
127 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
128 grub_free (params);
129 params = 0;
130 #endif
132 loaded = 0;
134 return GRUB_ERR_NONE;
137 static grub_err_t
138 grub_linux_load32 (grub_elf_t elf, const char *filename,
139 void **extra_mem, grub_size_t extra_size)
141 Elf32_Addr base;
142 int extraoff;
143 grub_err_t err;
145 /* Linux's entry point incorrectly contains a virtual address. */
146 entry_addr = elf->ehdr.ehdr32.e_entry;
148 linux_size = grub_elf32_size (elf, &base, 0);
149 if (linux_size == 0)
150 return grub_errno;
151 target_addr = base;
152 /* Pad it; the kernel scribbles over memory beyond its load address. */
153 linux_size += 0x100000;
154 linux_size = ALIGN_UP (base + linux_size, 4) - base;
155 extraoff = linux_size;
156 linux_size += extra_size;
158 relocator = grub_relocator_new ();
159 if (!relocator)
160 return grub_errno;
163 grub_relocator_chunk_t ch;
164 err = grub_relocator_alloc_chunk_addr (relocator, &ch,
165 target_addr & 0x1fffffff,
166 linux_size);
167 if (err)
168 return err;
169 playground = get_virtual_current_address (ch);
172 *extra_mem = playground + extraoff;
174 /* Now load the segments into the area we claimed. */
175 return grub_elf32_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
178 static grub_err_t
179 grub_linux_load64 (grub_elf_t elf, const char *filename,
180 void **extra_mem, grub_size_t extra_size)
182 Elf64_Addr base;
183 int extraoff;
184 grub_err_t err;
186 /* Linux's entry point incorrectly contains a virtual address. */
187 entry_addr = elf->ehdr.ehdr64.e_entry;
189 linux_size = grub_elf64_size (elf, &base, 0);
190 if (linux_size == 0)
191 return grub_errno;
192 target_addr = base;
193 /* Pad it; the kernel scribbles over memory beyond its load address. */
194 linux_size += 0x100000;
195 linux_size = ALIGN_UP (base + linux_size, 4) - base;
196 extraoff = linux_size;
197 linux_size += extra_size;
199 relocator = grub_relocator_new ();
200 if (!relocator)
201 return grub_errno;
204 grub_relocator_chunk_t ch;
205 err = grub_relocator_alloc_chunk_addr (relocator, &ch,
206 target_addr & 0x1fffffff,
207 linux_size);
208 if (err)
209 return err;
210 playground = get_virtual_current_address (ch);
213 *extra_mem = playground + extraoff;
215 /* Now load the segments into the area we claimed. */
216 return grub_elf64_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
219 static grub_err_t
220 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
221 int argc, char *argv[])
223 grub_elf_t elf = 0;
224 int size;
225 void *extra = NULL;
226 #ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
227 int i;
228 grub_uint32_t *linux_argv;
229 char *linux_args;
230 #endif
231 grub_err_t err;
232 #ifdef GRUB_MACHINE_MIPS_LOONGSON
233 char *linux_envs;
234 grub_uint32_t *linux_envp;
235 #endif
237 if (argc == 0)
238 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
240 elf = grub_elf_open (argv[0]);
241 if (! elf)
242 return grub_errno;
244 if (elf->ehdr.ehdr32.e_type != ET_EXEC)
246 grub_elf_close (elf);
247 return grub_error (GRUB_ERR_UNKNOWN_OS,
248 N_("this ELF file is not of the right type"));
251 /* Release the previously used memory. */
252 grub_loader_unset ();
253 loaded = 0;
255 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
256 size = 0;
257 #else
258 /* For arguments. */
259 linux_argc = argc;
260 #ifdef GRUB_MACHINE_MIPS_LOONGSON
261 linux_argc++;
262 #endif
263 /* Main arguments. */
264 size = (linux_argc) * sizeof (grub_uint32_t);
265 /* Initrd address and size. */
266 size += 2 * sizeof (grub_uint32_t);
267 /* NULL terminator. */
268 size += sizeof (grub_uint32_t);
270 /* First argument is always "a0". */
271 size += ALIGN_UP (sizeof ("a0"), 4);
272 /* Normal arguments. */
273 for (i = 1; i < argc; i++)
274 size += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
275 #ifdef GRUB_MACHINE_MIPS_LOONGSON
276 size += ALIGN_UP (sizeof (loongson_machtypes[0]), 4);
277 #endif
279 /* rd arguments. */
280 size += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
281 size += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
283 /* For the environment. */
284 size += sizeof (grub_uint32_t);
285 size += 4 * sizeof (grub_uint32_t);
286 size += ALIGN_UP (sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"), 4)
287 + ALIGN_UP (sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"), 4)
288 + ALIGN_UP (sizeof ("busclock=XXXXXXXXXX"), 4)
289 + ALIGN_UP (sizeof ("cpuclock=XXXXXXXXXX"), 4);
290 #endif
292 if (grub_elf_is_elf32 (elf))
293 err = grub_linux_load32 (elf, argv[0], &extra, size);
294 else
295 if (grub_elf_is_elf64 (elf))
296 err = grub_linux_load64 (elf, argv[0], &extra, size);
297 else
298 err = grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
300 grub_elf_close (elf);
302 if (err)
303 return err;
305 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
306 /* Create kernel command line. */
307 size = grub_loader_cmdline_size(argc, argv);
308 params = grub_malloc (size + sizeof (LINUX_IMAGE));
309 if (! params)
311 grub_linux_unload ();
312 return grub_errno;
315 grub_memcpy (params, LINUX_IMAGE, sizeof (LINUX_IMAGE));
316 grub_create_loader_cmdline (argc, argv, params + sizeof (LINUX_IMAGE) - 1,
317 size);
318 #else
319 linux_argv = extra;
320 argv_off = (grub_uint8_t *) linux_argv - (grub_uint8_t *) playground;
321 extra = linux_argv + (linux_argc + 1 + 2);
322 linux_args = extra;
324 grub_memcpy (linux_args, "a0", sizeof ("a0"));
325 *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
326 + target_addr;
327 linux_argv++;
328 linux_args += ALIGN_UP (sizeof ("a0"), 4);
330 #ifdef GRUB_MACHINE_MIPS_LOONGSON
332 unsigned mtype = grub_arch_machine;
333 if (mtype >= ARRAY_SIZE (loongson_machtypes))
334 mtype = 0;
335 /* In Loongson platform, it is the responsibility of the bootloader/firmware
336 to supply the OS kernel with machine type information. */
337 grub_memcpy (linux_args, loongson_machtypes[mtype],
338 sizeof (loongson_machtypes[mtype]));
339 *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
340 + target_addr;
341 linux_argv++;
342 linux_args += ALIGN_UP (sizeof (loongson_machtypes[mtype]), 4);
344 #endif
346 for (i = 1; i < argc; i++)
348 grub_memcpy (linux_args, argv[i], grub_strlen (argv[i]) + 1);
349 *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
350 + target_addr;
351 linux_argv++;
352 linux_args += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
355 /* Reserve space for rd arguments. */
356 rd_addr_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
357 linux_args += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
358 *linux_argv = 0;
359 linux_argv++;
361 rd_size_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
362 linux_args += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
363 *linux_argv = 0;
364 linux_argv++;
366 *linux_argv = 0;
368 extra = linux_args;
370 #ifdef GRUB_MACHINE_MIPS_LOONGSON
371 linux_envp = extra;
372 envp_off = (grub_uint8_t *) linux_envp - (grub_uint8_t *) playground;
373 linux_envs = (char *) (linux_envp + 5);
374 grub_snprintf (linux_envs, sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"),
375 "memsize=%lld",
376 (unsigned long long) grub_mmap_get_lower () >> 20);
377 linux_envp[0] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
378 + target_addr;
379 linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
380 grub_snprintf (linux_envs, sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"),
381 "highmemsize=%lld",
382 (unsigned long long) grub_mmap_get_upper () >> 20);
383 linux_envp[1] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
384 + target_addr;
385 linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
387 grub_snprintf (linux_envs, sizeof ("busclock=XXXXXXXXXX"),
388 "busclock=%d", grub_arch_busclock);
389 linux_envp[2] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
390 + target_addr;
391 linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
392 grub_snprintf (linux_envs, sizeof ("cpuclock=XXXXXXXXXX"),
393 "cpuclock=%d", grub_arch_cpuclock);
394 linux_envp[3] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
395 + target_addr;
396 linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
398 linux_envp[4] = 0;
399 #endif
400 #endif
402 grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
403 initrd_loaded = 0;
404 loaded = 1;
405 grub_dl_ref (my_mod);
407 return GRUB_ERR_NONE;
410 static grub_err_t
411 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
412 int argc, char *argv[])
414 grub_size_t size = 0;
415 void *initrd_src;
416 grub_addr_t initrd_dest;
417 grub_err_t err;
418 struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
420 if (argc == 0)
421 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
423 if (!loaded)
424 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
426 if (initrd_loaded)
427 return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd command can be issued.");
429 if (grub_initrd_init (argc, argv, &initrd_ctx))
430 goto fail;
432 size = grub_get_initrd_size (&initrd_ctx);
435 grub_relocator_chunk_t ch;
437 err = grub_relocator_alloc_chunk_align (relocator, &ch,
438 (target_addr & 0x1fffffff)
439 + linux_size + 0x10000,
440 (0x10000000 - size),
441 size, 0x10000,
442 GRUB_RELOCATOR_PREFERENCE_NONE, 0);
444 if (err)
445 goto fail;
446 initrd_src = get_virtual_current_address (ch);
447 initrd_dest = get_physical_target_address (ch) | 0x80000000;
450 if (grub_initrd_load (&initrd_ctx, argv, initrd_src))
451 goto fail;
453 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
455 char *tmp;
456 tmp = grub_xasprintf ("%s rd_start=0x%" PRIxGRUB_ADDR
457 " rd_size=0x%" PRIxGRUB_ADDR, params,
458 initrd_dest, size);
459 if (!tmp)
460 goto fail;
461 grub_free (params);
462 params = tmp;
464 #else
465 grub_snprintf ((char *) playground + rd_addr_arg_off,
466 sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), "rd_start=0x%llx",
467 (unsigned long long) initrd_dest);
468 ((grub_uint32_t *) (playground + argv_off))[linux_argc]
469 = target_addr + rd_addr_arg_off;
470 linux_argc++;
472 grub_snprintf ((char *) playground + rd_size_arg_off,
473 sizeof ("rd_size=0xXXXXXXXXXXXXXXXXX"), "rd_size=0x%llx",
474 (unsigned long long) size);
475 ((grub_uint32_t *) (playground + argv_off))[linux_argc]
476 = target_addr + rd_size_arg_off;
477 linux_argc++;
478 #endif
480 initrd_loaded = 1;
482 fail:
483 grub_initrd_close (&initrd_ctx);
485 return grub_errno;
488 static grub_command_t cmd_linux, cmd_initrd;
490 GRUB_MOD_INIT(linux)
492 cmd_linux = grub_register_command ("linux", grub_cmd_linux,
493 0, N_("Load Linux."));
494 cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
495 0, N_("Load initrd."));
496 my_mod = mod;
499 GRUB_MOD_FINI(linux)
501 grub_unregister_command (cmd_linux);
502 grub_unregister_command (cmd_initrd);