1 /* linux.c - boot Linux */
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/>.
21 #include <grub/elfload.h>
22 #include <grub/loader.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
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"
52 static grub_dl_t my_mod
;
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
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
;
69 static grub_off_t rd_addr_arg_off
, rd_size_arg_off
;
71 static int initrd_loaded
= 0;
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
86 grub_relocator_chunk_t ch
;
87 grub_uint32_t
*memsize
;
91 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
93 grub_strlen (params
) + 1 + 8);
96 memsize
= get_virtual_current_address (ch
);
98 *memsize
= grub_mmap_get_lower ();
100 str
= (char *) (magic
+ 1);
101 grub_strcpy (str
, params
);
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
;
116 grub_relocator32_boot (relocator
, state
);
118 return GRUB_ERR_NONE
;
122 grub_linux_unload (void)
124 grub_relocator_unload (relocator
);
125 grub_dl_unref (my_mod
);
127 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
134 return GRUB_ERR_NONE
;
138 grub_linux_load32 (grub_elf_t elf
, const char *filename
,
139 void **extra_mem
, grub_size_t extra_size
)
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);
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 ();
163 grub_relocator_chunk_t ch
;
164 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
165 target_addr
& 0x1fffffff,
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);
179 grub_linux_load64 (grub_elf_t elf
, const char *filename
,
180 void **extra_mem
, grub_size_t extra_size
)
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);
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 ();
204 grub_relocator_chunk_t ch
;
205 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
206 target_addr
& 0x1fffffff,
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);
220 grub_cmd_linux (grub_command_t cmd
__attribute__ ((unused
)),
221 int argc
, char *argv
[])
226 #ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
228 grub_uint32_t
*linux_argv
;
232 #ifdef GRUB_MACHINE_MIPS_LOONGSON
234 grub_uint32_t
*linux_envp
;
238 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
240 elf
= grub_elf_open (argv
[0]);
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 ();
255 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
260 #ifdef GRUB_MACHINE_MIPS_LOONGSON
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);
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);
292 if (grub_elf_is_elf32 (elf
))
293 err
= grub_linux_load32 (elf
, argv
[0], &extra
, size
);
295 if (grub_elf_is_elf64 (elf
))
296 err
= grub_linux_load64 (elf
, argv
[0], &extra
, size
);
298 err
= grub_error (GRUB_ERR_BAD_OS
, N_("invalid arch-dependent ELF magic"));
300 grub_elf_close (elf
);
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
));
311 grub_linux_unload ();
315 grub_memcpy (params
, LINUX_IMAGE
, sizeof (LINUX_IMAGE
));
316 grub_create_loader_cmdline (argc
, argv
, params
+ sizeof (LINUX_IMAGE
) - 1,
320 argv_off
= (grub_uint8_t
*) linux_argv
- (grub_uint8_t
*) playground
;
321 extra
= linux_argv
+ (linux_argc
+ 1 + 2);
324 grub_memcpy (linux_args
, "a0", sizeof ("a0"));
325 *linux_argv
= (grub_uint8_t
*) linux_args
- (grub_uint8_t
*) playground
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
))
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
342 linux_args
+= ALIGN_UP (sizeof (loongson_machtypes
[mtype
]), 4);
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
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);
361 rd_size_arg_off
= (grub_uint8_t
*) linux_args
- (grub_uint8_t
*) playground
;
362 linux_args
+= ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
370 #ifdef GRUB_MACHINE_MIPS_LOONGSON
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"),
376 (unsigned long long) grub_mmap_get_lower () >> 20);
377 linux_envp
[0] = (grub_uint8_t
*) linux_envs
- (grub_uint8_t
*) playground
379 linux_envs
+= ALIGN_UP (grub_strlen (linux_envs
) + 1, 4);
380 grub_snprintf (linux_envs
, sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"),
382 (unsigned long long) grub_mmap_get_upper () >> 20);
383 linux_envp
[1] = (grub_uint8_t
*) linux_envs
- (grub_uint8_t
*) playground
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
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
396 linux_envs
+= ALIGN_UP (grub_strlen (linux_envs
) + 1, 4);
402 grub_loader_set (grub_linux_boot
, grub_linux_unload
, 1);
405 grub_dl_ref (my_mod
);
407 return GRUB_ERR_NONE
;
411 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
412 int argc
, char *argv
[])
414 grub_size_t size
= 0;
416 grub_addr_t initrd_dest
;
418 struct grub_linux_initrd_context initrd_ctx
= { 0, 0, 0 };
421 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
424 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("you need to load the kernel first"));
427 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "only one initrd command can be issued.");
429 if (grub_initrd_init (argc
, argv
, &initrd_ctx
))
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,
442 GRUB_RELOCATOR_PREFERENCE_NONE
, 0);
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
))
453 #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
456 tmp
= grub_xasprintf ("%s rd_start=0x%" PRIxGRUB_ADDR
457 " rd_size=0x%" PRIxGRUB_ADDR
, params
,
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
;
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
;
483 grub_initrd_close (&initrd_ctx
);
488 static grub_command_t cmd_linux
, cmd_initrd
;
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."));
501 grub_unregister_command (cmd_linux
);
502 grub_unregister_command (cmd_initrd
);