Merge branch 'afs' into befs2
[grub2/phcoder.git] / loader / i386 / linux.c
blob78fa848229cd332eb07b06beb73bedaa67772677
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/loader.h>
20 #include <grub/machine/machine.h>
21 #include <grub/machine/memory.h>
22 #include <grub/machine/loader.h>
23 #include <grub/normal.h>
24 #include <grub/file.h>
25 #include <grub/disk.h>
26 #include <grub/err.h>
27 #include <grub/misc.h>
28 #include <grub/types.h>
29 #include <grub/dl.h>
30 #include <grub/mm.h>
31 #include <grub/term.h>
32 #include <grub/cpu/linux.h>
33 #include <grub/video.h>
34 /* FIXME: the definition of `struct grub_video_render_target' is
35 VBE-specific. */
36 #include <grub/i386/pc/vbe.h>
37 #include <grub/command.h>
39 #define GRUB_LINUX_CL_OFFSET 0x1000
40 #define GRUB_LINUX_CL_END_OFFSET 0x2000
42 /* This macro is useful for distributors, who can be certain they built FB support
43 into Linux, and therefore can benefit from seamless mode transition between
44 GRUB and Linux (saving boot time and visual glitches). Official GRUB, OTOH,
45 needs to be conservative. */
46 #ifdef GRUB_ASSUME_LINUX_HAS_FB_SUPPORT
47 #define DEFAULT_VIDEO_MODE "keep,1024x768,800x600,640x480"
48 #else
49 #define DEFAULT_VIDEO_MODE "text"
50 #endif
52 static grub_dl_t my_mod;
54 static grub_size_t linux_mem_size;
55 static int loaded;
56 static void *real_mode_mem;
57 static void *prot_mode_mem;
58 static void *initrd_mem;
59 static grub_uint32_t real_mode_pages;
60 static grub_uint32_t prot_mode_pages;
61 static grub_uint32_t initrd_pages;
63 static grub_uint8_t gdt[] __attribute__ ((aligned(16))) =
65 /* NULL. */
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67 /* Reserved. */
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69 /* Code segment. */
70 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00,
71 /* Data segment. */
72 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
75 struct gdt_descriptor
77 grub_uint16_t limit;
78 void *base;
79 } __attribute__ ((packed));
81 static struct gdt_descriptor gdt_desc =
83 sizeof (gdt) - 1,
84 gdt
87 struct idt_descriptor
89 grub_uint16_t limit;
90 void *base;
91 } __attribute__ ((packed));
93 static struct idt_descriptor idt_desc =
99 #ifdef GRUB_MACHINE_PCBIOS
100 struct linux_vesafb_res
102 grub_uint16_t width;
103 grub_uint16_t height;
106 struct linux_vesafb_mode
108 grub_uint8_t res_index;
109 grub_uint8_t depth;
112 enum vga_modes
114 VGA_320_200,
115 VGA_640_400,
116 VGA_640_480,
117 VGA_800_500,
118 VGA_800_600,
119 VGA_896_672,
120 VGA_1024_640,
121 VGA_1024_768,
122 VGA_1152_720,
123 VGA_1280_1024,
124 VGA_1440_900,
125 VGA_1600_1200,
128 static struct linux_vesafb_res linux_vesafb_res[] =
130 { 320, 200 },
131 { 640, 400 },
132 { 640, 480 },
133 { 800, 500 },
134 { 800, 600 },
135 { 896, 672 },
136 { 1024, 640 },
137 { 1024, 768 },
138 { 1152, 720 },
139 { 1280, 1024 },
140 { 1440, 900 },
141 { 1600, 1200 },
144 /* This is the reverse of the table in [linux]/Documentation/fb/vesafb.txt
145 plus a few more modes based on the table in
146 http://en.wikipedia.org/wiki/VESA_BIOS_Extensions */
147 struct linux_vesafb_mode linux_vesafb_modes[] =
149 { VGA_640_400, 8 }, /* 0x300 */
150 { VGA_640_480, 8 }, /* 0x301 */
151 { VGA_800_600, 4 }, /* 0x302 */
152 { VGA_800_600, 8 }, /* 0x303 */
153 { VGA_1024_768, 4 }, /* 0x304 */
154 { VGA_1024_768, 8 }, /* 0x305 */
155 { VGA_1280_1024, 4 }, /* 0x306 */
156 { VGA_1280_1024, 8 }, /* 0x307 */
157 { 0, 0 },
158 { 0, 0 },
159 { 0, 0 },
160 { 0, 0 },
161 { 0, 0 },
162 { VGA_320_200, 15 }, /* 0x30d */
163 { VGA_320_200, 16 }, /* 0x30e */
164 { VGA_320_200, 24 }, /* 0x30f */
165 { VGA_640_480, 15 }, /* 0x310 */
166 { VGA_640_480, 16 }, /* 0x311 */
167 { VGA_640_480, 24 }, /* 0x312 */
168 { VGA_800_600, 15 }, /* 0x313 */
169 { VGA_800_600, 16 }, /* 0x314 */
170 { VGA_800_600, 24 }, /* 0x315 */
171 { VGA_1024_768, 15 }, /* 0x316 */
172 { VGA_1024_768, 16 }, /* 0x317 */
173 { VGA_1024_768, 24 }, /* 0x318 */
174 { VGA_1280_1024, 15 }, /* 0x319 */
175 { VGA_1280_1024, 16 }, /* 0x31a */
176 { VGA_1280_1024, 24 }, /* 0x31b */
177 { VGA_1600_1200, 8 }, /* 0x31c */
178 { VGA_1600_1200, 15 }, /* 0x31d */
179 { VGA_1600_1200, 16 }, /* 0x31e */
180 { VGA_1600_1200, 24 }, /* 0x31f */
181 { 0, 0 },
182 { VGA_640_400, 15 }, /* 0x321 */
183 { VGA_640_400, 16 }, /* 0x322 */
184 { VGA_640_400, 24 }, /* 0x323 */
185 { VGA_640_400, 32 }, /* 0x324 */
186 { 0, 0 },
187 { 0, 0 },
188 { 0, 0 },
189 { 0, 0 },
190 { VGA_640_480, 32 }, /* 0x329 */
191 { 0, 0 },
192 { 0, 0 },
193 { 0, 0 },
194 { 0, 0 },
195 { 0, 0 },
196 { VGA_896_672, 8 }, /* 0x32f */
197 { VGA_896_672, 15 }, /* 0x330 */
198 { VGA_896_672, 16 }, /* 0x331 */
199 { VGA_896_672, 24 }, /* 0x332 */
200 { VGA_896_672, 32 }, /* 0x333 */
201 { 0, 0 },
202 { 0, 0 },
203 { 0, 0 },
204 { 0, 0 },
205 { 0, 0 },
206 { 0, 0 },
207 { 0, 0 },
208 { 0, 0 },
209 { 0, 0 },
210 { 0, 0 },
211 { 0, 0 },
212 { 0, 0 },
213 { 0, 0 },
214 { 0, 0 },
215 { VGA_1600_1200, 32 }, /* 0x342 */
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 { 0, 0 },
239 { 0, 0 },
240 { 0, 0 },
241 { 0, 0 },
242 { 0, 0 },
243 { 0, 0 },
244 { 0, 0 },
245 { VGA_1440_900, 8 }, /* 0x360 */
246 { VGA_1440_900, 15 }, /* 0x361 */
247 { VGA_1440_900, 16 }, /* 0x362 */
248 { VGA_1440_900, 24 }, /* 0x363 */
249 { VGA_1440_900, 32 }, /* 0x364 */
250 { VGA_1152_720, 8 }, /* 0x365 */
251 { VGA_1152_720, 15 }, /* 0x366 */
252 { VGA_1152_720, 16 }, /* 0x367 */
253 { VGA_1152_720, 24 }, /* 0x368 */
254 { VGA_1152_720, 32 }, /* 0x369 */
255 { VGA_1024_640, 8 }, /* 0x36a */
256 { VGA_1024_640, 15 }, /* 0x36b */
257 { VGA_1024_640, 16 }, /* 0x36c */
258 { VGA_1024_640, 24 }, /* 0x36d */
259 { VGA_1024_640, 32 }, /* 0x36e */
260 { VGA_800_500, 8 }, /* 0x36f */
261 { VGA_800_500, 15 }, /* 0x370 */
262 { VGA_800_500, 16 }, /* 0x371 */
263 { VGA_800_500, 24 }, /* 0x372 */
264 { VGA_800_500, 32 }, /* 0x373 */
266 #endif
268 static inline grub_size_t
269 page_align (grub_size_t size)
271 return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
274 /* Find the optimal number of pages for the memory map. */
275 static grub_size_t
276 find_mmap_size (void)
278 grub_size_t count = 0, mmap_size;
280 auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
281 int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)),
282 grub_uint64_t size __attribute__ ((unused)),
283 grub_uint32_t type __attribute__ ((unused)))
285 count++;
286 return 0;
289 grub_mmap_iterate (hook);
291 mmap_size = count * sizeof (struct grub_e820_mmap);
293 /* Increase the size a bit for safety, because GRUB allocates more on
294 later. */
295 mmap_size += (1 << 12);
297 return page_align (mmap_size);
300 static void
301 free_pages (void)
303 real_mode_mem = prot_mode_mem = initrd_mem = 0;
306 /* Allocate pages for the real mode code and the protected mode code
307 for linux as well as a memory map buffer. */
308 static int
309 allocate_pages (grub_size_t prot_size)
311 grub_size_t real_size, mmap_size;
313 /* Make sure that each size is aligned to a page boundary. */
314 real_size = GRUB_LINUX_CL_END_OFFSET;
315 prot_size = page_align (prot_size);
316 mmap_size = find_mmap_size ();
318 grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
319 (unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
321 /* Calculate the number of pages; Combine the real mode code with
322 the memory map buffer for simplicity. */
323 real_mode_pages = ((real_size + mmap_size) >> 12);
324 prot_mode_pages = (prot_size >> 12);
326 /* Initialize the memory pointers with NULL for convenience. */
327 free_pages ();
329 /* FIXME: Should request low memory from the heap when this feature is
330 implemented. */
332 auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
333 int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
335 /* We must put real mode code in the traditional space. */
337 if (type == GRUB_MACHINE_MEMORY_AVAILABLE
338 && addr <= 0x90000)
340 if (addr < 0x10000)
342 size += addr - 0x10000;
343 addr = 0x10000;
346 if (addr + size > 0x90000)
347 size = 0x90000 - addr;
349 if (real_size + mmap_size > size)
350 return 0;
352 real_mode_mem =
353 (void *) (grub_size_t) ((addr + size) - (real_size + mmap_size));
354 return 1;
357 return 0;
359 grub_mmap_iterate (hook);
360 if (! real_mode_mem)
362 grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
363 goto fail;
366 prot_mode_mem = (void *) 0x100000;
368 grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
369 "prot_mode_mem = %lx, prot_mode_pages = %x\n",
370 (unsigned long) real_mode_mem, (unsigned) real_mode_pages,
371 (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
373 return 1;
375 fail:
376 free_pages ();
377 return 0;
380 static void
381 grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
382 grub_uint64_t start, grub_uint64_t size,
383 grub_uint32_t type)
385 int n = *e820_num;
387 if (n >= GRUB_E820_MAX_ENTRY)
388 grub_fatal ("Too many e820 memory map entries");
390 if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
391 (e820_map[n - 1].type == type))
392 e820_map[n - 1].size += size;
393 else
395 e820_map[n].addr = start;
396 e820_map[n].size = size;
397 e820_map[n].type = type;
398 (*e820_num)++;
402 static int
403 grub_linux_setup_video (struct linux_kernel_params *params)
405 struct grub_video_mode_info mode_info;
406 struct grub_video_render_target *render_target;
407 int ret;
409 ret = grub_video_get_info (&mode_info);
410 if (ret)
411 return 1;
413 ret = grub_video_get_active_render_target (&render_target);
414 if (ret)
415 return 1;
417 params->lfb_width = mode_info.width;
418 params->lfb_height = mode_info.height;
419 params->lfb_depth = mode_info.bpp;
420 params->lfb_line_len = mode_info.pitch;
422 params->lfb_base = (grub_size_t) render_target->data;
423 params->lfb_size = (params->lfb_line_len * params->lfb_height + 65535) >> 16;
425 params->red_mask_size = mode_info.red_mask_size;
426 params->red_field_pos = mode_info.red_field_pos;
427 params->green_mask_size = mode_info.green_mask_size;
428 params->green_field_pos = mode_info.green_field_pos;
429 params->blue_mask_size = mode_info.blue_mask_size;
430 params->blue_field_pos = mode_info.blue_field_pos;
431 params->reserved_mask_size = mode_info.reserved_mask_size;
432 params->reserved_field_pos = mode_info.reserved_field_pos;
434 return 0;
437 #ifdef __x86_64__
438 extern grub_uint8_t grub_linux_trampoline_start[];
439 extern grub_uint8_t grub_linux_trampoline_end[];
440 #endif
442 static grub_err_t
443 grub_linux_boot (void)
445 struct linux_kernel_params *params;
446 int e820_num;
447 grub_err_t err = 0;
448 char *modevar, *tmp;
450 params = real_mode_mem;
452 modevar = grub_env_get ("gfxpayload");
454 /* Now all graphical modes are acceptable.
455 May change in future if we have modes without framebuffer. */
456 if (modevar && *modevar != 0)
458 tmp = grub_malloc (grub_strlen (modevar)
459 + sizeof (DEFAULT_VIDEO_MODE) + 1);
460 if (! tmp)
461 return grub_errno;
462 grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar);
463 err = grub_video_set_mode (tmp, 0);
464 grub_free (tmp);
466 #ifndef GRUB_ASSUME_LINUX_HAS_FB_SUPPORT
467 else
468 err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0);
469 #endif
471 if (err)
473 grub_print_error ();
474 grub_printf ("Booting however\n");
475 grub_errno = GRUB_ERR_NONE;
478 if (! grub_linux_setup_video (params))
479 params->have_vga = GRUB_VIDEO_TYPE_VLFB;
480 else
482 params->have_vga = GRUB_VIDEO_TYPE_TEXT;
483 params->video_width = 80;
484 params->video_height = 25;
487 grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n",
488 (unsigned) params->code32_start,
489 (unsigned long) &(idt_desc.limit),
490 (unsigned long) &(gdt_desc.limit));
491 grub_dprintf ("linux", "idt = %x:%lx, gdt = %x:%lx\n",
492 (unsigned) idt_desc.limit, (unsigned long) idt_desc.base,
493 (unsigned) gdt_desc.limit, (unsigned long) gdt_desc.base);
495 auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
496 int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
498 switch (type)
500 case GRUB_MACHINE_MEMORY_AVAILABLE:
501 grub_e820_add_region (params->e820_map, &e820_num,
502 addr, size, GRUB_E820_RAM);
503 break;
505 #ifdef GRUB_MACHINE_MEMORY_ACPI
506 case GRUB_MACHINE_MEMORY_ACPI:
507 grub_e820_add_region (params->e820_map, &e820_num,
508 addr, size, GRUB_E820_ACPI);
509 break;
510 #endif
512 #ifdef GRUB_MACHINE_MEMORY_NVS
513 case GRUB_MACHINE_MEMORY_NVS:
514 grub_e820_add_region (params->e820_map, &e820_num,
515 addr, size, GRUB_E820_NVS);
516 break;
517 #endif
519 #ifdef GRUB_MACHINE_MEMORY_CODE
520 case GRUB_MACHINE_MEMORY_CODE:
521 grub_e820_add_region (params->e820_map, &e820_num,
522 addr, size, GRUB_E820_EXEC_CODE);
523 break;
524 #endif
526 default:
527 grub_e820_add_region (params->e820_map, &e820_num,
528 addr, size, GRUB_E820_RESERVED);
530 return 0;
533 e820_num = 0;
534 grub_mmap_iterate (hook);
535 params->mmap_size = e820_num;
537 /* Initialize these last, because terminal position could be affected by printfs above. */
538 if (params->have_vga == GRUB_VIDEO_TYPE_TEXT)
540 params->video_cursor_x = grub_getxy () >> 8;
541 params->video_cursor_y = grub_getxy () & 0xff;
544 #ifdef __x86_64__
546 grub_memcpy ((char *) prot_mode_mem + (prot_mode_pages << 12),
547 grub_linux_trampoline_start,
548 grub_linux_trampoline_end - grub_linux_trampoline_start);
550 ((void (*) (unsigned long, void *)) ((char *) prot_mode_mem
551 + (prot_mode_pages << 12)))
552 (params->code32_start, real_mode_mem);
553 #else
555 /* Hardware interrupts are not safe any longer. */
556 asm volatile ("cli" : : );
558 /* Load the IDT and the GDT for the bootstrap. */
559 asm volatile ("lidt %0" : : "m" (idt_desc));
560 asm volatile ("lgdt %0" : : "m" (gdt_desc));
562 /* Enter Linux. */
563 asm volatile ("jmp *%2" : : "b" (0), "S" (real_mode_mem), "g" (params->code32_start));
565 #endif
567 /* Never reach here. */
568 return GRUB_ERR_NONE;
571 static grub_err_t
572 grub_linux_unload (void)
574 grub_dl_unref (my_mod);
575 loaded = 0;
576 return GRUB_ERR_NONE;
579 static grub_err_t
580 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
581 int argc, char *argv[])
583 grub_file_t file = 0;
584 struct linux_kernel_header lh;
585 struct linux_kernel_params *params;
586 grub_uint8_t setup_sects;
587 grub_size_t real_size, prot_size;
588 grub_ssize_t len;
589 int i;
590 char *dest;
592 grub_dl_ref (my_mod);
594 if (argc == 0)
596 grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
597 goto fail;
600 file = grub_file_open (argv[0]);
601 if (! file)
602 goto fail;
604 if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
606 grub_error (GRUB_ERR_READ_ERROR, "cannot read the linux header");
607 goto fail;
610 if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
612 grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
613 goto fail;
616 if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
618 grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
619 goto fail;
622 if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
624 grub_error (GRUB_ERR_BAD_OS, "zImage doesn't support 32-bit boot"
625 #ifdef GRUB_MACHINE_PCBIOS
626 " (try with `linux16')"
627 #endif
629 goto fail;
632 /* FIXME: 2.03 is not always good enough (Linux 2.4 can be 2.03 and
633 still not support 32-bit boot. */
634 if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
635 || grub_le_to_cpu16 (lh.version) < 0x0203)
637 grub_error (GRUB_ERR_BAD_OS, "version too old for 32-bit boot"
638 #ifdef GRUB_MACHINE_PCBIOS
639 " (try with `linux16')"
640 #endif
642 goto fail;
645 setup_sects = lh.setup_sects;
647 /* If SETUP_SECTS is not set, set it to the default (4). */
648 if (! setup_sects)
649 setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
651 real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
652 prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
654 if (! allocate_pages (prot_size))
655 goto fail;
657 params = (struct linux_kernel_params *) real_mode_mem;
658 grub_memset (params, 0, GRUB_LINUX_CL_END_OFFSET);
659 grub_memcpy (&params->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
661 params->ps_mouse = params->padding10 = 0;
663 len = 0x400 - sizeof (lh);
664 if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len)
666 grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
667 goto fail;
670 params->type_of_loader = (LINUX_LOADER_ID_GRUB << 4);
672 /* These two are used (instead of cmd_line_ptr) by older versions of Linux,
673 and otherwise ignored. */
674 params->cl_magic = GRUB_LINUX_CL_MAGIC;
675 params->cl_offset = 0x1000;
677 params->cmd_line_ptr = (unsigned long) real_mode_mem + 0x1000;
678 params->ramdisk_image = 0;
679 params->ramdisk_size = 0;
681 params->heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
682 params->loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
684 /* These are not needed to be precise, because Linux uses these values
685 only to raise an error when the decompression code cannot find good
686 space. */
687 params->ext_mem = ((32 * 0x100000) >> 10);
688 params->alt_mem = ((32 * 0x100000) >> 10);
690 /* Ignored by Linux. */
691 params->video_page = 0;
693 /* Must be non-zero even in text mode, or Linux will think there's no VGA. */
694 params->video_mode = 0x3;
696 /* Only used when `video_mode == 0x7', otherwise ignored. */
697 params->video_ega_bx = 0;
699 params->font_size = 16; /* XXX */
701 /* The other parameters are filled when booting. */
703 grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
705 grub_printf (" [Linux-bzImage, setup=0x%x, size=0x%x]\n",
706 (unsigned) real_size, (unsigned) prot_size);
708 /* Look for memory size and video mode specified on the command line. */
709 linux_mem_size = 0;
710 for (i = 1; i < argc; i++)
711 #ifdef GRUB_MACHINE_PCBIOS
712 if (grub_memcmp (argv[i], "vga=", 4) == 0)
714 /* Video mode selection support. */
715 char *val = argv[i] + 4;
716 unsigned vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
717 struct linux_vesafb_mode *linux_mode;
718 grub_err_t err;
719 char *buf;
721 if (grub_strcmp (val, "normal") == 0)
722 vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
723 else if (grub_strcmp (val, "ext") == 0)
724 vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
725 else if (grub_strcmp (val, "ask") == 0)
727 grub_printf ("Legacy `ask' parameter no longer supported.\n");
729 /* We usually would never do this in a loader, but "vga=ask" means user
730 requested interaction, so it can't hurt to request keyboard input. */
731 grub_wait_after_message ();
733 goto fail;
735 else
736 vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
738 switch (vid_mode)
740 case 0:
741 case GRUB_LINUX_VID_MODE_NORMAL:
742 grub_env_set ("gfxpayload", "text");
743 grub_printf ("%s is deprecated. "
744 "Use set gfxpayload=text before "
745 "linux command instead.\n",
746 argv[i]);
747 break;
749 case 1:
750 case GRUB_LINUX_VID_MODE_EXTENDED:
751 /* FIXME: support 80x50 text. */
752 grub_env_set ("gfxpayload", "text");
753 grub_printf ("%s is deprecated. "
754 "Use set gfxpayload=text before "
755 "linux command instead.\n",
756 argv[i]);
757 break;
758 default:
759 /* Ignore invalid values. */
760 if (vid_mode < GRUB_LINUX_VID_MODE_VESA_START ||
761 vid_mode >= GRUB_LINUX_VID_MODE_VESA_START +
762 ARRAY_SIZE (linux_vesafb_modes))
764 grub_env_set ("gfxpayload", "text");
765 grub_printf ("%s is deprecated. Mode %d isn't recognized. "
766 "Use set gfxpayload=WIDTHxHEIGHT[xDEPTH] before "
767 "linux command instead.\n",
768 argv[i], vid_mode);
769 break;
772 buf = grub_malloc (sizeof ("WWWWxHHHHxDD;WWWWxHHHH"));
773 if (! buf)
774 goto fail;
776 linux_mode
777 = &linux_vesafb_modes[vid_mode - GRUB_LINUX_VID_MODE_VESA_START];
779 grub_sprintf (buf, "%ux%ux%u;%ux%u",
780 linux_vesafb_res[linux_mode->res_index].width,
781 linux_vesafb_res[linux_mode->res_index].height,
782 linux_mode->depth,
783 linux_vesafb_res[linux_mode->res_index].width,
784 linux_vesafb_res[linux_mode->res_index].height);
785 grub_printf ("%s is deprecated. "
786 "Use set gfxpayload=%s before "
787 "linux command instead.\n",
788 argv[i], buf);
789 err = grub_env_set ("gfxpayload", buf);
790 grub_free (buf);
791 if (err)
792 goto fail;
795 else
796 #endif /* GRUB_MACHINE_PCBIOS */
797 if (grub_memcmp (argv[i], "mem=", 4) == 0)
799 char *val = argv[i] + 4;
801 linux_mem_size = grub_strtoul (val, &val, 0);
803 if (grub_errno)
805 grub_errno = GRUB_ERR_NONE;
806 linux_mem_size = 0;
808 else
810 int shift = 0;
812 switch (grub_tolower (val[0]))
814 case 'g':
815 shift += 10;
816 case 'm':
817 shift += 10;
818 case 'k':
819 shift += 10;
820 default:
821 break;
824 /* Check an overflow. */
825 if (linux_mem_size > (~0UL >> shift))
826 linux_mem_size = 0;
827 else
828 linux_mem_size <<= shift;
831 else if (grub_memcmp (argv[i], "quiet", sizeof ("quiet") - 1) == 0)
833 params->loadflags |= GRUB_LINUX_FLAG_QUIET;
837 /* Specify the boot file. */
838 dest = grub_stpcpy ((char *) real_mode_mem + GRUB_LINUX_CL_OFFSET,
839 "BOOT_IMAGE=");
840 dest = grub_stpcpy (dest, argv[0]);
842 /* Copy kernel parameters. */
843 for (i = 1;
844 i < argc
845 && dest + grub_strlen (argv[i]) + 1 < ((char *) real_mode_mem
846 + GRUB_LINUX_CL_END_OFFSET);
847 i++)
849 *dest++ = ' ';
850 dest = grub_stpcpy (dest, argv[i]);
853 len = prot_size;
854 if (grub_file_read (file, (void *) GRUB_LINUX_BZIMAGE_ADDR, len) != len)
855 grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
857 if (grub_errno == GRUB_ERR_NONE)
859 grub_loader_set (grub_linux_boot, grub_linux_unload,
860 0 /* set noreturn=0 in order to avoid grub_console_fini() */);
861 loaded = 1;
864 fail:
866 if (file)
867 grub_file_close (file);
869 if (grub_errno != GRUB_ERR_NONE)
871 grub_dl_unref (my_mod);
872 loaded = 0;
875 return grub_errno;
878 static grub_err_t
879 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
880 int argc, char *argv[])
882 grub_file_t file = 0;
883 grub_ssize_t size;
884 grub_addr_t addr_min, addr_max;
885 grub_addr_t addr;
886 struct linux_kernel_header *lh;
888 if (argc == 0)
890 grub_error (GRUB_ERR_BAD_ARGUMENT, "No module specified");
891 goto fail;
894 if (! loaded)
896 grub_error (GRUB_ERR_BAD_ARGUMENT, "You need to load the kernel first.");
897 goto fail;
900 file = grub_file_open (argv[0]);
901 if (! file)
902 goto fail;
904 size = grub_file_size (file);
905 initrd_pages = (page_align (size) >> 12);
907 lh = (struct linux_kernel_header *) real_mode_mem;
909 /* Get the highest address available for the initrd. */
910 if (grub_le_to_cpu16 (lh->version) >= 0x0203)
912 addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
914 /* XXX in reality, Linux specifies a bogus value, so
915 it is necessary to make sure that ADDR_MAX does not exceed
916 0x3fffffff. */
917 if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
918 addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
920 else
921 addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
923 if (linux_mem_size != 0 && linux_mem_size < addr_max)
924 addr_max = linux_mem_size;
926 /* Linux 2.3.xx has a bug in the memory range check, so avoid
927 the last page.
928 Linux 2.2.xx has a bug in the memory range check, which is
929 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
930 addr_max -= 0x10000;
932 /* Usually, the compression ratio is about 50%. */
933 addr_min = (grub_addr_t) prot_mode_mem + ((prot_mode_pages * 3) << 12)
934 + page_align (size);
936 if (addr_max > grub_os_area_addr + grub_os_area_size)
937 addr_max = grub_os_area_addr + grub_os_area_size;
939 /* Put the initrd as high as possible, 4KiB aligned. */
940 addr = (addr_max - size) & ~0xFFF;
942 if (addr < addr_min)
944 grub_error (GRUB_ERR_OUT_OF_RANGE, "The initrd is too big");
945 goto fail;
948 initrd_mem = (void *) addr;
950 if (grub_file_read (file, initrd_mem, size) != size)
952 grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
953 goto fail;
956 grub_printf (" [Initrd, addr=0x%x, size=0x%x]\n",
957 (unsigned) addr, (unsigned) size);
959 lh->ramdisk_image = addr;
960 lh->ramdisk_size = size;
961 lh->root_dev = 0x0100; /* XXX */
963 fail:
964 if (file)
965 grub_file_close (file);
967 return grub_errno;
970 static grub_command_t cmd_linux, cmd_initrd;
972 GRUB_MOD_INIT(linux)
974 cmd_linux = grub_register_command ("linux", grub_cmd_linux,
975 0, "load linux");
976 cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
977 0, "load initrd");
978 my_mod = mod;
981 GRUB_MOD_FINI(linux)
983 grub_unregister_command (cmd_linux);
984 grub_unregister_command (cmd_initrd);