1
/* linux.c - boot Linux */
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/>.
21
#include <grub/elfload.h>
22
#include <grub/loader.h>
25
#include <grub/misc.h>
26
#include <grub/ieee1275/ieee1275.h>
27
#include <grub/machine/loader.h>
28
#include <grub/command.h>
29
#include <grub/i18n.h>
31
#define ELF32_LOADMASK (0xc0000000UL)
32
#define ELF64_LOADMASK (0xc000000000000000ULL)
34
static grub_dl_t my_mod;
38
static grub_addr_t initrd_addr;
39
static grub_size_t initrd_size;
41
static grub_addr_t linux_addr;
42
static grub_size_t linux_size;
44
static char *linux_args;
46
typedef void (*kernel_entry_t) (void *, unsigned long, int (void *),
47
unsigned long, unsigned long);
50
grub_linux_boot (void)
52
kernel_entry_t linuxmain;
55
/* Set the command line arguments. */
56
grub_ieee1275_set_property (grub_ieee1275_chosen, "bootargs", linux_args,
57
grub_strlen (linux_args) + 1, &actual);
59
grub_dprintf ("loader", "Entry point: 0x%x\n", linux_addr);
60
grub_dprintf ("loader", "Initrd at: 0x%x, size 0x%x\n", initrd_addr,
62
grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
63
grub_dprintf ("loader", "Jumping to Linux...\n");
65
/* Boot the kernel. */
66
linuxmain = (kernel_entry_t) linux_addr;
67
linuxmain ((void *) initrd_addr, initrd_size, grub_ieee1275_entry_fn, 0, 0);
73
grub_linux_release_mem (void)
75
grub_free (linux_args);
78
if (linux_addr && grub_ieee1275_release (linux_addr, linux_size))
79
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory");
81
if (initrd_addr && grub_ieee1275_release (initrd_addr, initrd_size))
82
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory");
91
grub_linux_unload (void)
95
err = grub_linux_release_mem ();
96
grub_dl_unref (my_mod);
104
grub_linux_load32 (grub_elf_t elf)
109
/* Linux's entry point incorrectly contains a virtual address. */
110
entry = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK;
114
linux_size = grub_elf32_size (elf, 0);
117
/* Pad it; the kernel scribbles over memory beyond its load address. */
118
linux_size += 0x100000;
120
/* On some systems, firmware occupies the memory we're trying to use.
121
* Happily, Linux can be loaded anywhere (it relocates itself). Iterate
122
* until we find an open area. */
123
for (linux_addr = entry; linux_addr < entry + 200 * 0x100000; linux_addr += 0x100000)
125
grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
126
linux_addr, linux_size);
127
found_addr = grub_claimmap (linux_addr, linux_size);
128
if (found_addr != -1)
131
if (found_addr == -1)
132
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory");
134
/* Now load the segments into the area we claimed. */
135
auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load);
136
grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load)
138
if (phdr->p_type != PT_LOAD)
145
/* Linux's program headers incorrectly contain virtual addresses.
146
* Translate those to physical, and offset to the area we claimed. */
147
*addr = (phdr->p_paddr & ~ELF32_LOADMASK) + linux_addr;
150
return grub_elf32_load (elf, offset_phdr, 0, 0);
154
grub_linux_load64 (grub_elf_t elf)
159
/* Linux's entry point incorrectly contains a virtual address. */
160
entry = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK;
164
linux_size = grub_elf64_size (elf, 0);
167
/* Pad it; the kernel scribbles over memory beyond its load address. */
168
linux_size += 0x100000;
170
/* On some systems, firmware occupies the memory we're trying to use.
171
* Happily, Linux can be loaded anywhere (it relocates itself). Iterate
172
* until we find an open area. */
173
for (linux_addr = entry; linux_addr < entry + 200 * 0x100000; linux_addr += 0x100000)
175
grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
176
linux_addr, linux_size);
177
found_addr = grub_claimmap (linux_addr, linux_size);
178
if (found_addr != -1)
181
if (found_addr == -1)
182
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory");
184
/* Now load the segments into the area we claimed. */
185
auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load);
186
grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load)
188
if (phdr->p_type != PT_LOAD)
194
/* Linux's program headers incorrectly contain virtual addresses.
195
* Translate those to physical, and offset to the area we claimed. */
196
*addr = (phdr->p_paddr & ~ELF64_LOADMASK) + linux_addr;
199
return grub_elf64_load (elf, offset_phdr, 0, 0);
203
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
204
int argc, char *argv[])
211
grub_dl_ref (my_mod);
215
grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
219
elf = grub_elf_open (argv[0]);
223
if (elf->ehdr.ehdr32.e_type != ET_EXEC)
225
grub_error (GRUB_ERR_UNKNOWN_OS,
226
"this ELF file is not of the right type");
230
/* Release the previously used memory. */
231
grub_loader_unset ();
233
if (grub_elf_is_elf32 (elf))
234
grub_linux_load32 (elf);
236
if (grub_elf_is_elf64 (elf))
237
grub_linux_load64 (elf);
240
grub_error (GRUB_ERR_BAD_FILE_TYPE, "unknown ELF class");
244
size = sizeof ("BOOT_IMAGE=") + grub_strlen (argv[0]);
245
for (i = 0; i < argc; i++)
246
size += grub_strlen (argv[i]) + 1;
248
linux_args = grub_malloc (size);
252
/* Specify the boot file. */
253
dest = grub_stpcpy (linux_args, "BOOT_IMAGE=");
254
dest = grub_stpcpy (dest, argv[0]);
256
for (i = 1; i < argc; i++)
259
dest = grub_stpcpy (dest, argv[i]);
265
grub_elf_close (elf);
267
if (grub_errno != GRUB_ERR_NONE)
269
grub_linux_release_mem ();
270
grub_dl_unref (my_mod);
275
grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
284
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
285
int argc, char *argv[])
287
grub_file_t file = 0;
289
grub_addr_t first_addr;
295
grub_error (GRUB_ERR_BAD_ARGUMENT, "no initrd specified");
301
grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
305
file = grub_file_open (argv[0]);
309
first_addr = linux_addr + linux_size;
310
size = grub_file_size (file);
312
/* Attempt to claim at a series of addresses until successful in
313
the same way that grub_rescue_cmd_linux does. */
314
for (addr = first_addr; addr < first_addr + 200 * 0x100000; addr += 0x100000)
316
grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
318
found_addr = grub_claimmap (addr, size);
319
if (found_addr != -1)
323
if (found_addr == -1)
325
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot claim memory");
329
grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr, size);
331
if (grub_file_read (file, (void *) addr, size) != size)
333
grub_ieee1275_release (addr, size);
334
grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
343
grub_file_close (file);
348
static grub_command_t cmd_linux, cmd_initrd;
352
cmd_linux = grub_register_command ("linux", grub_cmd_linux,
353
0, N_("Load Linux."));
354
cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
355
0, N_("Load initrd."));
361
grub_unregister_command (cmd_linux);
362
grub_unregister_command (cmd_initrd);