2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2006,2007,2008 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/file.h>
24 #include <grub/disk.h>
26 #include <grub/misc.h>
27 #include <grub/types.h>
28 #include <grub/rescue.h>
31 #include <grub/term.h>
32 #include <grub/cpu/linux.h>
34 #define GRUB_LINUX_CL_OFFSET 0x1000
35 #define GRUB_LINUX_CL_END_OFFSET 0x2000
37 static grub_dl_t my_mod
;
39 static grub_size_t linux_mem_size
;
41 static void *real_mode_mem
;
42 static void *prot_mode_mem
;
43 static void *initrd_mem
;
44 static grub_uint32_t real_mode_pages
;
45 static grub_uint32_t prot_mode_pages
;
46 static grub_uint32_t initrd_pages
;
48 static grub_uint8_t gdt
[] __attribute__ ((aligned(16))) =
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00,
57 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
64 } __attribute__ ((packed
));
66 static struct gdt_descriptor gdt_desc
=
76 } __attribute__ ((packed
));
78 static struct idt_descriptor idt_desc
=
84 static inline grub_size_t
85 page_align (grub_size_t size
)
87 return (size
+ (1 << 12) - 1) & (~((1 << 12) - 1));
90 /* Find the optimal number of pages for the memory map. */
94 grub_size_t count
= 0, mmap_size
;
96 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
, grub_uint32_t
);
97 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
__attribute__ ((unused
)),
98 grub_uint64_t size
__attribute__ ((unused
)),
99 grub_uint32_t type
__attribute__ ((unused
)))
105 grub_machine_mmap_iterate (hook
);
107 mmap_size
= count
* sizeof (struct grub_e820_mmap
);
109 /* Increase the size a bit for safety, because GRUB allocates more on
111 mmap_size
+= (1 << 12);
113 return page_align (mmap_size
);
119 real_mode_mem
= prot_mode_mem
= initrd_mem
= 0;
122 /* Allocate pages for the real mode code and the protected mode code
123 for linux as well as a memory map buffer. */
125 allocate_pages (grub_size_t prot_size
)
127 grub_size_t real_size
, mmap_size
;
129 /* Make sure that each size is aligned to a page boundary. */
130 real_size
= GRUB_LINUX_CL_END_OFFSET
;
131 prot_size
= page_align (prot_size
);
132 mmap_size
= find_mmap_size ();
134 grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
135 (unsigned) real_size
, (unsigned) prot_size
, (unsigned) mmap_size
);
137 /* Calculate the number of pages; Combine the real mode code with
138 the memory map buffer for simplicity. */
139 real_mode_pages
= ((real_size
+ mmap_size
) >> 12);
140 prot_mode_pages
= (prot_size
>> 12);
142 /* Initialize the memory pointers with NULL for convenience. */
146 /* i386-pc port adds lower memory to heap, which collides with
147 `real_mode_mem' allocation below. */
148 #ifdef GRUB_MACHINE_PCBIOS
152 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
, grub_uint32_t
);
153 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
, grub_uint64_t size
, grub_uint32_t type
)
155 /* We must put real mode code in the traditional space. */
157 if (type
== GRUB_MACHINE_MEMORY_AVAILABLE
162 size
+= addr
- 0x10000;
166 if (addr
+ size
> 0x90000)
167 size
= 0x90000 - addr
;
169 if (real_size
+ mmap_size
> size
)
172 real_mode_mem
= (addr
+ size
) - (real_size
+ mmap_size
);
178 grub_machine_mmap_iterate (hook
);
181 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "cannot allocate real mode pages");
185 prot_mode_mem
= (void *) 0x100000;
187 grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
188 "prot_mode_mem = %lx, prot_mode_pages = %x\n",
189 (unsigned long) real_mode_mem
, (unsigned) real_mode_pages
,
190 (unsigned long) prot_mode_mem
, (unsigned) prot_mode_pages
);
200 grub_e820_add_region (struct grub_e820_mmap
*e820_map
, int *e820_num
,
201 grub_uint64_t start
, grub_uint64_t size
,
206 if (n
>= GRUB_E820_MAX_ENTRY
)
207 grub_fatal ("Too many e820 memory map entries");
209 if ((n
> 0) && (e820_map
[n
- 1].addr
+ e820_map
[n
- 1].size
== start
) &&
210 (e820_map
[n
- 1].type
== type
))
211 e820_map
[n
- 1].size
+= size
;
214 e820_map
[n
].addr
= start
;
215 e820_map
[n
].size
= size
;
216 e820_map
[n
].type
= type
;
224 grub_uint32_t kernel_entry
;
225 grub_uint32_t kernel_cs
;
230 grub_linux32_boot (void)
232 struct linux_kernel_params
*params
;
235 params
= real_mode_mem
;
237 grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n",
238 (unsigned) params
->code32_start
,
239 (unsigned long) &(idt_desc
.limit
),
240 (unsigned long) &(gdt_desc
.limit
));
241 grub_dprintf ("linux", "idt = %x:%lx, gdt = %x:%lx\n",
242 (unsigned) idt_desc
.limit
, (unsigned long) idt_desc
.base
,
243 (unsigned) gdt_desc
.limit
, (unsigned long) gdt_desc
.base
);
245 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
, grub_uint32_t
);
246 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
, grub_uint64_t size
, grub_uint32_t type
)
250 case GRUB_MACHINE_MEMORY_AVAILABLE
:
251 grub_e820_add_region (params
->e820_map
, &e820_num
,
252 addr
, size
, GRUB_E820_RAM
);
256 grub_e820_add_region (params
->e820_map
, &e820_num
,
257 addr
, size
, GRUB_E820_RESERVED
);
263 grub_machine_mmap_iterate (hook
);
264 params
->mmap_size
= e820_num
;
266 /* Hardware interrupts are not safe any longer. */
267 asm volatile ("cli" : : );
269 /* Load the IDT and the GDT for the bootstrap. */
270 asm volatile ("lidt %0" : : "m" (idt_desc
));
271 asm volatile ("lgdt %0" : : "m" (gdt_desc
));
275 jumpvector
.kernel_entry
= (grub_uint64_t
) grub_linux_real_boot
;
276 jumpvector
.kernel_cs
= 0x10;
278 asm volatile ( "mov %0, %%rbx" : : "m" (params
->code32_start
));
279 asm volatile ( "mov %0, %%rsi" : : "m" (real_mode_mem
));
281 asm volatile ( "ljmp *%0" : : "m" (jumpvector
));
285 /* Pass parameters. */
286 asm volatile ("movl %0, %%ecx" : : "m" (params
->code32_start
));
287 asm volatile ("movl %0, %%esi" : : "m" (real_mode_mem
));
289 asm volatile ("xorl %%ebx, %%ebx" : : );
292 asm volatile ("jmp *%%ecx" : : );
296 /* Never reach here. */
297 return GRUB_ERR_NONE
;
301 grub_linux_unload (void)
304 grub_dl_unref (my_mod
);
306 return GRUB_ERR_NONE
;
310 grub_rescue_cmd_linux (int argc
, char *argv
[])
312 grub_file_t file
= 0;
313 struct linux_kernel_header lh
;
314 struct linux_kernel_params
*params
;
315 grub_uint8_t setup_sects
;
316 grub_size_t real_size
, prot_size
;
322 grub_dl_ref (my_mod
);
326 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no kernel specified");
330 file
= grub_file_open (argv
[0]);
334 if (grub_file_read (file
, (char *) &lh
, sizeof (lh
)) != sizeof (lh
))
336 grub_error (GRUB_ERR_READ_ERROR
, "cannot read the linux header");
340 if (lh
.boot_flag
!= grub_cpu_to_le16 (0xaa55))
342 grub_error (GRUB_ERR_BAD_OS
, "invalid magic number");
346 if (lh
.setup_sects
> GRUB_LINUX_MAX_SETUP_SECTS
)
348 grub_error (GRUB_ERR_BAD_OS
, "too many setup sectors");
352 /* FIXME: Is 2.02 recent enough for 32-bit boot? */
353 if (lh
.header
!= grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
354 || grub_le_to_cpu16 (lh
.version
) < 0x0203)
356 grub_error (GRUB_ERR_BAD_OS
, "too old version");
360 /* zImage doesn't support 32-bit boot. */
361 if (! (lh
.loadflags
& GRUB_LINUX_FLAG_BIG_KERNEL
))
363 grub_error (GRUB_ERR_BAD_OS
, "zImage is not supported");
367 setup_sects
= lh
.setup_sects
;
369 /* If SETUP_SECTS is not set, set it to the default (4). */
371 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
373 real_size
= setup_sects
<< GRUB_DISK_SECTOR_BITS
;
374 prot_size
= grub_file_size (file
) - real_size
- GRUB_DISK_SECTOR_SIZE
;
376 if (! allocate_pages (prot_size
))
379 params
= (struct linux_kernel_params
*) real_mode_mem
;
380 grub_memset (params
, 0, GRUB_LINUX_CL_END_OFFSET
);
381 grub_memcpy (¶ms
->setup_sects
, &lh
.setup_sects
, sizeof (lh
) - 0x1F1);
383 params
->ps_mouse
= params
->padding10
= 0;
385 len
= 0x400 - sizeof (lh
);
386 if (grub_file_read (file
, (char *) real_mode_mem
+ sizeof (lh
), len
) != len
)
388 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
392 params
->type_of_loader
= (LINUX_LOADER_ID_GRUB
<< 4);
394 params
->cl_magic
= GRUB_LINUX_CL_MAGIC
;
395 params
->cl_offset
= 0x1000;
396 params
->cmd_line_ptr
= (unsigned long) real_mode_mem
+ 0x1000;
397 params
->ramdisk_image
= 0;
398 params
->ramdisk_size
= 0;
400 params
->heap_end_ptr
= GRUB_LINUX_HEAP_END_OFFSET
;
401 params
->loadflags
|= GRUB_LINUX_FLAG_CAN_USE_HEAP
;
403 /* These are not needed to be precise, because Linux uses these values
404 only to raise an error when the decompression code cannot find good
406 params
->ext_mem
= ((32 * 0x100000) >> 10);
407 params
->alt_mem
= ((32 * 0x100000) >> 10);
409 params
->video_cursor_x
= grub_getxy () >> 8;
410 params
->video_cursor_y
= grub_getxy () & 0xff;
411 params
->video_page
= 0; /* ??? */
412 params
->video_mode
= 0;
413 params
->video_width
= (grub_getwh () >> 8);
414 params
->video_ega_bx
= 0;
415 params
->video_height
= (grub_getwh () & 0xff);
416 params
->have_vga
= 0;
417 params
->font_size
= 16; /* XXX */
419 /* The other parameters are filled when booting. */
421 grub_file_seek (file
, real_size
+ GRUB_DISK_SECTOR_SIZE
);
423 grub_printf (" [Linux-bzImage, setup=0x%x, size=0x%x]\n",
424 (unsigned) real_size
, (unsigned) prot_size
);
426 /* Detect explicitly specified memory size, if any. */
429 for (i
= 1; i
< argc
; i
++)
430 if (grub_memcmp (argv
[i
], "mem=", 4) == 0)
432 char *val
= argv
[i
] + 4;
434 linux_mem_size
= grub_strtoul (val
, &val
, 0);
438 grub_errno
= GRUB_ERR_NONE
;
445 switch (grub_tolower (val
[0]))
457 /* Check an overflow. */
458 if (linux_mem_size
> (~0UL >> shift
))
461 linux_mem_size
<<= shift
;
465 /* Specify the boot file. */
466 dest
= grub_stpcpy ((char *) real_mode_mem
+ GRUB_LINUX_CL_OFFSET
,
468 dest
= grub_stpcpy (dest
, argv
[0]);
470 /* Copy kernel parameters. */
473 && dest
+ grub_strlen (argv
[i
]) + 1 < ((char *) real_mode_mem
474 + GRUB_LINUX_CL_END_OFFSET
);
478 dest
= grub_stpcpy (dest
, argv
[i
]);
482 if (grub_file_read (file
, (char *) GRUB_LINUX_BZIMAGE_ADDR
, len
) != len
)
483 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
485 if (grub_errno
== GRUB_ERR_NONE
)
487 grub_loader_set (grub_linux32_boot
, grub_linux_unload
, 1);
494 grub_file_close (file
);
496 if (grub_errno
!= GRUB_ERR_NONE
)
498 grub_dl_unref (my_mod
);
504 grub_rescue_cmd_initrd (int argc
, char *argv
[])
506 grub_file_t file
= 0;
508 grub_addr_t addr_min
, addr_max
;
510 struct linux_kernel_header
*lh
;
514 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module specified");
520 grub_error (GRUB_ERR_BAD_ARGUMENT
, "You need to load the kernel first.");
524 file
= grub_file_open (argv
[0]);
528 size
= grub_file_size (file
);
529 initrd_pages
= (page_align (size
) >> 12);
531 lh
= (struct linux_kernel_header
*) real_mode_mem
;
533 /* Get the highest address available for the initrd. */
534 if (grub_le_to_cpu16 (lh
->version
) >= 0x0203)
536 addr_max
= grub_cpu_to_le32 (lh
->initrd_addr_max
);
538 /* XXX in reality, Linux specifies a bogus value, so
539 it is necessary to make sure that ADDR_MAX does not exceed
541 if (addr_max
> GRUB_LINUX_INITRD_MAX_ADDRESS
)
542 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
545 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
547 if (linux_mem_size
!= 0 && linux_mem_size
< addr_max
)
548 addr_max
= linux_mem_size
;
550 /* Linux 2.3.xx has a bug in the memory range check, so avoid
552 Linux 2.2.xx has a bug in the memory range check, which is
553 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
556 /* Usually, the compression ratio is about 50%. */
557 addr_min
= (grub_addr_t
) prot_mode_mem
+ ((prot_mode_pages
* 3) << 12)
560 if (addr_max
> grub_os_area_addr
+ grub_os_area_size
)
561 addr_max
= grub_os_area_addr
+ grub_os_area_size
;
563 /* Put the initrd as high as possible, 4KiB aligned. */
564 addr
= (addr_max
- size
) & ~0xFFF;
568 grub_error (GRUB_ERR_OUT_OF_RANGE
, "The initrd is too big");
572 initrd_mem
= (void *) addr
;
574 if (grub_file_read (file
, initrd_mem
, size
) != size
)
576 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
580 grub_printf (" [Initrd, addr=0x%x, size=0x%x]\n",
581 (unsigned) addr
, (unsigned) size
);
583 lh
->ramdisk_image
= addr
;
584 lh
->ramdisk_size
= size
;
585 lh
->root_dev
= 0x0100; /* XXX */
589 grub_file_close (file
);
595 grub_rescue_register_command ("linux",
596 grub_rescue_cmd_linux
,
598 grub_rescue_register_command ("initrd",
599 grub_rescue_cmd_initrd
,
606 grub_rescue_unregister_command ("linux");
607 grub_rescue_unregister_command ("initrd");