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