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