Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / loader / arm / linux.c
blob5b39f02bb2e592d85639891db6eb68a97ce12dfa
1 /* linux.c - boot Linux */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2013 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/dl.h>
21 #include <grub/fdt.h>
22 #include <grub/file.h>
23 #include <grub/loader.h>
24 #include <grub/mm.h>
25 #include <grub/misc.h>
26 #include <grub/command.h>
27 #include <grub/cache.h>
28 #include <grub/cpu/linux.h>
29 #include <grub/lib/cmdline.h>
30 #include <grub/linux.h>
32 GRUB_MOD_LICENSE ("GPLv3+");
34 static grub_dl_t my_mod;
36 static grub_addr_t initrd_start;
37 static grub_addr_t initrd_end;
39 static grub_addr_t linux_addr;
40 static grub_size_t linux_size;
42 static char *linux_args;
44 static grub_uint32_t machine_type;
45 static void *fdt_addr;
47 typedef void (*kernel_entry_t) (int, unsigned long, void *);
49 #define LINUX_ZIMAGE_OFFSET 0x24
50 #define LINUX_ZIMAGE_MAGIC 0x016f2818
52 #define LINUX_PHYS_OFFSET (0x00008000)
53 #define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000)
54 #define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000)
56 static grub_size_t
57 get_atag_size (grub_uint32_t *atag)
59 grub_uint32_t *atag0 = atag;
60 while (atag[0] && atag[1])
61 atag += atag[0];
62 return atag - atag0;
66 * linux_prepare_fdt():
67 * Prepares a loaded FDT for being passed to Linux.
68 * Merges in command line parameters and sets up initrd addresses.
70 static grub_err_t
71 linux_prepare_atag (void)
73 grub_uint32_t *atag_orig = (grub_uint32_t *) fdt_addr;
74 grub_uint32_t *tmp_atag, *from, *to;
75 grub_size_t tmp_size;
76 grub_size_t arg_size = grub_strlen (linux_args);
77 char *cmdline_orig = NULL;
78 grub_size_t cmdline_orig_len = 0;
80 /* some place for cmdline, initrd and terminator. */
81 tmp_size = get_atag_size (atag_orig) + 20 + (arg_size) / 4;
82 tmp_atag = grub_malloc (tmp_size * sizeof (grub_uint32_t));
83 if (!tmp_atag)
84 return grub_errno;
86 for (from = atag_orig, to = tmp_atag; from[0] && from[1];
87 from += from[0])
88 switch (from[1])
90 case 0x54410004:
91 case 0x54410005:
92 case 0x54420005:
93 break;
94 case 0x54410009:
95 if (*(char *) (from + 2))
97 cmdline_orig = (char *) (from + 2);
98 cmdline_orig_len = grub_strlen (cmdline_orig) + 1;
100 break;
101 default:
102 grub_memcpy (to, from, sizeof (grub_uint32_t) * from[0]);
103 to += from[0];
104 break;
107 grub_dprintf ("linux", "linux inherited args: '%s'\n",
108 cmdline_orig ? : "");
109 grub_dprintf ("linux", "linux_args: '%s'\n", linux_args);
111 /* Generate and set command line */
112 to[0] = 3 + (arg_size + cmdline_orig_len) / 4;
113 to[1] = 0x54410009;
114 if (cmdline_orig)
116 grub_memcpy ((char *) to + 8, cmdline_orig, cmdline_orig_len - 1);
117 *((char *) to + 8 + cmdline_orig_len - 1) = ' ';
119 grub_memcpy ((char *) to + 8 + cmdline_orig_len, linux_args, arg_size);
120 grub_memset ((char *) to + 8 + cmdline_orig_len + arg_size, 0,
121 4 - ((arg_size + cmdline_orig_len) & 3));
122 to += to[0];
124 if (initrd_start && initrd_end)
127 * We're using physical addresses, so even if we have LPAE, we're
128 * restricted to a 32-bit address space.
130 grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n",
131 initrd_start, initrd_end);
133 to[0] = 4;
134 to[1] = 0x54420005;
135 to[2] = initrd_start;
136 to[3] = initrd_end - initrd_start;
137 to += 4;
140 to[0] = 0;
141 to[1] = 0;
142 to += 2;
144 /* Copy updated FDT to its launch location */
145 grub_memcpy (atag_orig, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag));
146 grub_free (tmp_atag);
148 grub_dprintf ("loader", "ATAG updated for Linux boot\n");
150 return GRUB_ERR_NONE;
154 * linux_prepare_fdt():
155 * Prepares a loaded FDT for being passed to Linux.
156 * Merges in command line parameters and sets up initrd addresses.
158 static grub_err_t
159 linux_prepare_fdt (void)
161 int node;
162 int retval;
163 int tmp_size;
164 void *tmp_fdt;
166 tmp_size = grub_fdt_get_totalsize (fdt_addr) + 0x100 + grub_strlen (linux_args);
167 tmp_fdt = grub_malloc (tmp_size);
168 if (!tmp_fdt)
169 return grub_errno;
171 grub_memcpy (tmp_fdt, fdt_addr, grub_fdt_get_totalsize (fdt_addr));
172 grub_fdt_set_totalsize (tmp_fdt, tmp_size);
174 /* Find or create '/chosen' node */
175 node = grub_fdt_find_subnode (tmp_fdt, 0, "chosen");
176 if (node < 0)
178 grub_dprintf ("linux", "No 'chosen' node in FDT - creating.\n");
179 node = grub_fdt_add_subnode (tmp_fdt, 0, "chosen");
180 if (node < 0)
181 goto failure;
184 grub_dprintf ("linux", "linux_args: '%s'\n", linux_args);
186 /* Generate and set command line */
187 retval = grub_fdt_set_prop (tmp_fdt, node, "bootargs", linux_args,
188 grub_strlen (linux_args) + 1);
189 if (retval)
190 goto failure;
192 if (initrd_start && initrd_end)
195 * We're using physical addresses, so even if we have LPAE, we're
196 * restricted to a 32-bit address space.
198 grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n",
199 initrd_start, initrd_end);
201 retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-start",
202 initrd_start);
203 if (retval)
204 goto failure;
205 retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-end",
206 initrd_end);
207 if (retval)
208 goto failure;
211 /* Copy updated FDT to its launch location */
212 grub_memcpy (fdt_addr, tmp_fdt, tmp_size);
213 grub_free (tmp_fdt);
215 grub_dprintf ("loader", "FDT updated for Linux boot\n");
217 return GRUB_ERR_NONE;
219 failure:
220 grub_free (tmp_fdt);
221 return grub_error (GRUB_ERR_BAD_ARGUMENT, "unable to prepare FDT");
224 static grub_err_t
225 linux_boot (void)
227 kernel_entry_t linuxmain;
228 int fdt_valid, atag_valid;
230 fdt_valid = (fdt_addr && grub_fdt_check_header_nosize (fdt_addr) == 0);
231 atag_valid = ((((grub_uint16_t *) fdt_addr)[3] & ~3) == 0x5440
232 && *((grub_uint32_t *) fdt_addr));
233 grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n",
234 fdt_addr,
235 ((grub_uint16_t *) fdt_addr)[3],
236 *((grub_uint32_t *) fdt_addr),
237 (char *) fdt_addr,
238 (char *) fdt_addr + 1);
240 if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT)
241 return grub_error (GRUB_ERR_FILE_NOT_FOUND,
242 N_("device tree must be supplied (see `devicetree' command)"));
244 grub_arch_sync_caches ((void *) linux_addr, linux_size);
246 grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr);
248 if (fdt_valid)
250 grub_err_t err;
252 err = linux_prepare_fdt ();
253 if (err)
254 return err;
255 grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr);
257 else if (atag_valid)
259 grub_err_t err;
261 err = linux_prepare_atag ();
262 if (err)
263 return err;
264 grub_dprintf ("loader", "ATAG @ 0x%p\n", fdt_addr);
267 grub_dprintf ("loader", "Jumping to Linux...\n");
269 /* Boot the kernel.
270 * Arguments to kernel:
271 * r0 - 0
272 * r1 - machine type
273 * r2 - address of DTB
275 linuxmain = (kernel_entry_t) linux_addr;
277 #ifdef GRUB_MACHINE_EFI
279 grub_err_t err;
280 err = grub_efi_prepare_platform();
281 if (err != GRUB_ERR_NONE)
282 return err;
284 #endif
286 grub_arm_disable_caches_mmu ();
288 linuxmain (0, machine_type, fdt_addr);
290 return grub_error (GRUB_ERR_BAD_OS, "Linux call returned");
294 * Only support zImage, so no relocations necessary
296 static grub_err_t
297 linux_load (const char *filename, grub_file_t file)
299 int size;
301 size = grub_file_size (file);
303 #ifdef GRUB_MACHINE_EFI
304 linux_addr = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_PHYS_OFFSET, size);
305 if (!linux_addr)
306 return grub_errno;
307 #else
308 linux_addr = LINUX_ADDRESS;
309 #endif
310 grub_dprintf ("loader", "Loading Linux to 0x%08x\n",
311 (grub_addr_t) linux_addr);
313 if (grub_file_read (file, (void *) linux_addr, size) != size)
315 if (!grub_errno)
316 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
317 filename);
318 return grub_errno;
321 if (size > LINUX_ZIMAGE_OFFSET + 4
322 && *(grub_uint32_t *) (linux_addr + LINUX_ZIMAGE_OFFSET)
323 == LINUX_ZIMAGE_MAGIC)
325 else if (size > 0x8000 && *(grub_uint32_t *) (linux_addr) == 0xea000006
326 && machine_type == GRUB_ARM_MACHINE_TYPE_RASPBERRY_PI)
327 grub_memmove ((void *) linux_addr, (void *) (linux_addr + 0x8000),
328 size - 0x8000);
329 else
330 return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid zImage"));
332 linux_size = size;
334 return GRUB_ERR_NONE;
337 static grub_err_t
338 linux_unload (void)
340 grub_dl_unref (my_mod);
342 grub_free (linux_args);
343 linux_args = NULL;
345 initrd_start = initrd_end = 0;
347 return GRUB_ERR_NONE;
350 static grub_err_t
351 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
352 int argc, char *argv[])
354 int size;
355 grub_err_t err;
356 grub_file_t file;
357 grub_dl_ref (my_mod);
359 if (argc == 0)
360 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
362 file = grub_file_open (argv[0]);
363 if (!file)
364 goto fail;
366 err = linux_load (argv[0], file);
367 grub_file_close (file);
368 if (err)
369 goto fail;
371 grub_loader_set (linux_boot, linux_unload, 0);
373 size = grub_loader_cmdline_size (argc, argv);
374 linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
375 if (!linux_args)
377 grub_loader_unset();
378 goto fail;
381 /* Create kernel command line. */
382 grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
383 grub_create_loader_cmdline (argc, argv,
384 linux_args + sizeof (LINUX_IMAGE) - 1, size);
386 return GRUB_ERR_NONE;
388 fail:
389 grub_dl_unref (my_mod);
390 return grub_errno;
393 static grub_err_t
394 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
395 int argc, char *argv[])
397 grub_file_t file;
398 grub_size_t size = 0;
399 struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
401 if (argc == 0)
402 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
404 file = grub_file_open (argv[0]);
405 if (!file)
406 return grub_errno;
408 if (grub_initrd_init (argc, argv, &initrd_ctx))
409 goto fail;
411 size = grub_get_initrd_size (&initrd_ctx);
413 #ifdef GRUB_MACHINE_EFI
414 if (initrd_start)
415 grub_efi_free_pages (initrd_start,
416 (initrd_end - initrd_start + 0xfff) >> 12);
417 initrd_start = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_INITRD_PHYS_OFFSET, size);
419 if (!initrd_start)
421 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
422 goto fail;
424 #else
425 initrd_start = LINUX_INITRD_ADDRESS;
426 #endif
428 grub_dprintf ("loader", "Loading initrd to 0x%08x\n",
429 (grub_addr_t) initrd_start);
431 if (grub_initrd_load (&initrd_ctx, argv, (void *) initrd_start))
432 goto fail;
434 initrd_end = initrd_start + size;
436 return GRUB_ERR_NONE;
438 fail:
439 grub_file_close (file);
441 return grub_errno;
444 static grub_err_t
445 load_dtb (grub_file_t dtb, int size)
447 if ((grub_file_read (dtb, fdt_addr, size) != size)
448 || (grub_fdt_check_header (fdt_addr, size) != 0))
449 return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
451 grub_fdt_set_totalsize (fdt_addr, size);
452 return GRUB_ERR_NONE;
455 static grub_err_t
456 grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
457 int argc, char *argv[])
459 grub_file_t dtb;
460 int size;
462 if (argc != 1)
463 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
465 dtb = grub_file_open (argv[0]);
466 if (!dtb)
467 goto out;
469 size = grub_file_size (dtb);
470 if (size == 0)
472 grub_error (GRUB_ERR_BAD_OS, "empty file");
473 goto out;
476 #ifdef GRUB_MACHINE_EFI
477 fdt_addr = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
478 if (!fdt_addr)
480 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
481 goto out;
483 #else
484 fdt_addr = (void *) LINUX_FDT_ADDRESS;
485 #endif
487 grub_dprintf ("loader", "Loading device tree to 0x%08x\n",
488 (grub_addr_t) fdt_addr);
489 load_dtb (dtb, size);
490 if (grub_errno != GRUB_ERR_NONE)
492 fdt_addr = NULL;
493 goto out;
497 * We've successfully loaded an FDT, so any machine type passed
498 * from firmware is now obsolete.
500 machine_type = GRUB_ARM_MACHINE_TYPE_FDT;
502 out:
503 grub_file_close (dtb);
505 return grub_errno;
508 static grub_command_t cmd_linux, cmd_initrd, cmd_devicetree;
510 GRUB_MOD_INIT (linux)
512 cmd_linux = grub_register_command ("linux", grub_cmd_linux,
513 0, N_("Load Linux."));
514 cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
515 0, N_("Load initrd."));
516 cmd_devicetree = grub_register_command ("devicetree", grub_cmd_devicetree,
517 /* TRANSLATORS: DTB stands for device tree blob. */
518 0, N_("Load DTB file."));
519 my_mod = mod;
520 fdt_addr = (void *) grub_arm_firmware_get_boot_data ();
521 machine_type = grub_arm_firmware_get_machine_type ();
524 GRUB_MOD_FINI (linux)
526 grub_unregister_command (cmd_linux);
527 grub_unregister_command (cmd_initrd);
528 grub_unregister_command (cmd_devicetree);