1 /* linux.c - boot Linux */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003, 2004, 2005, 2007 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/rescue.h>
26 #include <grub/misc.h>
27 #include <grub/ieee1275/ieee1275.h>
28 #include <grub/machine/loader.h>
30 #define ELF32_LOADMASK (0xc0000000UL)
31 #define ELF64_LOADMASK (0xc000000000000000ULL)
33 static grub_dl_t my_mod
;
37 static grub_addr_t initrd_addr
;
38 static grub_size_t initrd_size
;
40 static grub_addr_t linux_addr
;
41 static grub_size_t linux_size
;
43 static char *linux_args
;
45 typedef void (*kernel_entry_t
) (void *, unsigned long, int (void *),
46 unsigned long, unsigned long);
49 grub_linux_boot (void)
51 kernel_entry_t linuxmain
;
54 /* Set the command line arguments. */
55 grub_ieee1275_set_property (grub_ieee1275_chosen
, "bootargs", linux_args
,
56 grub_strlen (linux_args
) + 1, &actual
);
58 grub_dprintf ("loader", "Entry point: 0x%x\n", linux_addr
);
59 grub_dprintf ("loader", "Initrd at: 0x%x, size 0x%x\n", initrd_addr
,
61 grub_dprintf ("loader", "Boot arguments: %s\n", linux_args
);
62 grub_dprintf ("loader", "Jumping to Linux...\n");
64 /* Boot the kernel. */
65 linuxmain
= (kernel_entry_t
) linux_addr
;
66 linuxmain ((void *) initrd_addr
, initrd_size
, grub_ieee1275_entry_fn
, 0, 0);
72 grub_linux_release_mem (void)
74 grub_free (linux_args
);
77 if (linux_addr
&& grub_ieee1275_release (linux_addr
, linux_size
))
78 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "Can not release memory");
80 if (initrd_addr
&& grub_ieee1275_release (initrd_addr
, initrd_size
))
81 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "Can not release memory");
90 grub_linux_unload (void)
94 err
= grub_linux_release_mem ();
95 grub_dl_unref (my_mod
);
103 grub_linux_load32 (grub_elf_t elf
)
108 /* Linux's entry point incorrectly contains a virtual address. */
109 entry
= elf
->ehdr
.ehdr32
.e_entry
& ~ELF32_LOADMASK
;
113 linux_size
= grub_elf32_size (elf
);
116 /* Pad it; the kernel scribbles over memory beyond its load address. */
117 linux_size
+= 0x100000;
119 /* On some systems, firmware occupies the memory we're trying to use.
120 * Happily, Linux can be loaded anywhere (it relocates itself). Iterate
121 * until we find an open area. */
122 for (linux_addr
= entry
; linux_addr
< entry
+ 200 * 0x100000; linux_addr
+= 0x100000)
124 grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
125 linux_addr
, linux_size
);
126 found_addr
= grub_claimmap (linux_addr
, linux_size
);
127 if (found_addr
!= -1)
130 if (found_addr
== -1)
131 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "Could not claim memory.");
133 /* Now load the segments into the area we claimed. */
134 auto grub_err_t
offset_phdr (Elf32_Phdr
*phdr
, grub_addr_t
*addr
);
135 grub_err_t
offset_phdr (Elf32_Phdr
*phdr
, grub_addr_t
*addr
)
137 /* Linux's program headers incorrectly contain virtual addresses.
138 * Translate those to physical, and offset to the area we claimed. */
139 *addr
= (phdr
->p_paddr
& ~ELF32_LOADMASK
) + linux_addr
;
142 return grub_elf32_load (elf
, offset_phdr
, 0, 0);
146 grub_linux_load64 (grub_elf_t elf
)
151 /* Linux's entry point incorrectly contains a virtual address. */
152 entry
= elf
->ehdr
.ehdr64
.e_entry
& ~ELF64_LOADMASK
;
156 linux_size
= grub_elf64_size (elf
);
159 /* Pad it; the kernel scribbles over memory beyond its load address. */
160 linux_size
+= 0x100000;
162 /* On some systems, firmware occupies the memory we're trying to use.
163 * Happily, Linux can be loaded anywhere (it relocates itself). Iterate
164 * until we find an open area. */
165 for (linux_addr
= entry
; linux_addr
< entry
+ 200 * 0x100000; linux_addr
+= 0x100000)
167 grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
168 linux_addr
, linux_size
);
169 found_addr
= grub_claimmap (linux_addr
, linux_size
);
170 if (found_addr
!= -1)
173 if (found_addr
== -1)
174 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "Could not claim memory.");
176 /* Now load the segments into the area we claimed. */
177 auto grub_err_t
offset_phdr (Elf64_Phdr
*phdr
, grub_addr_t
*addr
);
178 grub_err_t
offset_phdr (Elf64_Phdr
*phdr
, grub_addr_t
*addr
)
180 /* Linux's program headers incorrectly contain virtual addresses.
181 * Translate those to physical, and offset to the area we claimed. */
182 *addr
= (phdr
->p_paddr
& ~ELF64_LOADMASK
) + linux_addr
;
185 return grub_elf64_load (elf
, offset_phdr
, 0, 0);
189 grub_rescue_cmd_linux (int argc
, char *argv
[])
196 grub_dl_ref (my_mod
);
200 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no kernel specified");
204 elf
= grub_elf_open (argv
[0]);
208 if (elf
->ehdr
.ehdr32
.e_type
!= ET_EXEC
)
210 grub_error (GRUB_ERR_UNKNOWN_OS
,
211 "This ELF file is not of the right type\n");
215 /* Release the previously used memory. */
216 grub_loader_unset ();
218 if (grub_elf_is_elf32 (elf
))
219 grub_linux_load32 (elf
);
221 if (grub_elf_is_elf64 (elf
))
222 grub_linux_load64 (elf
);
225 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "Unknown ELF class");
229 size
= sizeof ("BOOT_IMAGE=") + grub_strlen (argv
[0]);
230 for (i
= 0; i
< argc
; i
++)
231 size
+= grub_strlen (argv
[i
]) + 1;
233 linux_args
= grub_malloc (size
);
237 /* Specify the boot file. */
238 dest
= grub_stpcpy (linux_args
, "BOOT_IMAGE=");
239 dest
= grub_stpcpy (dest
, argv
[0]);
241 for (i
= 1; i
< argc
; i
++)
244 dest
= grub_stpcpy (dest
, argv
[i
]);
250 grub_elf_close (elf
);
252 if (grub_errno
!= GRUB_ERR_NONE
)
254 grub_linux_release_mem ();
255 grub_dl_unref (my_mod
);
260 grub_loader_set (grub_linux_boot
, grub_linux_unload
, 1);
267 grub_rescue_cmd_initrd (int argc
, char *argv
[])
269 grub_file_t file
= 0;
271 grub_addr_t first_addr
;
277 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no initrd specified");
283 grub_error (GRUB_ERR_BAD_ARGUMENT
, "You need to load the kernel first.");
287 file
= grub_file_open (argv
[0]);
291 first_addr
= linux_addr
+ linux_size
;
292 size
= grub_file_size (file
);
294 /* Attempt to claim at a series of addresses until successful in
295 the same way that grub_rescue_cmd_linux does. */
296 for (addr
= first_addr
; addr
< first_addr
+ 200 * 0x100000; addr
+= 0x100000)
298 grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
300 found_addr
= grub_claimmap (addr
, size
);
301 if (found_addr
!= -1)
305 if (found_addr
== -1)
307 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "Can not claim memory");
311 grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr
, size
);
313 if (grub_file_read (file
, (void *) addr
, size
) != size
)
315 grub_ieee1275_release (addr
, size
);
316 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
325 grub_file_close (file
);
332 grub_rescue_register_command ("linux", grub_rescue_cmd_linux
,
333 "load a linux kernel");
334 grub_rescue_register_command ("initrd", grub_rescue_cmd_initrd
,
341 grub_rescue_unregister_command ("linux");
342 grub_rescue_unregister_command ("initrd");