]> git.proxmox.com Git - grub2.git/blame - grub-core/loader/i386/linux.c
* grub-core/disk/diskfilter.c (is_lv_readable): New argument "easily".
[grub2.git] / grub-core / loader / i386 / linux.c
CommitLineData
c9baafe7 1/*
2 * GRUB -- GRand Unified Bootloader
1a064917 3 * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
c9baafe7 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>
c8142599 20#include <grub/memory.h>
6c67de15 21#include <grub/normal.h>
c9baafe7 22#include <grub/file.h>
23#include <grub/disk.h>
24#include <grub/err.h>
25#include <grub/misc.h>
26#include <grub/types.h>
c9baafe7 27#include <grub/dl.h>
28#include <grub/mm.h>
29#include <grub/term.h>
30#include <grub/cpu/linux.h>
14aad807 31#include <grub/video.h>
d3539132 32#include <grub/video_fb.h>
b1b797cb 33#include <grub/command.h>
55b40bc6 34#include <grub/i386/relocator.h>
809bbfeb 35#include <grub/i18n.h>
25953e10 36#include <grub/lib/cmdline.h>
c9baafe7 37
e745cf0c
VS
38GRUB_MOD_LICENSE ("GPLv3+");
39
e31bb619
VS
40#ifdef GRUB_MACHINE_PCBIOS
41#include <grub/i386/pc/vesa_modes_table.h>
42#endif
43
c8142599
VS
44#ifdef GRUB_MACHINE_EFI
45#include <grub/efi/efi.h>
46#define HAS_VGA_TEXT 0
bf26bcc4 47#define DEFAULT_VIDEO_MODE "auto"
b1257f65 48#define ACCEPTS_PURE_TEXT 0
8071fb79
VS
49#elif defined (GRUB_MACHINE_IEEE1275)
50#include <grub/ieee1275/ieee1275.h>
51#define HAS_VGA_TEXT 0
52#define DEFAULT_VIDEO_MODE "text"
b1257f65 53#define ACCEPTS_PURE_TEXT 1
c8142599
VS
54#else
55#include <grub/i386/pc/vbe.h>
56#include <grub/i386/pc/console.h>
57#define HAS_VGA_TEXT 1
58#define DEFAULT_VIDEO_MODE "text"
b1257f65 59#define ACCEPTS_PURE_TEXT 1
c8142599
VS
60#endif
61
c9baafe7 62#define GRUB_LINUX_CL_OFFSET 0x1000
c9baafe7 63
64static grub_dl_t my_mod;
65
66static grub_size_t linux_mem_size;
67static int loaded;
68static void *real_mode_mem;
55b40bc6 69static grub_addr_t real_mode_target;
c9baafe7 70static void *prot_mode_mem;
55b40bc6 71static grub_addr_t prot_mode_target;
c9baafe7 72static void *initrd_mem;
55b40bc6 73static grub_addr_t initrd_mem_target;
c9baafe7 74static grub_uint32_t real_mode_pages;
75static grub_uint32_t prot_mode_pages;
76static grub_uint32_t initrd_pages;
55b40bc6 77static struct grub_relocator *relocator = NULL;
c8142599 78static void *efi_mmap_buf;
a06eb03a 79static grub_size_t maximal_cmdline_size;
d3fbca98
VS
80#ifdef GRUB_MACHINE_EFI
81static grub_efi_uintn_t efi_mmap_size;
82#else
83static const grub_size_t efi_mmap_size = 0;
84#endif
c9baafe7 85
55b40bc6
VS
86/* FIXME */
87#if 0
c9baafe7 88struct idt_descriptor
89{
90 grub_uint16_t limit;
91 void *base;
92} __attribute__ ((packed));
93
94static struct idt_descriptor idt_desc =
95 {
96 0,
97 0
98 };
55b40bc6 99#endif
c9baafe7 100
101static inline grub_size_t
102page_align (grub_size_t size)
103{
104 return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
105}
106
c8142599
VS
107#ifdef GRUB_MACHINE_EFI
108/* Find the optimal number of pages for the memory map. Is it better to
109 move this code to efi/mm.c? */
110static grub_efi_uintn_t
111find_efi_mmap_size (void)
112{
113 static grub_efi_uintn_t mmap_size = 0;
114
115 if (mmap_size != 0)
116 return mmap_size;
117
118 mmap_size = (1 << 12);
119 while (1)
120 {
121 int ret;
122 grub_efi_memory_descriptor_t *mmap;
123 grub_efi_uintn_t desc_size;
124
125 mmap = grub_malloc (mmap_size);
126 if (! mmap)
127 return 0;
128
129 ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
130 grub_free (mmap);
131
132 if (ret < 0)
119d11c8
VS
133 {
134 grub_error (GRUB_ERR_IO, "cannot get memory map");
135 return 0;
136 }
c8142599
VS
137 else if (ret > 0)
138 break;
139
140 mmap_size += (1 << 12);
141 }
142
143 /* Increase the size a bit for safety, because GRUB allocates more on
144 later, and EFI itself may allocate more. */
145 mmap_size += (1 << 12);
146
be1a7ce0
CW
147 mmap_size = page_align (mmap_size);
148 return mmap_size;
c8142599
VS
149}
150
151#endif
152
c9baafe7 153/* Find the optimal number of pages for the memory map. */
154static grub_size_t
155find_mmap_size (void)
156{
157 grub_size_t count = 0, mmap_size;
158
df3df23d
VS
159 auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
160 grub_memory_type_t);
c9baafe7 161 int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)),
162 grub_uint64_t size __attribute__ ((unused)),
df3df23d 163 grub_memory_type_t type __attribute__ ((unused)))
c9baafe7 164 {
165 count++;
166 return 0;
167 }
b39f9d20 168
09d842b9 169 grub_mmap_iterate (hook);
b39f9d20 170
c9baafe7 171 mmap_size = count * sizeof (struct grub_e820_mmap);
172
173 /* Increase the size a bit for safety, because GRUB allocates more on
174 later. */
175 mmap_size += (1 << 12);
b39f9d20 176
c9baafe7 177 return page_align (mmap_size);
178}
179
180static void
181free_pages (void)
182{
55b40bc6
VS
183 grub_relocator_unload (relocator);
184 relocator = NULL;
c9baafe7 185 real_mode_mem = prot_mode_mem = initrd_mem = 0;
55b40bc6 186 real_mode_target = prot_mode_target = initrd_mem_target = 0;
c9baafe7 187}
188
189/* Allocate pages for the real mode code and the protected mode code
190 for linux as well as a memory map buffer. */
55b40bc6 191static grub_err_t
9be4c45d
VS
192allocate_pages (grub_size_t prot_size, grub_size_t *align,
193 grub_size_t min_align, int relocatable,
194 grub_uint64_t prefered_address)
c9baafe7 195{
196 grub_size_t real_size, mmap_size;
55b40bc6 197 grub_err_t err;
c9baafe7 198
199 /* Make sure that each size is aligned to a page boundary. */
a06eb03a 200 real_size = GRUB_LINUX_CL_OFFSET + maximal_cmdline_size;
c9baafe7 201 prot_size = page_align (prot_size);
202 mmap_size = find_mmap_size ();
203
c8142599
VS
204#ifdef GRUB_MACHINE_EFI
205 efi_mmap_size = find_efi_mmap_size ();
119d11c8
VS
206 if (efi_mmap_size == 0)
207 return grub_errno;
c8142599
VS
208#endif
209
c9baafe7 210 grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
211 (unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
b39f9d20 212
c9baafe7 213 /* Calculate the number of pages; Combine the real mode code with
214 the memory map buffer for simplicity. */
c8142599 215 real_mode_pages = ((real_size + mmap_size + efi_mmap_size) >> 12);
c9baafe7 216 prot_mode_pages = (prot_size >> 12);
b39f9d20 217
c9baafe7 218 /* Initialize the memory pointers with NULL for convenience. */
1007d1f5 219 free_pages ();
b39f9d20 220
55b40bc6
VS
221 relocator = grub_relocator_new ();
222 if (!relocator)
223 {
224 err = grub_errno;
225 goto fail;
226 }
227
aa9f3bff 228 /* FIXME: Should request low memory from the heap when this feature is
229 implemented. */
2f2a3442 230
df3df23d
VS
231 auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
232 grub_memory_type_t);
233 int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
234 grub_memory_type_t type)
10fc3eb9 235 {
236 /* We must put real mode code in the traditional space. */
237
df3df23d 238 if (type == GRUB_MEMORY_AVAILABLE
10fc3eb9 239 && addr <= 0x90000)
240 {
241 if (addr < 0x10000)
242 {
243 size += addr - 0x10000;
244 addr = 0x10000;
245 }
246
247 if (addr + size > 0x90000)
248 size = 0x90000 - addr;
249
c8142599 250 if (real_size + mmap_size + efi_mmap_size > size)
10fc3eb9 251 return 0;
252
c8142599 253 real_mode_target = ((addr + size) - (real_size + mmap_size + efi_mmap_size));
10fc3eb9 254 return 1;
255 }
256
257 return 0;
258 }
9be4c45d
VS
259#ifdef GRUB_MACHINE_EFI
260 grub_efi_mmap_iterate (hook, 1);
261#else
09d842b9 262 grub_mmap_iterate (hook);
9be4c45d 263#endif
55b40bc6 264 if (! real_mode_target)
c9baafe7 265 {
55b40bc6 266 err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
c9baafe7 267 goto fail;
268 }
269
4b2ec20b
VS
270 {
271 grub_relocator_chunk_t ch;
272 err = grub_relocator_alloc_chunk_addr (relocator, &ch,
273 real_mode_target,
274 (real_size + mmap_size
275 + efi_mmap_size));
276 if (err)
277 goto fail;
278 real_mode_mem = get_virtual_current_address (ch);
279 }
c8142599 280 efi_mmap_buf = (grub_uint8_t *) real_mode_mem + real_size + mmap_size;
55b40bc6 281
4b2ec20b
VS
282 {
283 grub_relocator_chunk_t ch;
9be4c45d
VS
284 if (relocatable)
285 {
286 err = grub_relocator_alloc_chunk_align (relocator, &ch,
287 prefered_address,
288 prefered_address,
289 prot_size, 1,
290 GRUB_RELOCATOR_PREFERENCE_LOW,
291 1);
292 for (; err && *align >= min_align; (*align)--)
293 {
294 grub_errno = GRUB_ERR_NONE;
295 err = grub_relocator_alloc_chunk_align (relocator, &ch,
296 0x1000000, 0xffffffff,
297 prot_size, 1 << *align,
298 GRUB_RELOCATOR_PREFERENCE_LOW,
299 1);
300 }
301 if (err)
302 goto fail;
303 }
304 else
305 err = grub_relocator_alloc_chunk_addr (relocator, &ch,
306 prefered_address,
307 prot_size);
4b2ec20b
VS
308 if (err)
309 goto fail;
310 prot_mode_mem = get_virtual_current_address (ch);
9be4c45d 311 prot_mode_target = get_physical_target_address (ch);
4b2ec20b 312 }
c9baafe7 313
314 grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
315 "prot_mode_mem = %lx, prot_mode_pages = %x\n",
316 (unsigned long) real_mode_mem, (unsigned) real_mode_pages,
317 (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
318
55b40bc6 319 return GRUB_ERR_NONE;
c9baafe7 320
321 fail:
322 free_pages ();
55b40bc6 323 return err;
c9baafe7 324}
325
119d11c8 326static grub_err_t
c9baafe7 327grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
328 grub_uint64_t start, grub_uint64_t size,
329 grub_uint32_t type)
330{
331 int n = *e820_num;
332
333 if (n >= GRUB_E820_MAX_ENTRY)
119d11c8
VS
334 {
335 return grub_error (GRUB_ERR_OUT_OF_RANGE,
336 "Too many e820 memory map entries");
337 }
c9baafe7 338
339 if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
340 (e820_map[n - 1].type == type))
341 e820_map[n - 1].size += size;
342 else
343 {
344 e820_map[n].addr = start;
345 e820_map[n].size = size;
346 e820_map[n].type = type;
347 (*e820_num)++;
348 }
119d11c8 349 return GRUB_ERR_NONE;
c9baafe7 350}
351
c8142599 352static grub_err_t
14aad807 353grub_linux_setup_video (struct linux_kernel_params *params)
354{
355 struct grub_video_mode_info mode_info;
d3539132 356 void *framebuffer;
c8142599 357 grub_err_t err;
3395fe52 358 grub_video_driver_id_t driver_id;
d35d0d37 359 const char *gfxlfbvar = grub_env_get ("gfxpayloadforcelfb");
3395fe52
VS
360
361 driver_id = grub_video_get_driver_id ();
362
363 if (driver_id == GRUB_VIDEO_DRIVER_NONE)
364 return 1;
c8142599 365
c8142599 366 err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
14aad807 367
c8142599 368 if (err)
74e31b5c
VS
369 {
370 grub_errno = GRUB_ERR_NONE;
371 return 1;
372 }
14aad807 373
374 params->lfb_width = mode_info.width;
375 params->lfb_height = mode_info.height;
376 params->lfb_depth = mode_info.bpp;
377 params->lfb_line_len = mode_info.pitch;
378
d3539132 379 params->lfb_base = (grub_size_t) framebuffer;
7cae4377 380 params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height, 65536);
14aad807 381
7a6bf9f2 382 params->red_mask_size = mode_info.red_mask_size;
383 params->red_field_pos = mode_info.red_field_pos;
384 params->green_mask_size = mode_info.green_mask_size;
385 params->green_field_pos = mode_info.green_field_pos;
386 params->blue_mask_size = mode_info.blue_mask_size;
387 params->blue_field_pos = mode_info.blue_field_pos;
388 params->reserved_mask_size = mode_info.reserved_mask_size;
389 params->reserved_field_pos = mode_info.reserved_field_pos;
14aad807 390
3395fe52
VS
391 if (gfxlfbvar && (gfxlfbvar[0] == '1' || gfxlfbvar[0] == 'y'))
392 params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
393 else
394 {
395 switch (driver_id)
396 {
397 case GRUB_VIDEO_DRIVER_VBE:
398 params->lfb_size >>= 16;
399 params->have_vga = GRUB_VIDEO_LINUX_TYPE_VESA;
400 break;
401
402 case GRUB_VIDEO_DRIVER_EFI_UGA:
403 case GRUB_VIDEO_DRIVER_EFI_GOP:
404 params->have_vga = GRUB_VIDEO_LINUX_TYPE_EFIFB;
405 break;
406
407 /* FIXME: check if better id is available. */
408 case GRUB_VIDEO_DRIVER_SM712:
67b1e5c9 409 case GRUB_VIDEO_DRIVER_SIS315PRO:
3395fe52
VS
410 case GRUB_VIDEO_DRIVER_VGA:
411 case GRUB_VIDEO_DRIVER_CIRRUS:
412 case GRUB_VIDEO_DRIVER_BOCHS:
fe8d4a7b 413 case GRUB_VIDEO_DRIVER_RADEON_FULOONG2E:
3395fe52
VS
414 /* Make gcc happy. */
415 case GRUB_VIDEO_DRIVER_SDL:
416 case GRUB_VIDEO_DRIVER_NONE:
417 params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
418 break;
419 }
420 }
61229557 421
422#ifdef GRUB_MACHINE_PCBIOS
423 /* VESA packed modes may come with zeroed mask sizes, which need
424 to be set here according to DAC Palette width. If we don't,
425 this results in Linux displaying a black screen. */
3395fe52 426 if (driver_id == GRUB_VIDEO_DRIVER_VBE && mode_info.bpp <= 8)
61229557 427 {
428 struct grub_vbe_info_block controller_info;
429 int status;
430 int width = 8;
431
432 status = grub_vbe_bios_get_controller_info (&controller_info);
433
434 if (status == GRUB_VBE_STATUS_OK &&
435 (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
436 status = grub_vbe_bios_set_dac_palette_width (&width);
437
438 if (status != GRUB_VBE_STATUS_OK)
439 /* 6 is default after mode reset. */
440 width = 6;
441
442 params->red_mask_size = params->green_mask_size
443 = params->blue_mask_size = width;
444 params->reserved_mask_size = 0;
445 }
446#endif
447
c8142599 448 return GRUB_ERR_NONE;
14aad807 449}
450
c9baafe7 451static grub_err_t
a9368fd3 452grub_linux_boot (void)
c9baafe7 453{
454 struct linux_kernel_params *params;
455 int e820_num;
ad760f81 456 grub_err_t err = 0;
d35d0d37
VS
457 const char *modevar;
458 char *tmp;
55b40bc6 459 struct grub_relocator32_state state;
3eb5ed4e 460
c9baafe7 461 params = real_mode_mem;
462
8071fb79
VS
463#ifdef GRUB_MACHINE_IEEE1275
464 {
d35d0d37 465 const char *bootpath;
8071fb79
VS
466 grub_ssize_t len;
467
468 bootpath = grub_env_get ("root");
469 if (bootpath)
470 grub_ieee1275_set_property (grub_ieee1275_chosen,
471 "bootpath", bootpath,
472 grub_strlen (bootpath) + 1,
473 &len);
474 }
475#endif
476
55b40bc6
VS
477 grub_dprintf ("linux", "code32_start = %x\n",
478 (unsigned) params->code32_start);
c9baafe7 479
df3df23d
VS
480 auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
481 grub_memory_type_t);
482 int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
483 grub_memory_type_t type)
c9baafe7 484 {
119d11c8 485 grub_uint32_t e820_type;
c9baafe7 486 switch (type)
487 {
df3df23d 488 case GRUB_MEMORY_AVAILABLE:
119d11c8 489 e820_type = GRUB_E820_RAM;
c9baafe7 490 break;
491
df3df23d 492 case GRUB_MEMORY_ACPI:
119d11c8 493 e820_type = GRUB_E820_ACPI;
09d842b9 494 break;
09d842b9 495
df3df23d 496 case GRUB_MEMORY_NVS:
119d11c8 497 e820_type = GRUB_E820_NVS;
09d842b9 498 break;
09d842b9 499
18a38098 500 case GRUB_MEMORY_BADRAM:
119d11c8 501 e820_type = GRUB_E820_BADRAM;
09d842b9 502 break;
09d842b9 503
c9baafe7 504 default:
119d11c8 505 e820_type = GRUB_E820_RESERVED;
c9baafe7 506 }
119d11c8
VS
507 if (grub_e820_add_region (params->e820_map, &e820_num,
508 addr, size, e820_type))
509 return 1;
510
c9baafe7 511 return 0;
512 }
513
514 e820_num = 0;
119d11c8
VS
515 if (grub_mmap_iterate (hook))
516 return grub_errno;
c9baafe7 517 params->mmap_size = e820_num;
518
d3539132 519 modevar = grub_env_get ("gfxpayload");
520
521 /* Now all graphical modes are acceptable.
522 May change in future if we have modes without framebuffer. */
523 if (modevar && *modevar != 0)
524 {
3c83bc50 525 tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
d3539132 526 if (! tmp)
527 return grub_errno;
b1257f65 528#if ACCEPTS_PURE_TEXT
df48e9e1 529 err = grub_video_set_mode (tmp, 0, 0);
b1257f65
VS
530#else
531 err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
532#endif
d3539132 533 grub_free (tmp);
534 }
d3539132 535 else
b1257f65
VS
536 {
537#if ACCEPTS_PURE_TEXT
538 err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
539#else
540 err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
541 GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
542#endif
543 }
d3539132 544 if (err)
545 {
546 grub_print_error ();
10f0117b 547 grub_puts_ (N_("Booting in blind mode"));
d3539132 548 grub_errno = GRUB_ERR_NONE;
549 }
550
3395fe52 551 if (grub_linux_setup_video (params))
d3539132 552 {
8b0800f6 553#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
8040619d 554 params->have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT;
c8142599 555 params->video_mode = 0x3;
c8142599
VS
556#else
557 params->have_vga = 0;
558 params->video_mode = 0;
559 params->video_width = 0;
560 params->video_height = 0;
561#endif
d3539132 562 }
563
b09db61d 564 /* Initialize these last, because terminal position could be affected by printfs above. */
8071fb79 565#ifndef GRUB_MACHINE_IEEE1275
8040619d 566 if (params->have_vga == GRUB_VIDEO_LINUX_TYPE_TEXT)
8071fb79 567#endif
b09db61d 568 {
288031b3
VS
569 grub_term_output_t term;
570 int found = 0;
571 FOR_ACTIVE_TERM_OUTPUTS(term)
5a1ed29e 572 if (grub_strcmp (term->name, "vga_text") == 0
8071fb79
VS
573 || grub_strcmp (term->name, "console") == 0
574 || grub_strcmp (term->name, "ofconsole") == 0)
288031b3
VS
575 {
576 grub_uint16_t pos = grub_term_getxy (term);
577 params->video_cursor_x = pos >> 8;
578 params->video_cursor_y = pos & 0xff;
8071fb79
VS
579 params->video_width = grub_term_width (term);
580 params->video_height = grub_term_height (term);
288031b3 581 found = 1;
5a1ed29e 582 break;
288031b3
VS
583 }
584 if (!found)
585 {
586 params->video_cursor_x = 0;
587 params->video_cursor_y = 0;
8071fb79
VS
588 params->video_width = 80;
589 params->video_height = 25;
288031b3 590 }
b09db61d 591 }
592
8071fb79
VS
593#ifdef GRUB_MACHINE_IEEE1275
594 {
595 params->ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
596 params->ofw_num_items = 1;
597 params->ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
598 params->ofw_idt = 0;
599 }
600#endif
601
c8142599
VS
602#ifdef GRUB_MACHINE_EFI
603 {
91b58e6b 604 grub_efi_uintn_t efi_desc_size;
3c62402d 605 grub_size_t efi_mmap_target;
c8142599 606 grub_efi_uint32_t efi_desc_version;
91b58e6b
VS
607 err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL,
608 &efi_desc_size, &efi_desc_version);
609 if (err)
610 return err;
c8142599 611
c8142599 612 /* Note that no boot services are available from here. */
3c62402d
VS
613 efi_mmap_target = real_mode_target
614 + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem);
c8142599
VS
615 /* Pass EFI parameters. */
616 if (grub_le_to_cpu16 (params->version) >= 0x0206)
617 {
618 params->v0206.efi_mem_desc_size = efi_desc_size;
619 params->v0206.efi_mem_desc_version = efi_desc_version;
3c62402d 620 params->v0206.efi_mmap = efi_mmap_target;
c8142599
VS
621 params->v0206.efi_mmap_size = efi_mmap_size;
622#ifdef __x86_64__
3c62402d 623 params->v0206.efi_mmap_hi = (efi_mmap_target >> 32);
c8142599
VS
624#endif
625 }
626 else if (grub_le_to_cpu16 (params->version) >= 0x0204)
627 {
628 params->v0204.efi_mem_desc_size = efi_desc_size;
629 params->v0204.efi_mem_desc_version = efi_desc_version;
3c62402d 630 params->v0204.efi_mmap = efi_mmap_target;
c8142599
VS
631 params->v0204.efi_mmap_size = efi_mmap_size;
632 }
633 }
634#endif
635
55b40bc6
VS
636 /* FIXME. */
637 /* asm volatile ("lidt %0" : : "m" (idt_desc)); */
9056cbf3 638 state.ebp = state.edi = state.ebx = 0;
55b40bc6 639 state.esi = real_mode_target;
8071fb79 640 state.esp = real_mode_target;
55b40bc6 641 state.eip = params->code32_start;
9be4c45d 642 return grub_relocator32_boot (relocator, state, 0);
c9baafe7 643}
644
645static grub_err_t
646grub_linux_unload (void)
647{
c9baafe7 648 grub_dl_unref (my_mod);
649 loaded = 0;
650 return GRUB_ERR_NONE;
651}
652
b1b797cb 653static grub_err_t
654grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
655 int argc, char *argv[])
c9baafe7 656{
657 grub_file_t file = 0;
658 struct linux_kernel_header lh;
659 struct linux_kernel_params *params;
660 grub_uint8_t setup_sects;
661 grub_size_t real_size, prot_size;
662 grub_ssize_t len;
663 int i;
9be4c45d
VS
664 grub_size_t align, min_align;
665 int relocatable;
666 grub_uint64_t preffered_address = GRUB_LINUX_BZIMAGE_ADDR;
c9baafe7 667
668 grub_dl_ref (my_mod);
b39f9d20 669
c9baafe7 670 if (argc == 0)
671 {
9c4b5c13 672 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
c9baafe7 673 goto fail;
674 }
675
676 file = grub_file_open (argv[0]);
677 if (! file)
87a4623b 678 goto fail;
c9baafe7 679
5c5215d5 680 if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
c9baafe7 681 {
7a45a539 682 if (!grub_errno)
9c4b5c13 683 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
7a45a539 684 argv[0]);
c9baafe7 685 goto fail;
686 }
687
688 if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
689 {
690 grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
691 goto fail;
692 }
693
694 if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
695 {
696 grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
697 goto fail;
698 }
699
92f33540 700 if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
c9baafe7 701 {
92f33540 702 grub_error (GRUB_ERR_BAD_OS, "zImage doesn't support 32-bit boot"
703#ifdef GRUB_MACHINE_PCBIOS
704 " (try with `linux16')"
705#endif
706 );
c9baafe7 707 goto fail;
708 }
709
92f33540 710 /* FIXME: 2.03 is not always good enough (Linux 2.4 can be 2.03 and
711 still not support 32-bit boot. */
712 if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
713 || grub_le_to_cpu16 (lh.version) < 0x0203)
c9baafe7 714 {
92f33540 715 grub_error (GRUB_ERR_BAD_OS, "version too old for 32-bit boot"
716#ifdef GRUB_MACHINE_PCBIOS
717 " (try with `linux16')"
718#endif
719 );
c9baafe7 720 goto fail;
721 }
722
a06eb03a
VS
723 if (grub_le_to_cpu16 (lh.version) >= 0x0206)
724 maximal_cmdline_size = grub_le_to_cpu32 (lh.cmdline_size) + 1;
725 else
726 maximal_cmdline_size = 256;
727
728 if (maximal_cmdline_size < 128)
729 maximal_cmdline_size = 128;
730
c9baafe7 731 setup_sects = lh.setup_sects;
b39f9d20 732
c9baafe7 733 /* If SETUP_SECTS is not set, set it to the default (4). */
734 if (! setup_sects)
735 setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
736
737 real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
738 prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
b39f9d20 739
9be4c45d
VS
740 if (grub_le_to_cpu16 (lh.version) >= 0x205
741 && lh.kernel_alignment != 0
742 && ((lh.kernel_alignment - 1) & lh.kernel_alignment) == 0)
743 {
744 for (align = 0; align < 32; align++)
745 if (grub_le_to_cpu32 (lh.kernel_alignment) & (1 << align))
746 break;
747 relocatable = grub_le_to_cpu32 (lh.relocatable);
748 }
749 else
750 {
751 align = 0;
752 relocatable = 0;
753 }
754
755 if (grub_le_to_cpu16 (lh.version) >= 0x020a)
756 {
757 min_align = lh.min_alignment;
758 prot_size = grub_le_to_cpu32 (lh.init_size);
759 preffered_address = grub_le_to_cpu64 (lh.pref_address);
760 }
761 else
762 {
763 min_align = 0;
764 prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
765 preffered_address = grub_le_to_cpu32 (lh.code32_start);
766 }
767
768 if (allocate_pages (prot_size, &align,
769 min_align, relocatable,
770 preffered_address))
c9baafe7 771 goto fail;
b39f9d20 772
c9baafe7 773 params = (struct linux_kernel_params *) real_mode_mem;
a06eb03a 774 grub_memset (params, 0, GRUB_LINUX_CL_OFFSET + maximal_cmdline_size);
c9baafe7 775 grub_memcpy (&params->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
776
777 params->ps_mouse = params->padding10 = 0;
778
779 len = 0x400 - sizeof (lh);
780 if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len)
781 {
7a45a539 782 if (!grub_errno)
9c4b5c13 783 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
7a45a539 784 argv[0]);
c9baafe7 785 goto fail;
786 }
787
7f8b0fd7 788 params->type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
c9baafe7 789
b09db61d 790 /* These two are used (instead of cmd_line_ptr) by older versions of Linux,
791 and otherwise ignored. */
c9baafe7 792 params->cl_magic = GRUB_LINUX_CL_MAGIC;
793 params->cl_offset = 0x1000;
b09db61d 794
55b40bc6 795 params->cmd_line_ptr = real_mode_target + 0x1000;
c9baafe7 796 params->ramdisk_image = 0;
797 params->ramdisk_size = 0;
798
799 params->heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
800 params->loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
801
802 /* These are not needed to be precise, because Linux uses these values
803 only to raise an error when the decompression code cannot find good
804 space. */
805 params->ext_mem = ((32 * 0x100000) >> 10);
806 params->alt_mem = ((32 * 0x100000) >> 10);
b39f9d20 807
b09db61d 808 /* Ignored by Linux. */
809 params->video_page = 0;
810
b09db61d 811 /* Only used when `video_mode == 0x7', otherwise ignored. */
c9baafe7 812 params->video_ega_bx = 0;
b09db61d 813
c9baafe7 814 params->font_size = 16; /* XXX */
815
c8142599
VS
816#ifdef GRUB_MACHINE_EFI
817 if (grub_le_to_cpu16 (params->version) >= 0x0206)
818 {
819 params->v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
820 params->v0206.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
821#ifdef __x86_64__
822 params->v0206.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
823#endif
824 }
825 else if (grub_le_to_cpu16 (params->version) >= 0x0204)
826 {
827 params->v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
828 params->v0204.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
829 }
830#endif
831
c9baafe7 832 /* The other parameters are filled when booting. */
833
834 grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
835
1a064917
RM
836 grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
837 (unsigned) real_size, (unsigned) prot_size);
c9baafe7 838
1007d1f5 839 /* Look for memory size and video mode specified on the command line. */
c9baafe7 840 linux_mem_size = 0;
c9baafe7 841 for (i = 1; i < argc; i++)
b01f0548 842#ifdef GRUB_MACHINE_PCBIOS
9c323f09 843 if (grub_memcmp (argv[i], "vga=", 4) == 0)
844 {
845 /* Video mode selection support. */
846 char *val = argv[i] + 4;
3eb5ed4e 847 unsigned vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
e31bb619 848 struct grub_vesa_mode_table_entry *linux_mode;
3eb5ed4e 849 grub_err_t err;
850 char *buf;
9c323f09 851
2b36fbf4
VS
852 grub_dl_load ("vbe");
853
9c323f09 854 if (grub_strcmp (val, "normal") == 0)
855 vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
856 else if (grub_strcmp (val, "ext") == 0)
857 vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
1007d1f5 858 else if (grub_strcmp (val, "ask") == 0)
cba416eb 859 {
10f0117b 860 grub_puts_ (N_("Legacy `ask' parameter no longer supported."));
6c67de15 861
862 /* We usually would never do this in a loader, but "vga=ask" means user
863 requested interaction, so it can't hurt to request keyboard input. */
864 grub_wait_after_message ();
865
cba416eb 866 goto fail;
867 }
9c323f09 868 else
869 vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
870
112972a9 871 switch (vid_mode)
872 {
873 case 0:
3eb5ed4e 874 case GRUB_LINUX_VID_MODE_NORMAL:
875 grub_env_set ("gfxpayload", "text");
10f0117b 876 grub_printf_ (N_("%s is deprecated. "
67093bc0
VS
877 "Use set gfxpayload=%s before "
878 "linux command instead.\n"), "text",
879 argv[i]);
112972a9 880 break;
3eb5ed4e 881
112972a9 882 case 1:
3eb5ed4e 883 case GRUB_LINUX_VID_MODE_EXTENDED:
884 /* FIXME: support 80x50 text. */
885 grub_env_set ("gfxpayload", "text");
10f0117b 886 grub_printf_ (N_("%s is deprecated. "
67093bc0
VS
887 "Use set gfxpayload=%s before "
888 "linux command instead.\n"), "text",
889 argv[i]);
112972a9 890 break;
891 default:
892 /* Ignore invalid values. */
e31bb619
VS
893 if (vid_mode < GRUB_VESA_MODE_TABLE_START ||
894 vid_mode > GRUB_VESA_MODE_TABLE_END)
3eb5ed4e 895 {
896 grub_env_set ("gfxpayload", "text");
8822a8a0 897 grub_printf_ (N_("%s is deprecated. VGA mode %d isn't recognized. "
10f0117b
VS
898 "Use set gfxpayload=WIDTHxHEIGHT[xDEPTH] "
899 "before linux command instead.\n"),
b39f9d20 900 argv[i], vid_mode);
3eb5ed4e 901 break;
902 }
903
e31bb619
VS
904 linux_mode = &grub_vesa_mode_table[vid_mode
905 - GRUB_VESA_MODE_TABLE_START];
b39f9d20 906
61eb45ee 907 buf = grub_xasprintf ("%ux%ux%u,%ux%u",
e31bb619 908 linux_mode->width, linux_mode->height,
8b442f3f 909 linux_mode->depth,
e31bb619 910 linux_mode->width, linux_mode->height);
8b442f3f
VS
911 if (! buf)
912 goto fail;
913
10f0117b
VS
914 grub_printf_ (N_("%s is deprecated. "
915 "Use set gfxpayload=%s before "
916 "linux command instead.\n"),
b39f9d20 917 argv[i], buf);
3eb5ed4e 918 err = grub_env_set ("gfxpayload", buf);
919 grub_free (buf);
920 if (err)
921 goto fail;
112972a9 922 }
9c323f09 923 }
7c1d00cd 924 else
b01f0548 925#endif /* GRUB_MACHINE_PCBIOS */
7c1d00cd 926 if (grub_memcmp (argv[i], "mem=", 4) == 0)
c9baafe7 927 {
928 char *val = argv[i] + 4;
b39f9d20 929
c9baafe7 930 linux_mem_size = grub_strtoul (val, &val, 0);
b39f9d20 931
c9baafe7 932 if (grub_errno)
933 {
934 grub_errno = GRUB_ERR_NONE;
935 linux_mem_size = 0;
936 }
937 else
938 {
939 int shift = 0;
b39f9d20 940
c9baafe7 941 switch (grub_tolower (val[0]))
942 {
943 case 'g':
944 shift += 10;
945 case 'm':
946 shift += 10;
947 case 'k':
948 shift += 10;
949 default:
950 break;
951 }
952
953 /* Check an overflow. */
954 if (linux_mem_size > (~0UL >> shift))
955 linux_mem_size = 0;
956 else
957 linux_mem_size <<= shift;
958 }
959 }
28333ad0 960 else if (grub_memcmp (argv[i], "quiet", sizeof ("quiet") - 1) == 0)
961 {
962 params->loadflags |= GRUB_LINUX_FLAG_QUIET;
963 }
964
25953e10
SJ
965 /* Create kernel command line. */
966 grub_memcpy ((char *)real_mode_mem + GRUB_LINUX_CL_OFFSET, LINUX_IMAGE,
967 sizeof (LINUX_IMAGE));
968 grub_create_loader_cmdline (argc, argv,
969 (char *)real_mode_mem + GRUB_LINUX_CL_OFFSET
970 + sizeof (LINUX_IMAGE) - 1,
05caa461 971 maximal_cmdline_size
25953e10 972 - (sizeof (LINUX_IMAGE) - 1));
c9baafe7 973
974 len = prot_size;
7a45a539 975 if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno)
9c4b5c13 976 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
7a45a539 977 argv[0]);
c9baafe7 978
979 if (grub_errno == GRUB_ERR_NONE)
980 {
a9368fd3 981 grub_loader_set (grub_linux_boot, grub_linux_unload,
14aad807 982 0 /* set noreturn=0 in order to avoid grub_console_fini() */);
c9baafe7 983 loaded = 1;
984 }
985
986 fail:
b39f9d20 987
c9baafe7 988 if (file)
989 grub_file_close (file);
990
991 if (grub_errno != GRUB_ERR_NONE)
992 {
993 grub_dl_unref (my_mod);
994 loaded = 0;
995 }
b1b797cb 996
997 return grub_errno;
c9baafe7 998}
999
b1b797cb 1000static grub_err_t
1001grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
1002 int argc, char *argv[])
c9baafe7 1003{
3c76ea0c
VS
1004 grub_file_t *files = 0;
1005 grub_size_t size = 0;
c9baafe7 1006 grub_addr_t addr_min, addr_max;
1007 grub_addr_t addr;
55b40bc6 1008 grub_err_t err;
c9baafe7 1009 struct linux_kernel_header *lh;
3c76ea0c
VS
1010 int i;
1011 int nfiles = 0;
1012 grub_uint8_t *ptr;
b39f9d20 1013
c9baafe7 1014 if (argc == 0)
1015 {
9c4b5c13 1016 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
c9baafe7 1017 goto fail;
1018 }
b39f9d20 1019
c9baafe7 1020 if (! loaded)
1021 {
9c4b5c13 1022 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
c9baafe7 1023 goto fail;
1024 }
1025
3c76ea0c
VS
1026 files = grub_zalloc (argc * sizeof (files[0]));
1027 if (!files)
c9baafe7 1028 goto fail;
1029
3c76ea0c
VS
1030 for (i = 0; i < argc; i++)
1031 {
1032 grub_file_filter_disable_compression ();
1033 files[i] = grub_file_open (argv[i]);
1034 if (! files[i])
1035 goto fail;
1036 nfiles++;
1037 size += grub_file_size (files[i]);
1038 }
1039
c9baafe7 1040 initrd_pages = (page_align (size) >> 12);
1041
1042 lh = (struct linux_kernel_header *) real_mode_mem;
92907110 1043
1044 /* Get the highest address available for the initrd. */
1045 if (grub_le_to_cpu16 (lh->version) >= 0x0203)
1046 {
1047 addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
1048
1049 /* XXX in reality, Linux specifies a bogus value, so
1050 it is necessary to make sure that ADDR_MAX does not exceed
1051 0x3fffffff. */
1052 if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
1053 addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
1054 }
1055 else
1056 addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
b39f9d20 1057
c9baafe7 1058 if (linux_mem_size != 0 && linux_mem_size < addr_max)
1059 addr_max = linux_mem_size;
b39f9d20 1060
c9baafe7 1061 /* Linux 2.3.xx has a bug in the memory range check, so avoid
1062 the last page.
1063 Linux 2.2.xx has a bug in the memory range check, which is
1064 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
1065 addr_max -= 0x10000;
1066
1067 /* Usually, the compression ratio is about 50%. */
55b40bc6 1068 addr_min = (grub_addr_t) prot_mode_target + ((prot_mode_pages * 3) << 12)
c9baafe7 1069 + page_align (size);
b39f9d20 1070
92907110 1071 /* Put the initrd as high as possible, 4KiB aligned. */
1072 addr = (addr_max - size) & ~0xFFF;
c9baafe7 1073
92907110 1074 if (addr < addr_min)
c9baafe7 1075 {
7fd0baee 1076 grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big");
c9baafe7 1077 goto fail;
1078 }
b39f9d20 1079
4b2ec20b
VS
1080 {
1081 grub_relocator_chunk_t ch;
1082 err = grub_relocator_alloc_chunk_align (relocator, &ch,
1083 addr_min, addr, size, 0x1000,
9be4c45d
VS
1084 GRUB_RELOCATOR_PREFERENCE_HIGH,
1085 1);
4b2ec20b
VS
1086 if (err)
1087 return err;
1088 initrd_mem = get_virtual_current_address (ch);
1089 initrd_mem_target = get_physical_target_address (ch);
1090 }
b39f9d20 1091
3c76ea0c
VS
1092 ptr = initrd_mem;
1093 for (i = 0; i < nfiles; i++)
c9baafe7 1094 {
3c76ea0c
VS
1095 grub_ssize_t cursize = grub_file_size (files[i]);
1096 if (grub_file_read (files[i], ptr, cursize) != cursize)
1097 {
1098 if (!grub_errno)
1099 grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
1100 argv[i]);
1101 goto fail;
1102 }
1103 ptr += cursize;
c9baafe7 1104 }
1105
1a064917
RM
1106 grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
1107 (unsigned) addr, (unsigned) size);
b39f9d20 1108
55b40bc6 1109 lh->ramdisk_image = initrd_mem_target;
c9baafe7 1110 lh->ramdisk_size = size;
1111 lh->root_dev = 0x0100; /* XXX */
b39f9d20 1112
c9baafe7 1113 fail:
3c76ea0c
VS
1114 for (i = 0; i < nfiles; i++)
1115 grub_file_close (files[i]);
1116 grub_free (files);
b1b797cb 1117
1118 return grub_errno;
c9baafe7 1119}
1120
b1b797cb 1121static grub_command_t cmd_linux, cmd_initrd;
c9baafe7 1122
1123GRUB_MOD_INIT(linux)
1124{
b1b797cb 1125 cmd_linux = grub_register_command ("linux", grub_cmd_linux,
809bbfeb 1126 0, N_("Load Linux."));
b1b797cb 1127 cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
809bbfeb 1128 0, N_("Load initrd."));
c9baafe7 1129 my_mod = mod;
1130}
1131
1132GRUB_MOD_FINI(linux)
1133{
b1b797cb 1134 grub_unregister_command (cmd_linux);
1135 grub_unregister_command (cmd_initrd);
c9baafe7 1136}