efaa42ccdd2bf74321de20cf5033a80676cc7dc3
[grub.git] / grub-core / loader / ia64 / efi / linux.c
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 2008,2010  Free Software Foundation, Inc.
4  *
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.
9  *
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.
14  *
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/>.
17  */
18
19 #include <grub/loader.h>
20 #include <grub/file.h>
21 #include <grub/disk.h>
22 #include <grub/err.h>
23 #include <grub/misc.h>
24 #include <grub/types.h>
25 #include <grub/command.h>
26 #include <grub/dl.h>
27 #include <grub/mm.h>
28 #include <grub/cache.h>
29 #include <grub/kernel.h>
30 #include <grub/efi/api.h>
31 #include <grub/efi/efi.h>
32 #include <grub/elf.h>
33 #include <grub/i18n.h>
34 #include <grub/env.h>
35 #include <grub/linux.h>
36
37 GRUB_MOD_LICENSE ("GPLv3+");
38
39 #pragma GCC diagnostic ignored "-Wcast-align"
40
41 #define ALIGN_MIN (256*1024*1024)
42
43 #define GRUB_ELF_SEARCH 1024
44
45 #define BOOT_PARAM_SIZE 16384
46
47 struct ia64_boot_param
48 {
49   grub_uint64_t command_line;   /* physical address of command line. */
50   grub_uint64_t efi_systab;     /* physical address of EFI system table */
51   grub_uint64_t efi_memmap;     /* physical address of EFI memory map */
52   grub_uint64_t efi_memmap_size;        /* size of EFI memory map */
53   grub_uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */
54   grub_uint32_t efi_memdesc_version;    /* memory descriptor version */
55   struct
56   {
57     grub_uint16_t num_cols;     /* number of columns on console output dev */
58     grub_uint16_t num_rows;     /* number of rows on console output device */
59     grub_uint16_t orig_x;       /* cursor's x position */
60     grub_uint16_t orig_y;       /* cursor's y position */
61   } console_info;
62   grub_uint64_t fpswa;          /* physical address of the fpswa interface */
63   grub_uint64_t initrd_start;
64   grub_uint64_t initrd_size;
65 };
66
67 typedef struct
68 {
69   grub_uint32_t revision;
70   grub_uint32_t reserved;
71   void *fpswa;
72 } fpswa_interface_t;
73 static fpswa_interface_t *fpswa;
74
75 #define NEXT_MEMORY_DESCRIPTOR(desc, size)      \
76   ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
77
78 static grub_dl_t my_mod;
79
80 static int loaded;
81
82 /* Kernel base and size.  */
83 static void *kernel_mem;
84 static grub_efi_uintn_t kernel_pages;
85 static grub_uint64_t entry;
86
87 /* Initrd base and size.  */
88 static void *initrd_mem;
89 static grub_efi_uintn_t initrd_pages;
90 static grub_efi_uintn_t initrd_size;
91
92 static struct ia64_boot_param *boot_param;
93 static grub_efi_uintn_t boot_param_pages;
94
95 static inline grub_size_t
96 page_align (grub_size_t size)
97 {
98   return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
99 }
100
101 static void
102 query_fpswa (void)
103 {
104   grub_efi_handle_t fpswa_image;
105   grub_efi_boot_services_t *bs;
106   grub_efi_status_t status;
107   grub_efi_uintn_t size;
108   static const grub_efi_guid_t fpswa_protocol = 
109     { 0xc41b6531, 0x97b9, 0x11d3,
110       {0x9a, 0x29, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
111
112   if (fpswa != NULL)
113     return;
114
115   size = sizeof(grub_efi_handle_t);
116   
117   bs = grub_efi_system_table->boot_services;
118   status = bs->locate_handle (GRUB_EFI_BY_PROTOCOL,
119                               (void *) &fpswa_protocol,
120                               NULL, &size, &fpswa_image);
121   if (status != GRUB_EFI_SUCCESS)
122     {
123       grub_printf ("%s\n", _("Could not locate FPSWA driver"));
124       return;
125     }
126   status = bs->handle_protocol (fpswa_image,
127                                 (void *) &fpswa_protocol, (void *) &fpswa);
128   if (status != GRUB_EFI_SUCCESS)
129     {
130       grub_printf ("%s\n",
131                    _("FPSWA protocol wasn't able to find the interface"));
132       return;
133     } 
134 }
135
136 /* Find the optimal number of pages for the memory map. Is it better to
137    move this code to efi/mm.c?  */
138 static grub_efi_uintn_t
139 find_mmap_size (void)
140 {
141   static grub_efi_uintn_t mmap_size = 0;
142
143   if (mmap_size != 0)
144     return mmap_size;
145   
146   mmap_size = (1 << 12);
147   while (1)
148     {
149       int ret;
150       grub_efi_memory_descriptor_t *mmap;
151       grub_efi_uintn_t desc_size;
152       
153       mmap = grub_malloc (mmap_size);
154       if (! mmap)
155         return 0;
156
157       ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
158       grub_free (mmap);
159       
160       if (ret < 0)
161         {
162           grub_error (GRUB_ERR_IO, "cannot get memory map");
163           return 0;
164         }
165       else if (ret > 0)
166         break;
167
168       mmap_size += (1 << 12);
169     }
170
171   /* Increase the size a bit for safety, because GRUB allocates more on
172      later, and EFI itself may allocate more.  */
173   mmap_size += (1 << 12);
174
175   return page_align (mmap_size);
176 }
177
178 static void
179 free_pages (void)
180 {
181   if (kernel_mem)
182     {
183       grub_efi_free_pages ((grub_addr_t) kernel_mem, kernel_pages);
184       kernel_mem = 0;
185     }
186
187   if (initrd_mem)
188     {
189       grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages);
190       initrd_mem = 0;
191     }
192
193   if (boot_param)
194     {
195       /* Free bootparam.  */
196       grub_efi_free_pages ((grub_efi_physical_address_t) boot_param,
197                            boot_param_pages);
198       boot_param = 0;
199     }
200 }
201
202 static void *
203 allocate_pages (grub_uint64_t align, grub_uint64_t size_pages,
204                 grub_uint64_t nobase)
205 {
206   grub_uint64_t size;
207   grub_efi_uintn_t desc_size;
208   grub_efi_memory_descriptor_t *mmap, *mmap_end;
209   grub_efi_uintn_t mmap_size, tmp_mmap_size;
210   grub_efi_memory_descriptor_t *desc;
211   void *mem = NULL;
212
213   size = size_pages << 12;
214
215   mmap_size = find_mmap_size ();
216   if (!mmap_size)
217     return 0;
218
219     /* Read the memory map temporarily, to find free space.  */
220   mmap = grub_malloc (mmap_size);
221   if (! mmap)
222     return 0;
223
224   tmp_mmap_size = mmap_size;
225   if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
226     {
227       grub_error (GRUB_ERR_IO, "cannot get memory map");
228       goto fail;
229     }
230
231   mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
232   
233   /* First, find free pages for the real mode code
234      and the memory map buffer.  */
235   for (desc = mmap;
236        desc < mmap_end;
237        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
238     {
239       grub_uint64_t start, end;
240       grub_uint64_t aligned_start;
241
242       if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY)
243         continue;
244
245       start = desc->physical_start;
246       end = start + (desc->num_pages << 12);
247       /* Align is a power of 2.  */
248       aligned_start = (start + align - 1) & ~(align - 1);
249       if (aligned_start + size > end)
250         continue;
251       if (aligned_start == nobase)
252         aligned_start += align;
253       if (aligned_start + size > end)
254         continue;
255       mem = grub_efi_allocate_pages (aligned_start, size_pages);
256       if (! mem)
257         {
258           grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
259           goto fail;
260         }
261       break;
262     }
263
264   if (! mem)
265     {
266       grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
267       goto fail;
268     }
269
270   grub_free (mmap);
271   return mem;
272
273  fail:
274   grub_free (mmap);
275   free_pages ();
276   return 0;
277 }
278
279 static void
280 set_boot_param_console (void)
281 {
282   grub_efi_simple_text_output_interface_t *conout;
283   grub_efi_uintn_t cols, rows;
284   
285   conout = grub_efi_system_table->con_out;
286   if (conout->query_mode (conout, conout->mode->mode, &cols, &rows)
287       != GRUB_EFI_SUCCESS)
288     return;
289
290   grub_dprintf ("linux",
291                 "Console info: cols=%lu rows=%lu x=%u y=%u\n",
292                 cols, rows,
293                 conout->mode->cursor_column, conout->mode->cursor_row);
294   
295   boot_param->console_info.num_cols = cols;
296   boot_param->console_info.num_rows = rows;
297   boot_param->console_info.orig_x = conout->mode->cursor_column;
298   boot_param->console_info.orig_y = conout->mode->cursor_row;
299 }
300
301 static grub_err_t
302 grub_linux_boot (void)
303 {
304   grub_efi_uintn_t mmap_size;
305   grub_efi_uintn_t map_key;
306   grub_efi_uintn_t desc_size;
307   grub_efi_uint32_t desc_version;
308   grub_efi_memory_descriptor_t *mmap_buf;
309   grub_err_t err;
310
311   /* FPSWA.  */
312   query_fpswa ();
313   boot_param->fpswa = (grub_uint64_t)fpswa;
314
315   /* Initrd.  */
316   boot_param->initrd_start = (grub_uint64_t)initrd_mem;
317   boot_param->initrd_size = (grub_uint64_t)initrd_size;
318
319   set_boot_param_console ();
320
321   grub_dprintf ("linux", "Jump to %016lx\n", entry);
322
323   /* MDT.
324      Must be done after grub_machine_fini because map_key is used by
325      exit_boot_services.  */
326   mmap_size = find_mmap_size ();
327   if (! mmap_size)
328     return grub_errno;
329   mmap_buf = grub_efi_allocate_pages (0, page_align (mmap_size) >> 12);
330   if (! mmap_buf)
331     return grub_error (GRUB_ERR_IO, "cannot allocate memory map");
332   err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, &map_key,
333                                        &desc_size, &desc_version);
334   if (err)
335     return err;
336
337   boot_param->efi_memmap = (grub_uint64_t)mmap_buf;
338   boot_param->efi_memmap_size = mmap_size;
339   boot_param->efi_memdesc_size = desc_size;
340   boot_param->efi_memdesc_version = desc_version;
341
342   /* See you next boot.  */
343   asm volatile ("mov r28=%1; br.sptk.few %0" :: "b"(entry),"r"(boot_param));
344   
345   /* Never reach here.  */
346   return GRUB_ERR_NONE;
347 }
348
349 static grub_err_t
350 grub_linux_unload (void)
351 {
352   free_pages ();
353   grub_dl_unref (my_mod);
354   loaded = 0;
355   return GRUB_ERR_NONE;
356 }
357
358 static grub_err_t
359 grub_load_elf64 (grub_file_t file, void *buffer, const char *filename)
360 {
361   Elf64_Ehdr *ehdr = (Elf64_Ehdr *) buffer;
362   Elf64_Phdr *phdr;
363   int i;
364   grub_uint64_t low_addr;
365   grub_uint64_t high_addr;
366   grub_uint64_t align;
367   grub_uint64_t reloc_offset;
368   const char *relocate;
369
370   if (ehdr->e_ident[EI_MAG0] != ELFMAG0
371       || ehdr->e_ident[EI_MAG1] != ELFMAG1
372       || ehdr->e_ident[EI_MAG2] != ELFMAG2
373       || ehdr->e_ident[EI_MAG3] != ELFMAG3
374       || ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
375     return grub_error(GRUB_ERR_UNKNOWN_OS,
376                       N_("invalid arch-independent ELF magic"));
377
378   if (ehdr->e_ident[EI_CLASS] != ELFCLASS64
379       || ehdr->e_version != EV_CURRENT
380       || ehdr->e_machine != EM_IA_64)
381     return grub_error (GRUB_ERR_UNKNOWN_OS,
382                        N_("invalid arch-dependent ELF magic"));
383
384   if (ehdr->e_type != ET_EXEC)
385     return grub_error (GRUB_ERR_UNKNOWN_OS,
386                        N_("this ELF file is not of the right type"));
387
388   /* FIXME: Should we support program headers at strange locations?  */
389   if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > GRUB_ELF_SEARCH)
390     return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
391
392   entry = ehdr->e_entry;
393
394   /* Compute low, high and align addresses.  */
395   low_addr = ~0UL;
396   high_addr = 0;
397   align = 0;
398   for (i = 0; i < ehdr->e_phnum; i++)
399     {
400       phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff
401                              + i * ehdr->e_phentsize);
402       if (phdr->p_type == PT_LOAD)
403         {
404           if (phdr->p_paddr < low_addr)
405             low_addr = phdr->p_paddr;
406           if (phdr->p_paddr + phdr->p_memsz > high_addr)
407             high_addr = phdr->p_paddr + phdr->p_memsz;
408           if (phdr->p_align > align)
409             align = phdr->p_align;
410         }
411     }
412
413   if (align < ALIGN_MIN)
414     align = ALIGN_MIN;
415
416   if (high_addr == 0)
417     return grub_error (GRUB_ERR_BAD_OS, "no program entries");
418
419   kernel_pages = page_align (high_addr - low_addr) >> 12;
420
421   /* Undocumented on purpose.  */
422   relocate = grub_env_get ("linux_relocate");
423   if (!relocate || grub_strcmp (relocate, "force") != 0)
424     {
425       kernel_mem = grub_efi_allocate_pages (low_addr, kernel_pages);
426       reloc_offset = 0;
427     }
428   /* Try to relocate.  */
429   if (! kernel_mem && (!relocate || grub_strcmp (relocate, "off") != 0))
430     {
431       kernel_mem = allocate_pages (align, kernel_pages, low_addr);
432       if (kernel_mem)
433         {
434           reloc_offset = (grub_uint64_t)kernel_mem - low_addr;
435           grub_dprintf ("linux", "  Relocated at %p (offset=%016lx)\n",
436                         kernel_mem, reloc_offset);
437           entry += reloc_offset;
438         }
439     }
440   if (! kernel_mem)
441     return grub_error (GRUB_ERR_OUT_OF_MEMORY,
442                        "cannot allocate memory for OS");
443
444   /* Load every loadable segment in memory.  */
445   for (i = 0; i < ehdr->e_phnum; i++)
446     {
447       phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff
448                              + i * ehdr->e_phentsize);
449       if (phdr->p_type == PT_LOAD)
450         {
451           grub_dprintf ("linux", "  [paddr=%lx load=%lx memsz=%08lx "
452                         "off=%lx flags=%x]\n",
453                         phdr->p_paddr, phdr->p_paddr + reloc_offset,
454                         phdr->p_memsz, phdr->p_offset, phdr->p_flags);
455           
456           if (grub_file_seek (file, phdr->p_offset) == (grub_off_t)-1)
457             return grub_errno;
458
459           if (grub_file_read (file, (void *) (phdr->p_paddr + reloc_offset),
460                               phdr->p_filesz)
461               != (grub_ssize_t) phdr->p_filesz)
462             {
463               if (!grub_errno)
464                 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
465                             filename);
466               return grub_errno;
467             }
468           
469           if (phdr->p_filesz < phdr->p_memsz)
470             grub_memset
471               ((char *)(phdr->p_paddr + reloc_offset + phdr->p_filesz),
472                0, phdr->p_memsz - phdr->p_filesz);
473
474           /* Sync caches if necessary.  */
475           if (phdr->p_flags & PF_X)
476             grub_arch_sync_caches
477               ((void *)(phdr->p_paddr + reloc_offset), phdr->p_memsz);
478         }
479     }
480   loaded = 1;
481   return 0;
482 }
483
484 static grub_err_t
485 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
486                 int argc, char *argv[])
487 {
488   grub_file_t file = 0;
489   char buffer[GRUB_ELF_SEARCH];
490   char *cmdline, *p;
491   grub_ssize_t len;
492   int i;
493
494   grub_dl_ref (my_mod);
495
496   grub_loader_unset ();
497     
498   if (argc == 0)
499     {
500       grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
501       goto fail;
502     }
503
504   file = grub_file_open (argv[0]);
505   if (! file)
506     goto fail;
507
508   len = grub_file_read (file, buffer, sizeof (buffer));
509   if (len < (grub_ssize_t) sizeof (Elf64_Ehdr))
510     {
511       if (!grub_errno)
512         grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
513                     argv[0]);
514       goto fail;
515     }
516
517   grub_dprintf ("linux", "Loading linux: %s\n", argv[0]);
518
519   if (grub_load_elf64 (file, buffer, argv[0]))
520     goto fail;
521
522   len = sizeof("BOOT_IMAGE=") + 8;
523   for (i = 0; i < argc; i++)
524     len += grub_strlen (argv[i]) + 1;
525   len += sizeof (struct ia64_boot_param) + 512; /* Room for extensions.  */
526   boot_param_pages = page_align (len) >> 12;
527   boot_param = grub_efi_allocate_pages (0, boot_param_pages);
528   if (boot_param == 0)
529     {
530       grub_error (GRUB_ERR_OUT_OF_MEMORY,
531                   "cannot allocate memory for bootparams");
532       goto fail;
533     }
534
535   grub_memset (boot_param, 0, len);
536   cmdline = ((char *)(boot_param + 1)) + 256;
537
538   /* Build cmdline.  */
539   p = grub_stpcpy (cmdline, "BOOT_IMAGE");
540   for (i = 0; i < argc; i++)
541     {
542       *p++ = ' ';
543       p = grub_stpcpy (p, argv[i]);
544     }
545   cmdline[10] = '=';
546   
547   boot_param->command_line = (grub_uint64_t) cmdline;
548   boot_param->efi_systab = (grub_uint64_t) grub_efi_system_table;
549
550   grub_errno = GRUB_ERR_NONE;
551
552   grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
553
554  fail:
555   if (file)
556     grub_file_close (file);
557
558   if (grub_errno != GRUB_ERR_NONE)
559     {
560       grub_efi_free_pages ((grub_efi_physical_address_t) boot_param,
561                            boot_param_pages);
562       grub_dl_unref (my_mod);
563     }
564   return grub_errno;
565 }
566
567 static grub_err_t
568 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
569                  int argc, char *argv[])
570 {
571   struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
572
573   if (argc == 0)
574     {
575       grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
576       goto fail;
577     }
578   
579   if (! loaded)
580     {
581       grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
582       goto fail;
583     }
584
585   if (grub_initrd_init (argc, argv, &initrd_ctx))
586     goto fail;
587
588   initrd_size = grub_get_initrd_size (&initrd_ctx);
589   grub_dprintf ("linux", "Loading initrd\n");
590
591   initrd_pages = (page_align (initrd_size) >> 12);
592   initrd_mem = grub_efi_allocate_pages (0, initrd_pages);
593   if (! initrd_mem)
594     {
595       grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate pages");
596       goto fail;
597     }
598   
599   grub_dprintf ("linux", "[addr=0x%lx, size=0x%lx]\n",
600                 (grub_uint64_t) initrd_mem, initrd_size);
601
602   if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
603     goto fail;
604  fail:
605   grub_initrd_close (&initrd_ctx);
606   return grub_errno;
607 }
608
609 static grub_err_t
610 grub_cmd_fpswa (grub_command_t cmd __attribute__ ((unused)),
611                 int argc __attribute__((unused)),
612                 char *argv[] __attribute__((unused)))
613 {
614   query_fpswa ();
615   if (fpswa == NULL)
616     grub_puts_ (N_("No FPSWA found"));
617   else
618     grub_printf (_("FPSWA revision: %x\n"), fpswa->revision);
619   return GRUB_ERR_NONE;
620 }
621
622 static grub_command_t cmd_linux, cmd_initrd, cmd_fpswa;
623
624 GRUB_MOD_INIT(linux)
625 {
626   cmd_linux = grub_register_command ("linux", grub_cmd_linux,
627                                      N_("FILE [ARGS...]"), N_("Load Linux."));
628   
629   cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
630                                       N_("FILE"), N_("Load initrd."));
631
632   cmd_fpswa = grub_register_command ("fpswa", grub_cmd_fpswa,
633                                      "", N_("Display FPSWA version."));
634
635   my_mod = mod;
636 }
637
638 GRUB_MOD_FINI(linux)
639 {
640   grub_unregister_command (cmd_linux);
641   grub_unregister_command (cmd_initrd);
642   grub_unregister_command (cmd_fpswa);
643 }