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/command.h>
28 #include <grub/i18n.h>
29 #include <grub/memory.h>
30 #include <grub/lib/cmdline.h>
31 #include <grub/linux.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 static grub_dl_t my_mod
;
39 /* /virtual-memory/translations property layout */
40 struct grub_ieee1275_translation
{
46 static struct grub_ieee1275_translation
*of_trans
;
47 static int of_num_trans
;
49 static grub_addr_t phys_base
;
50 static grub_addr_t grub_phys_start
;
51 static grub_addr_t grub_phys_end
;
53 static grub_addr_t initrd_addr
;
54 static grub_addr_t initrd_paddr
;
55 static grub_size_t initrd_size
;
57 static Elf64_Addr linux_entry
;
58 static grub_addr_t linux_addr
;
59 static grub_addr_t linux_paddr
;
60 static grub_size_t linux_size
;
62 static char *linux_args
;
64 struct linux_bootstr_info
{
70 /* All HdrS versions support these fields. */
71 unsigned int start_insns
[2];
72 char magic
[4]; /* "HdrS" */
73 unsigned int linux_kernel_version
; /* LINUX_VERSION_CODE */
74 unsigned short hdrs_version
;
75 unsigned short root_flags
;
76 unsigned short root_dev
;
77 unsigned short ram_flags
;
78 unsigned int __deprecated_ramdisk_image
;
79 unsigned int ramdisk_size
;
81 /* HdrS versions 0x0201 and higher only */
84 /* HdrS versions 0x0202 and higher only */
85 struct linux_bootstr_info
*bootstr_info
;
87 /* HdrS versions 0x0301 and higher only */
88 unsigned long ramdisk_image
;
92 grub_linux_boot (void)
94 struct linux_bootstr_info
*bp
;
95 struct linux_hdrs
*hp
;
98 hp
= (struct linux_hdrs
*) linux_addr
;
100 /* Any pointer we dereference in the kernel image must be relocated
101 to where we actually loaded the kernel. */
102 addr
= (grub_addr_t
) hp
->bootstr_info
;
103 addr
+= (linux_addr
- linux_entry
);
104 bp
= (struct linux_bootstr_info
*) addr
;
106 /* Set the command line arguments, unless the kernel has been
107 built with a fixed CONFIG_CMDLINE. */
110 int len
= grub_strlen (linux_args
) + 1;
113 memcpy(bp
->buf
, linux_args
, len
);
114 bp
->buf
[len
-1] = '\0';
120 /* The kernel expects the physical address, adjusted relative
121 to the lowest address advertised in "/memory"'s available
124 The history of this is that back when the kernel only supported
125 specifying a 32-bit ramdisk address, this was the way to still
126 be able to specify the ramdisk physical address even if memory
127 started at some place above 4GB.
129 The magic 0x400000 is KERNBASE, I have no idea why SILO adds
130 that term into the address, but it does and thus we have to do
131 it too as this is what the kernel expects. */
132 hp
->ramdisk_image
= initrd_paddr
- phys_base
+ 0x400000;
133 hp
->ramdisk_size
= initrd_size
;
136 grub_dprintf ("loader", "Entry point: 0x%lx\n", linux_addr
);
137 grub_dprintf ("loader", "Initrd at: 0x%lx, size 0x%lx\n", initrd_addr
,
139 grub_dprintf ("loader", "Boot arguments: %s\n", linux_args
);
140 grub_dprintf ("loader", "Jumping to Linux...\n");
142 /* Boot the kernel. */
143 asm volatile ("sethi %hi(grub_ieee1275_entry_fn), %o1\n"
144 "ldx [%o1 + %lo(grub_ieee1275_entry_fn)], %o4\n"
145 "sethi %hi(grub_ieee1275_original_stack), %o1\n"
146 "ldx [%o1 + %lo(grub_ieee1275_original_stack)], %o6\n"
147 "sethi %hi(linux_addr), %o1\n"
148 "ldx [%o1 + %lo(linux_addr)], %o5\n"
155 return GRUB_ERR_NONE
;
159 grub_linux_release_mem (void)
161 grub_free (linux_args
);
166 return GRUB_ERR_NONE
;
170 grub_linux_unload (void)
174 err
= grub_linux_release_mem ();
175 grub_dl_unref (my_mod
);
182 #define FOUR_MB (4 * 1024 * 1024)
184 /* Context for alloc_phys. */
185 struct alloc_phys_ctx
191 /* Helper for alloc_phys. */
193 alloc_phys_choose (grub_uint64_t addr
, grub_uint64_t len
,
194 grub_memory_type_t type
, void *data
)
196 struct alloc_phys_ctx
*ctx
= data
;
197 grub_addr_t end
= addr
+ len
;
202 addr
= ALIGN_UP (addr
, FOUR_MB
);
203 if (addr
+ ctx
->size
>= end
)
206 if (addr
>= grub_phys_start
&& addr
< grub_phys_end
)
208 addr
= ALIGN_UP (grub_phys_end
, FOUR_MB
);
209 if (addr
+ ctx
->size
>= end
)
212 if ((addr
+ ctx
->size
) >= grub_phys_start
213 && (addr
+ ctx
->size
) < grub_phys_end
)
215 addr
= ALIGN_UP (grub_phys_end
, FOUR_MB
);
216 if (addr
+ ctx
->size
>= end
)
222 grub_addr_t linux_end
= ALIGN_UP (linux_paddr
+ linux_size
, FOUR_MB
);
224 if (addr
>= linux_paddr
&& addr
< linux_end
)
227 if (addr
+ ctx
->size
>= end
)
230 if ((addr
+ ctx
->size
) >= linux_paddr
231 && (addr
+ ctx
->size
) < linux_end
)
234 if (addr
+ ctx
->size
>= end
)
244 alloc_phys (grub_addr_t size
)
246 struct alloc_phys_ctx ctx
= {
248 .ret
= (grub_addr_t
) -1
251 grub_machine_mmap_iterate (alloc_phys_choose
, &ctx
);
257 grub_linux_load64 (grub_elf_t elf
, const char *filename
)
259 grub_addr_t off
, paddr
, base
;
262 linux_entry
= elf
->ehdr
.ehdr64
.e_entry
;
263 linux_addr
= 0x40004000;
265 linux_size
= grub_elf64_size (elf
, 0, 0);
269 grub_dprintf ("loader", "Attempting to claim at 0x%lx, size 0x%lx.\n",
270 linux_addr
, linux_size
);
272 paddr
= alloc_phys (linux_size
+ off
);
273 if (paddr
== (grub_addr_t
) -1)
274 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
275 "couldn't allocate physical memory");
276 ret
= grub_ieee1275_map (paddr
, linux_addr
- off
,
277 linux_size
+ off
, IEEE1275_MAP_DEFAULT
);
279 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
280 "couldn't map physical memory");
282 grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
283 linux_addr
, paddr
, linux_size
);
287 base
= linux_entry
- off
;
289 /* Now load the segments into the area we claimed. */
290 return grub_elf64_load (elf
, filename
, (void *) (linux_addr
- off
- base
), GRUB_ELF_LOAD_FLAGS_NONE
, 0, 0);
294 grub_cmd_linux (grub_command_t cmd
__attribute__ ((unused
)),
295 int argc
, char *argv
[])
297 grub_file_t file
= 0;
301 grub_dl_ref (my_mod
);
305 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
309 file
= grub_file_open (argv
[0]);
313 elf
= grub_elf_file (file
, argv
[0]);
317 if (elf
->ehdr
.ehdr32
.e_type
!= ET_EXEC
)
319 grub_error (GRUB_ERR_UNKNOWN_OS
,
320 N_("this ELF file is not of the right type"));
324 /* Release the previously used memory. */
325 grub_loader_unset ();
327 if (grub_elf_is_elf64 (elf
))
328 grub_linux_load64 (elf
, argv
[0]);
331 grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("invalid arch-dependent ELF magic"));
335 size
= grub_loader_cmdline_size(argc
, argv
);
337 linux_args
= grub_malloc (size
+ sizeof (LINUX_IMAGE
));
341 /* Create kernel command line. */
342 grub_memcpy (linux_args
, LINUX_IMAGE
, sizeof (LINUX_IMAGE
));
343 grub_create_loader_cmdline (argc
, argv
, linux_args
+ sizeof (LINUX_IMAGE
) - 1,
348 grub_elf_close (elf
);
350 grub_file_close (file
);
352 if (grub_errno
!= GRUB_ERR_NONE
)
354 grub_linux_release_mem ();
355 grub_dl_unref (my_mod
);
360 grub_loader_set (grub_linux_boot
, grub_linux_unload
, 1);
369 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
370 int argc
, char *argv
[])
372 grub_size_t size
= 0;
376 struct grub_linux_initrd_context initrd_ctx
= { 0, 0, 0 };
380 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
386 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("you need to load the kernel first"));
390 if (grub_initrd_init (argc
, argv
, &initrd_ctx
))
393 size
= grub_get_initrd_size (&initrd_ctx
);
397 paddr
= alloc_phys (size
);
398 if (paddr
== (grub_addr_t
) -1)
400 grub_error (GRUB_ERR_OUT_OF_MEMORY
,
401 "couldn't allocate physical memory");
404 ret
= grub_ieee1275_map (paddr
, addr
, size
, IEEE1275_MAP_DEFAULT
);
407 grub_error (GRUB_ERR_OUT_OF_MEMORY
,
408 "couldn't map physical memory");
412 grub_dprintf ("loader", "Loading initrd at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
415 if (grub_initrd_load (&initrd_ctx
, argv
, (void *) addr
))
419 initrd_paddr
= paddr
;
423 grub_initrd_close (&initrd_ctx
);
428 /* Helper for determine_phys_base. */
430 get_physbase (grub_uint64_t addr
, grub_uint64_t len
__attribute__ ((unused
)),
431 grub_memory_type_t type
, void *data
__attribute__ ((unused
)))
435 if (addr
< phys_base
)
441 determine_phys_base (void)
443 phys_base
= ~(grub_uint64_t
) 0;
444 grub_machine_mmap_iterate (get_physbase
, NULL
);
448 fetch_translations (void)
450 grub_ieee1275_phandle_t node
;
454 if (grub_ieee1275_finddevice ("/virtual-memory", &node
))
456 grub_printf ("Cannot find /virtual-memory node.\n");
460 if (grub_ieee1275_get_property_length (node
, "translations", &actual
))
462 grub_printf ("Cannot find /virtual-memory/translations size.\n");
466 of_trans
= grub_malloc (actual
);
469 grub_printf ("Cannot allocate translations buffer.\n");
473 if (grub_ieee1275_get_property (node
, "translations", of_trans
, actual
, &actual
))
475 grub_printf ("Cannot fetch /virtual-memory/translations property.\n");
479 of_num_trans
= actual
/ sizeof(struct grub_ieee1275_translation
);
481 for (i
= 0; i
< of_num_trans
; i
++)
483 struct grub_ieee1275_translation
*p
= &of_trans
[i
];
485 if (p
->vaddr
== 0x2000)
487 grub_addr_t phys
, tte
= p
->data
;
489 phys
= tte
& ~(0xff00000000001fffULL
);
491 grub_phys_start
= phys
;
492 grub_phys_end
= grub_phys_start
+ p
->size
;
493 grub_dprintf ("loader", "Grub lives at phys_start[%lx] phys_end[%lx]\n",
494 (unsigned long) grub_phys_start
,
495 (unsigned long) grub_phys_end
);
502 static grub_command_t cmd_linux
, cmd_initrd
;
506 determine_phys_base ();
507 fetch_translations ();
509 cmd_linux
= grub_register_command ("linux", grub_cmd_linux
,
510 0, N_("Load Linux."));
511 cmd_initrd
= grub_register_command ("initrd", grub_cmd_initrd
,
512 0, N_("Load initrd."));
518 grub_unregister_command (cmd_linux
);
519 grub_unregister_command (cmd_initrd
);