1 /* linux.c - boot Linux */
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/>.
22 #include <grub/file.h>
23 #include <grub/loader.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)
57 get_atag_size (grub_uint32_t
*atag
)
59 grub_uint32_t
*atag0
= atag
;
60 while (atag
[0] && atag
[1])
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.
71 linux_prepare_atag (void)
73 grub_uint32_t
*atag_orig
= (grub_uint32_t
*) fdt_addr
;
74 grub_uint32_t
*tmp_atag
, *from
, *to
;
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
));
86 for (from
= atag_orig
, to
= tmp_atag
; from
[0] && from
[1];
95 if (*(char *) (from
+ 2))
97 cmdline_orig
= (char *) (from
+ 2);
98 cmdline_orig_len
= grub_strlen (cmdline_orig
) + 1;
102 grub_memcpy (to
, from
, sizeof (grub_uint32_t
) * from
[0]);
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;
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));
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
);
135 to
[2] = initrd_start
;
136 to
[3] = initrd_end
- initrd_start
;
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.
159 linux_prepare_fdt (void)
166 tmp_size
= grub_fdt_get_totalsize (fdt_addr
) + 0x100 + grub_strlen (linux_args
);
167 tmp_fdt
= grub_malloc (tmp_size
);
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");
178 grub_dprintf ("linux", "No 'chosen' node in FDT - creating.\n");
179 node
= grub_fdt_add_subnode (tmp_fdt
, 0, "chosen");
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);
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",
205 retval
= grub_fdt_set_prop32 (tmp_fdt
, node
, "linux,initrd-end",
211 /* Copy updated FDT to its launch location */
212 grub_memcpy (fdt_addr
, tmp_fdt
, tmp_size
);
215 grub_dprintf ("loader", "FDT updated for Linux boot\n");
217 return GRUB_ERR_NONE
;
221 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "unable to prepare FDT");
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",
235 ((grub_uint16_t
*) fdt_addr
)[3],
236 *((grub_uint32_t
*) 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
);
252 err
= linux_prepare_fdt ();
255 grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr
);
261 err
= linux_prepare_atag ();
264 grub_dprintf ("loader", "ATAG @ 0x%p\n", fdt_addr
);
267 grub_dprintf ("loader", "Jumping to Linux...\n");
270 * Arguments to kernel:
273 * r2 - address of DTB
275 linuxmain
= (kernel_entry_t
) linux_addr
;
277 #ifdef GRUB_MACHINE_EFI
280 err
= grub_efi_prepare_platform();
281 if (err
!= GRUB_ERR_NONE
)
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
297 linux_load (const char *filename
, grub_file_t file
)
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
);
308 linux_addr
= LINUX_ADDRESS
;
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
)
316 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"),
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),
330 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("invalid zImage"));
334 return GRUB_ERR_NONE
;
340 grub_dl_unref (my_mod
);
342 grub_free (linux_args
);
345 initrd_start
= initrd_end
= 0;
347 return GRUB_ERR_NONE
;
351 grub_cmd_linux (grub_command_t cmd
__attribute__ ((unused
)),
352 int argc
, char *argv
[])
357 grub_dl_ref (my_mod
);
360 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
362 file
= grub_file_open (argv
[0]);
366 err
= linux_load (argv
[0], file
);
367 grub_file_close (file
);
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
));
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
;
389 grub_dl_unref (my_mod
);
394 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
395 int argc
, char *argv
[])
398 grub_size_t size
= 0;
399 struct grub_linux_initrd_context initrd_ctx
= { 0, 0, 0 };
402 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
404 file
= grub_file_open (argv
[0]);
408 if (grub_initrd_init (argc
, argv
, &initrd_ctx
))
411 size
= grub_get_initrd_size (&initrd_ctx
);
413 #ifdef GRUB_MACHINE_EFI
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
);
421 grub_error (GRUB_ERR_OUT_OF_MEMORY
, N_("out of memory"));
425 initrd_start
= LINUX_INITRD_ADDRESS
;
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
))
434 initrd_end
= initrd_start
+ size
;
436 return GRUB_ERR_NONE
;
439 grub_file_close (file
);
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
;
456 grub_cmd_devicetree (grub_command_t cmd
__attribute__ ((unused
)),
457 int argc
, char *argv
[])
463 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
465 dtb
= grub_file_open (argv
[0]);
469 size
= grub_file_size (dtb
);
472 grub_error (GRUB_ERR_BAD_OS
, "empty file");
476 #ifdef GRUB_MACHINE_EFI
477 fdt_addr
= grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET
, size
);
480 grub_error (GRUB_ERR_OUT_OF_MEMORY
, N_("out of memory"));
484 fdt_addr
= (void *) LINUX_FDT_ADDRESS
;
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
)
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
;
503 grub_file_close (dtb
);
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."));
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
);