2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2008,2010 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/loader.h>
20 #include <grub/file.h>
21 #include <grub/disk.h>
23 #include <grub/misc.h>
24 #include <grub/types.h>
25 #include <grub/command.h>
28 #include <grub/cache.h>
29 #include <grub/kernel.h>
30 #include <grub/efi/api.h>
31 #include <grub/efi/efi.h>
33 #include <grub/i18n.h>
35 GRUB_MOD_LICENSE ("GPLv3+");
37 #define ALIGN_MIN (256*1024*1024)
39 #define GRUB_ELF_SEARCH 1024
41 #define BOOT_PARAM_SIZE 16384
43 struct ia64_boot_param
45 grub_uint64_t command_line
; /* physical address of command line. */
46 grub_uint64_t efi_systab
; /* physical address of EFI system table */
47 grub_uint64_t efi_memmap
; /* physical address of EFI memory map */
48 grub_uint64_t efi_memmap_size
; /* size of EFI memory map */
49 grub_uint64_t efi_memdesc_size
; /* size of an EFI memory map descriptor */
50 grub_uint32_t efi_memdesc_version
; /* memory descriptor version */
53 grub_uint16_t num_cols
; /* number of columns on console output dev */
54 grub_uint16_t num_rows
; /* number of rows on console output device */
55 grub_uint16_t orig_x
; /* cursor's x position */
56 grub_uint16_t orig_y
; /* cursor's y position */
58 grub_uint64_t fpswa
; /* physical address of the fpswa interface */
59 grub_uint64_t initrd_start
;
60 grub_uint64_t initrd_size
;
61 grub_uint64_t domain_start
; /* boot domain address. */
62 grub_uint64_t domain_size
; /* how big is the boot domain */
63 grub_uint64_t payloads_chain
;
64 grub_uint64_t payloads_nbr
;
67 struct ia64_boot_payload
72 /* Payload command line */
73 grub_uint64_t cmdline
;
80 grub_uint32_t revision
;
81 grub_uint32_t reserved
;
84 static fpswa_interface_t
*fpswa
;
86 #define NEXT_MEMORY_DESCRIPTOR(desc, size) \
87 ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
89 static grub_dl_t my_mod
;
93 /* Kernel base and size. */
94 static void *kernel_mem
;
95 static grub_efi_uintn_t kernel_pages
;
96 static grub_uint64_t entry
;
98 /* Initrd base and size. */
99 static void *initrd_mem
;
100 static grub_efi_uintn_t initrd_pages
;
101 static grub_efi_uintn_t initrd_size
;
103 static struct ia64_boot_param
*boot_param
;
104 static grub_efi_uintn_t boot_param_pages
;
105 static struct ia64_boot_payload
*last_payload
= NULL
;
107 /* Can linux kernel be relocated ? */
108 #define RELOCATE_OFF 0 /* No. */
109 #define RELOCATE_ON 1 /* Yes. */
110 #define RELOCATE_FORCE 2 /* Always - used to debug. */
111 static int relocate
= RELOCATE_OFF
;
113 static inline grub_size_t
114 page_align (grub_size_t size
)
116 return (size
+ (1 << 12) - 1) & (~((1 << 12) - 1));
122 grub_efi_handle_t fpswa_image
;
123 grub_efi_boot_services_t
*bs
;
124 grub_efi_status_t status
;
125 grub_efi_uintn_t size
;
126 static const grub_efi_guid_t fpswa_protocol
=
127 { 0xc41b6531, 0x97b9, 0x11d3,
128 {0x9a, 0x29, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
133 size
= sizeof(grub_efi_handle_t
);
135 bs
= grub_efi_system_table
->boot_services
;
136 status
= bs
->locate_handle (GRUB_EFI_BY_PROTOCOL
,
137 (void *)&fpswa_protocol
,
138 NULL
, &size
, &fpswa_image
);
139 if (status
!= GRUB_EFI_SUCCESS
)
141 grub_printf (_("Could not locate FPSWA driver\n"));
144 status
= bs
->handle_protocol (fpswa_image
,
145 (void *)&fpswa_protocol
, (void *)&fpswa
);
146 if (status
!= GRUB_EFI_SUCCESS
)
148 grub_printf (_("Fpswa protocol not able find the interface\n"));
153 /* Find the optimal number of pages for the memory map. Is it better to
154 move this code to efi/mm.c? */
155 static grub_efi_uintn_t
156 find_mmap_size (void)
158 static grub_efi_uintn_t mmap_size
= 0;
163 mmap_size
= (1 << 12);
167 grub_efi_memory_descriptor_t
*mmap
;
168 grub_efi_uintn_t desc_size
;
170 mmap
= grub_malloc (mmap_size
);
174 ret
= grub_efi_get_memory_map (&mmap_size
, mmap
, 0, &desc_size
, 0);
179 grub_error (GRUB_ERR_IO
, "cannot get memory map");
185 mmap_size
+= (1 << 12);
188 /* Increase the size a bit for safety, because GRUB allocates more on
189 later, and EFI itself may allocate more. */
190 mmap_size
+= (1 << 12);
192 return page_align (mmap_size
);
200 grub_efi_free_pages ((grub_addr_t
) kernel_mem
, kernel_pages
);
206 grub_efi_free_pages ((grub_addr_t
) initrd_mem
, initrd_pages
);
212 struct ia64_boot_payload
*payload
;
213 struct ia64_boot_payload
*next_payload
;
216 payload
= (struct ia64_boot_payload
*)boot_param
->payloads_chain
;
219 next_payload
= (struct ia64_boot_payload
*)payload
->next
;
222 (payload
->start
, page_align (payload
->length
) >> 12);
223 grub_efi_free_pages ((grub_efi_physical_address_t
)payload
, 1);
225 payload
= next_payload
;
228 /* Free bootparam. */
229 grub_efi_free_pages ((grub_efi_physical_address_t
)boot_param
,
236 allocate_pages (grub_uint64_t align
, grub_uint64_t size_pages
,
237 grub_uint64_t nobase
)
240 grub_efi_uintn_t desc_size
;
241 grub_efi_memory_descriptor_t
*mmap
, *mmap_end
;
242 grub_efi_uintn_t mmap_size
, tmp_mmap_size
;
243 grub_efi_memory_descriptor_t
*desc
;
246 size
= size_pages
<< 12;
248 mmap_size
= find_mmap_size ();
252 /* Read the memory map temporarily, to find free space. */
253 mmap
= grub_malloc (mmap_size
);
257 tmp_mmap_size
= mmap_size
;
258 if (grub_efi_get_memory_map (&tmp_mmap_size
, mmap
, 0, &desc_size
, 0) <= 0)
260 grub_error (GRUB_ERR_IO
, "cannot get memory map");
264 mmap_end
= NEXT_MEMORY_DESCRIPTOR (mmap
, tmp_mmap_size
);
266 /* First, find free pages for the real mode code
267 and the memory map buffer. */
270 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
))
272 grub_uint64_t start
, end
;
273 grub_uint64_t aligned_start
;
275 if (desc
->type
!= GRUB_EFI_CONVENTIONAL_MEMORY
)
278 start
= desc
->physical_start
;
279 end
= start
+ (desc
->num_pages
<< 12);
280 /* Align is a power of 2. */
281 aligned_start
= (start
+ align
- 1) & ~(align
- 1);
282 if (aligned_start
+ size
> end
)
284 if (aligned_start
== nobase
)
285 aligned_start
+= align
;
286 if (aligned_start
+ size
> end
)
288 mem
= grub_efi_allocate_pages (aligned_start
, size_pages
);
291 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "cannot allocate memory");
299 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "cannot allocate memory");
313 set_boot_param_console (void)
315 grub_efi_simple_text_output_interface_t
*conout
;
316 grub_efi_uintn_t cols
, rows
;
318 conout
= grub_efi_system_table
->con_out
;
319 if (conout
->query_mode (conout
, conout
->mode
->mode
, &cols
, &rows
)
323 grub_dprintf ("linux",
324 "Console info: cols=%lu rows=%lu x=%u y=%u\n",
326 conout
->mode
->cursor_column
, conout
->mode
->cursor_row
);
328 boot_param
->console_info
.num_cols
= cols
;
329 boot_param
->console_info
.num_rows
= rows
;
330 boot_param
->console_info
.orig_x
= conout
->mode
->cursor_column
;
331 boot_param
->console_info
.orig_y
= conout
->mode
->cursor_row
;
335 grub_linux_boot (void)
337 grub_efi_uintn_t mmap_size
;
338 grub_efi_uintn_t map_key
;
339 grub_efi_uintn_t desc_size
;
340 grub_efi_uint32_t desc_version
;
341 grub_efi_memory_descriptor_t
*mmap_buf
;
346 boot_param
->fpswa
= (grub_uint64_t
)fpswa
;
349 boot_param
->initrd_start
= (grub_uint64_t
)initrd_mem
;
350 boot_param
->initrd_size
= (grub_uint64_t
)initrd_size
;
352 set_boot_param_console ();
354 grub_dprintf ("linux", "Jump to %016lx\n", entry
);
357 Must be done after grub_machine_fini because map_key is used by
358 exit_boot_services. */
359 mmap_size
= find_mmap_size ();
362 mmap_buf
= grub_efi_allocate_pages (0, page_align (mmap_size
) >> 12);
364 return grub_error (GRUB_ERR_IO
, "cannot allocate memory map");
365 err
= grub_efi_finish_boot_services (&mmap_size
, mmap_buf
, &map_key
,
366 &desc_size
, &desc_version
);
370 boot_param
->efi_memmap
= (grub_uint64_t
)mmap_buf
;
371 boot_param
->efi_memmap_size
= mmap_size
;
372 boot_param
->efi_memdesc_size
= desc_size
;
373 boot_param
->efi_memdesc_version
= desc_version
;
375 /* See you next boot. */
376 asm volatile ("mov r28=%1; br.sptk.few %0" :: "b"(entry
),"r"(boot_param
));
378 /* Never reach here. */
379 return GRUB_ERR_NONE
;
383 grub_linux_unload (void)
386 grub_dl_unref (my_mod
);
388 return GRUB_ERR_NONE
;
392 grub_load_elf64 (grub_file_t file
, void *buffer
, const char *filename
)
394 Elf64_Ehdr
*ehdr
= (Elf64_Ehdr
*) buffer
;
397 grub_uint64_t low_addr
;
398 grub_uint64_t high_addr
;
400 grub_uint64_t reloc_offset
;
402 if (ehdr
->e_ident
[EI_CLASS
] != ELFCLASS64
)
403 return grub_error (GRUB_ERR_UNKNOWN_OS
, "invalid ELF class");
405 if (ehdr
->e_ident
[EI_MAG0
] != ELFMAG0
406 || ehdr
->e_ident
[EI_MAG1
] != ELFMAG1
407 || ehdr
->e_ident
[EI_MAG2
] != ELFMAG2
408 || ehdr
->e_ident
[EI_MAG3
] != ELFMAG3
409 || ehdr
->e_version
!= EV_CURRENT
410 || ehdr
->e_ident
[EI_DATA
] != ELFDATA2LSB
411 || ehdr
->e_machine
!= EM_IA_64
)
412 return grub_error(GRUB_ERR_UNKNOWN_OS
, "no valid ELF header found");
414 if (ehdr
->e_type
!= ET_EXEC
)
415 return grub_error (GRUB_ERR_UNKNOWN_OS
, "invalid ELF file type");
417 /* FIXME: Should we support program headers at strange locations? */
418 if (ehdr
->e_phoff
+ ehdr
->e_phnum
* ehdr
->e_phentsize
> GRUB_ELF_SEARCH
)
419 return grub_error (GRUB_ERR_BAD_OS
, "program header at a too high offset");
421 entry
= ehdr
->e_entry
;
423 /* Compute low, high and align addresses. */
427 for (i
= 0; i
< ehdr
->e_phnum
; i
++)
429 phdr
= (Elf64_Phdr
*) ((char *) buffer
+ ehdr
->e_phoff
430 + i
* ehdr
->e_phentsize
);
431 if (phdr
->p_type
== PT_LOAD
)
433 if (phdr
->p_paddr
< low_addr
)
434 low_addr
= phdr
->p_paddr
;
435 if (phdr
->p_paddr
+ phdr
->p_memsz
> high_addr
)
436 high_addr
= phdr
->p_paddr
+ phdr
->p_memsz
;
437 if (phdr
->p_align
> align
)
438 align
= phdr
->p_align
;
442 if (align
< ALIGN_MIN
)
446 return grub_error (GRUB_ERR_BAD_OS
, "no program entries");
448 kernel_pages
= page_align (high_addr
- low_addr
) >> 12;
450 if (relocate
!= RELOCATE_FORCE
)
452 kernel_mem
= grub_efi_allocate_pages (low_addr
, kernel_pages
);
455 /* Try to relocate. */
456 if (! kernel_mem
&& relocate
!= RELOCATE_OFF
)
458 kernel_mem
= allocate_pages (align
, kernel_pages
, low_addr
);
461 reloc_offset
= (grub_uint64_t
)kernel_mem
- low_addr
;
462 grub_dprintf ("linux", " Relocated at %p (offset=%016lx)\n",
463 kernel_mem
, reloc_offset
);
464 entry
+= reloc_offset
;
468 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
469 "cannot allocate memory for OS");
471 /* Load every loadable segment in memory. */
472 for (i
= 0; i
< ehdr
->e_phnum
; i
++)
474 phdr
= (Elf64_Phdr
*) ((char *) buffer
+ ehdr
->e_phoff
475 + i
* ehdr
->e_phentsize
);
476 if (phdr
->p_type
== PT_LOAD
)
478 grub_dprintf ("linux", " [paddr=%lx load=%lx memsz=%08lx "
479 "off=%lx flags=%x]\n",
480 phdr
->p_paddr
, phdr
->p_paddr
+ reloc_offset
,
481 phdr
->p_memsz
, phdr
->p_offset
, phdr
->p_flags
);
483 if (grub_file_seek (file
, phdr
->p_offset
) == (grub_off_t
)-1)
484 return grub_error (GRUB_ERR_BAD_OS
,
485 "invalid offset in program header");
487 if (grub_file_read (file
, (void *) (phdr
->p_paddr
+ reloc_offset
),
489 != (grub_ssize_t
) phdr
->p_filesz
)
492 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"),
497 if (phdr
->p_filesz
< phdr
->p_memsz
)
499 ((char *)(phdr
->p_paddr
+ reloc_offset
+ phdr
->p_filesz
),
500 0, phdr
->p_memsz
- phdr
->p_filesz
);
502 /* Sync caches if necessary. */
503 if (phdr
->p_flags
& PF_X
)
504 grub_arch_sync_caches
505 ((void *)(phdr
->p_paddr
+ reloc_offset
), phdr
->p_memsz
);
513 grub_cmd_linux (grub_command_t cmd
__attribute__ ((unused
)),
514 int argc
, char *argv
[])
516 grub_file_t file
= 0;
517 char buffer
[GRUB_ELF_SEARCH
];
522 grub_dl_ref (my_mod
);
524 grub_loader_unset ();
528 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No kernel specified");
532 file
= grub_file_open (argv
[0]);
536 len
= grub_file_read (file
, buffer
, sizeof (buffer
));
537 if (len
< (grub_ssize_t
) sizeof (Elf64_Ehdr
))
539 grub_error (GRUB_ERR_BAD_OS
, "File too small");
543 grub_dprintf ("linux", "Loading linux: %s\n", argv
[0]);
545 if (grub_load_elf64 (file
, buffer
, argv
[0]))
548 len
= sizeof("BOOT_IMAGE=") + 8;
549 for (i
= 0; i
< argc
; i
++)
550 len
+= grub_strlen (argv
[i
]) + 1;
551 len
+= sizeof (struct ia64_boot_param
) + 256; /* Room for extensions. */
552 boot_param_pages
= page_align (len
) >> 12;
553 boot_param
= grub_efi_allocate_pages (0, boot_param_pages
);
556 grub_error (GRUB_ERR_OUT_OF_MEMORY
,
557 "cannot allocate memory for bootparams");
561 grub_memset (boot_param
, 0, len
);
562 cmdline
= ((char *)(boot_param
+ 1)) + 256;
565 p
= grub_stpcpy (cmdline
, "BOOT_IMAGE");
566 for (i
= 0; i
< argc
; i
++)
569 p
= grub_stpcpy (p
, argv
[i
]);
573 boot_param
->command_line
= (grub_uint64_t
) cmdline
;
574 boot_param
->efi_systab
= (grub_uint64_t
) grub_efi_system_table
;
576 grub_errno
= GRUB_ERR_NONE
;
578 grub_loader_set (grub_linux_boot
, grub_linux_unload
, 0);
582 grub_file_close (file
);
584 if (grub_errno
!= GRUB_ERR_NONE
)
586 grub_efi_free_pages ((grub_efi_physical_address_t
) boot_param
,
588 grub_dl_unref (my_mod
);
594 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
595 int argc
, char *argv
[])
597 grub_file_t
*files
= 0;
604 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No filename specified");
610 grub_error (GRUB_ERR_BAD_ARGUMENT
, "You need to load the kernel first.");
614 files
= grub_zalloc (argc
* sizeof (files
[0]));
619 grub_dprintf ("linux", "Loading initrd\n");
620 for (i
= 0; i
< argc
; i
++)
622 grub_file_filter_disable_compression ();
623 files
[i
] = grub_file_open (argv
[i
]);
627 initrd_size
+= grub_file_size (files
[i
]);
628 grub_dprintf ("linux", "File %d: %s\n", i
, argv
[i
]);
631 initrd_pages
= (page_align (initrd_size
) >> 12);
632 initrd_mem
= grub_efi_allocate_pages (0, initrd_pages
);
635 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "cannot allocate pages");
639 grub_dprintf ("linux", "[addr=0x%lx, size=0x%lx]\n",
640 (grub_uint64_t
) initrd_mem
, initrd_size
);
643 for (i
= 0; i
< nfiles
; i
++)
645 grub_ssize_t cursize
= grub_file_size (files
[i
]);
646 if (grub_file_read (files
[i
], ptr
, cursize
) != cursize
)
649 grub_error (GRUB_ERR_FILE_READ_ERROR
, N_("premature end of file %s"),
656 for (i
= 0; i
< nfiles
; i
++)
657 grub_file_close (files
[i
]);
663 grub_cmd_payload (grub_command_t cmd
__attribute__ ((unused
)),
664 int argc
, char *argv
[])
666 grub_file_t file
= 0;
667 grub_ssize_t size
, len
= 0;
668 char *base
= 0, *cmdline
= 0, *p
;
669 struct ia64_boot_payload
*payload
= NULL
;
674 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module specified");
680 grub_error (GRUB_ERR_BAD_ARGUMENT
,
681 "You need to load the kernel first");
685 file
= grub_file_open (argv
[0]);
689 size
= grub_file_size (file
);
690 base
= grub_efi_allocate_pages (0, page_align (size
) >> 12);
694 grub_dprintf ("linux", "Payload %s [addr=%lx + %lx]\n",
695 argv
[0], (grub_uint64_t
)base
, size
);
697 if (grub_file_read (file
, base
, size
) != size
)
700 grub_error (GRUB_ERR_FILE_READ_ERROR
, N_("premature end of file %s"),
705 len
= sizeof (struct ia64_boot_payload
);
706 for (i
= 0; i
< argc
; i
++)
707 len
+= grub_strlen (argv
[i
]) + 1;
711 grub_error (GRUB_ERR_OUT_OF_RANGE
, "payload command line too long");
714 payload
= grub_efi_allocate_pages (0, 1);
718 p
= (char *)(payload
+ 1);
720 payload
->start
= (grub_uint64_t
)base
;
721 payload
->length
= size
;
722 payload
->cmdline
= (grub_uint64_t
)p
;
726 last_payload
->next
= (grub_uint64_t
)payload
;
729 last_payload
= payload
;
730 boot_param
->payloads_chain
= (grub_uint64_t
)payload
;
732 boot_param
->payloads_nbr
++;
734 /* Copy command line. */
735 for (i
= 0; i
< argc
; i
++)
737 p
= grub_stpcpy (p
, argv
[i
]);
741 /* Remove the space after the last word. */
747 grub_file_close (file
);
749 if (grub_errno
!= GRUB_ERR_NONE
)
758 grub_cmd_relocate (grub_command_t cmd
__attribute__ ((unused
)),
759 int argc
, char *argv
[])
761 static const char * const vals
[] = { "off", "on", "force"};
766 grub_printf (_("relocate is %s\n"), vals
[relocate
]);
767 return GRUB_ERR_NONE
;
771 if (kernel_mem
!= NULL
)
772 grub_printf (_("Warning: kernel already loaded!\n"));
773 for (i
= 0; i
< sizeof (vals
)/sizeof(vals
[0]); i
++)
774 if (grub_strcmp (argv
[0], vals
[i
]) == 0)
777 return GRUB_ERR_NONE
;
779 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "unknown relocate value");
783 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "accept 0 or 1 argument");
790 grub_cmd_fpswa (grub_command_t cmd
__attribute__ ((unused
)),
791 int argc
, char *argv
[] __attribute__((unused
)))
794 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "Arguments not expected");
797 grub_printf (_("No FPSWA loaded\n"));
799 grub_printf (_("FPSWA revision: %x\n"), fpswa
->revision
);
800 return GRUB_ERR_NONE
;
803 static grub_command_t cmd_linux
, cmd_initrd
, cmd_payload
, cmd_relocate
, cmd_fpswa
;
807 cmd_linux
= grub_register_command ("linux", grub_cmd_linux
,
808 N_("FILE [ARGS...]"), N_("Load Linux."));
810 cmd_initrd
= grub_register_command ("initrd", grub_cmd_initrd
,
811 N_("FILE"), N_("Load initrd."));
813 cmd_payload
= grub_register_command ("payload", grub_cmd_payload
,
814 N_("FILE [ARGS...]"),
815 N_("Load an additional file."));
817 cmd_relocate
= grub_register_command ("relocate", grub_cmd_relocate
,
819 N_("Set relocate feature."));
821 cmd_fpswa
= grub_register_command ("fpswa", grub_cmd_fpswa
,
822 "", N_("Display FPSWA version."));
829 grub_unregister_command (cmd_linux
);
830 grub_unregister_command (cmd_initrd
);
831 grub_unregister_command (cmd_payload
);
832 grub_unregister_command (cmd_relocate
);
833 grub_unregister_command (cmd_fpswa
);