1 /* linux.c - boot Linux zImage or bzImage */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/loader.h>
21 #include <grub/machine/loader.h>
22 #include <grub/file.h>
24 #include <grub/device.h>
25 #include <grub/disk.h>
26 #include <grub/misc.h>
27 #include <grub/types.h>
28 #include <grub/machine/init.h>
29 #include <grub/machine/memory.h>
30 #include <grub/rescue.h>
32 #include <grub/cpu/linux.h>
34 static grub_dl_t my_mod
;
36 static grub_size_t linux_mem_size
;
40 grub_linux_unload (void)
42 grub_dl_unref (my_mod
);
48 grub_rescue_cmd_linux (int argc
, char *argv
[])
51 struct linux_kernel_header lh
;
52 grub_uint8_t setup_sects
;
53 grub_size_t real_size
, prot_size
;
62 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no kernel specified");
66 file
= grub_file_open (argv
[0]);
70 if ((grub_size_t
) grub_file_size (file
) > grub_os_area_size
)
72 grub_error (GRUB_ERR_OUT_OF_RANGE
, "too big kernel (0x%x > 0x%x)",
73 (grub_size_t
) grub_file_size (file
),
78 if (grub_file_read (file
, (char *) &lh
, sizeof (lh
)) != sizeof (lh
))
80 grub_error (GRUB_ERR_READ_ERROR
, "cannot read the linux header");
84 if (lh
.boot_flag
!= grub_cpu_to_le16 (0xaa55))
86 grub_error (GRUB_ERR_BAD_OS
, "invalid magic number");
90 if (lh
.setup_sects
> GRUB_LINUX_MAX_SETUP_SECTS
)
92 grub_error (GRUB_ERR_BAD_OS
, "too many setup sectors");
96 grub_linux_is_bzimage
= 0;
97 setup_sects
= lh
.setup_sects
;
100 if (lh
.header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
101 && grub_le_to_cpu16 (lh
.version
) >= 0x0200)
103 grub_linux_is_bzimage
= (lh
.loadflags
& GRUB_LINUX_FLAG_BIG_KERNEL
);
104 lh
.type_of_loader
= GRUB_LINUX_BOOT_LOADER_TYPE
;
106 /* Put the real mode part at as a high location as possible. */
107 grub_linux_real_addr
= (char *) (grub_lower_mem
108 - GRUB_LINUX_SETUP_MOVE_SIZE
);
109 /* But it must not exceed the traditional area. */
110 if (grub_linux_real_addr
> (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
)
111 grub_linux_real_addr
= (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
;
113 if (grub_le_to_cpu16 (lh
.version
) >= 0x0201)
115 lh
.heap_end_ptr
= grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET
);
116 lh
.loadflags
|= GRUB_LINUX_FLAG_CAN_USE_HEAP
;
119 if (grub_le_to_cpu16 (lh
.version
) >= 0x0202)
120 lh
.cmd_line_ptr
= grub_linux_real_addr
+ GRUB_LINUX_CL_OFFSET
;
123 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
124 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
125 lh
.setup_move_size
= grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE
);
130 /* Your kernel is quite old... */
131 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
132 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
134 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
136 grub_linux_real_addr
= (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
;
139 /* If SETUP_SECTS is not set, set it to the default (4). */
141 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
143 real_size
= setup_sects
<< GRUB_DISK_SECTOR_BITS
;
144 prot_size
= grub_file_size (file
) - real_size
- GRUB_DISK_SECTOR_SIZE
;
146 grub_linux_tmp_addr
= (char *) GRUB_LINUX_BZIMAGE_ADDR
+ prot_size
;
148 if (! grub_linux_is_bzimage
149 && ((char *) GRUB_LINUX_ZIMAGE_ADDR
+ prot_size
150 > (grub_size_t
) grub_linux_real_addr
))
152 grub_error (GRUB_ERR_BAD_OS
, "too big zImage (0x%x > 0x%x), use bzImage instead",
153 (char *) GRUB_LINUX_ZIMAGE_ADDR
+ prot_size
,
154 (grub_size_t
) grub_linux_real_addr
);
158 if (grub_linux_real_addr
+ GRUB_LINUX_SETUP_MOVE_SIZE
159 > (char *) grub_lower_mem
)
161 grub_error (GRUB_ERR_OUT_OF_RANGE
,
162 "too small lower memory (0x%x > 0x%x)",
163 grub_linux_real_addr
+ GRUB_LINUX_SETUP_MOVE_SIZE
,
164 (char *) grub_lower_mem
);
168 grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
169 grub_linux_is_bzimage
? "bzImage" : "zImage", real_size
, prot_size
);
171 for (i
= 1; i
< argc
; i
++)
172 if (grub_memcmp (argv
[i
], "vga=", 4) == 0)
174 /* Video mode selection support. */
175 grub_uint16_t vid_mode
;
176 char *val
= argv
[i
] + 4;
178 if (grub_strcmp (val
, "normal") == 0)
179 vid_mode
= GRUB_LINUX_VID_MODE_NORMAL
;
180 else if (grub_strcmp (val
, "ext") == 0)
181 vid_mode
= GRUB_LINUX_VID_MODE_EXTENDED
;
182 else if (grub_strcmp (val
, "ask") == 0)
183 vid_mode
= GRUB_LINUX_VID_MODE_ASK
;
185 vid_mode
= (grub_uint16_t
) grub_strtoul (val
, 0, 0);
190 lh
.vid_mode
= grub_cpu_to_le16 (vid_mode
);
192 else if (grub_memcmp (argv
[i
], "mem=", 4) == 0)
194 char *val
= argv
[i
] + 4;
196 linux_mem_size
= grub_strtoul (val
, &val
, 0);
200 grub_errno
= GRUB_ERR_NONE
;
207 switch (grub_tolower (val
[0]))
219 /* Check an overflow. */
220 if (linux_mem_size
> (~0UL >> shift
))
223 linux_mem_size
<<= shift
;
227 /* Put the real mode code at the temporary address. */
228 grub_memmove (grub_linux_tmp_addr
, &lh
, sizeof (lh
));
230 len
= real_size
+ GRUB_DISK_SECTOR_SIZE
- sizeof (lh
);
231 if (grub_file_read (file
, grub_linux_tmp_addr
+ sizeof (lh
), len
) != len
)
233 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
237 if (lh
.header
!= grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
238 || grub_le_to_cpu16 (lh
.version
) < 0x0200)
239 /* Clear the heap space. */
240 grub_memset (grub_linux_tmp_addr
241 + ((setup_sects
+ 1) << GRUB_DISK_SECTOR_BITS
),
243 ((GRUB_LINUX_MAX_SETUP_SECTS
- setup_sects
- 1)
244 << GRUB_DISK_SECTOR_BITS
));
246 /* Specify the boot file. */
247 dest
= grub_stpcpy (grub_linux_tmp_addr
+ GRUB_LINUX_CL_OFFSET
,
249 dest
= grub_stpcpy (dest
, argv
[0]);
251 /* Copy kernel parameters. */
254 && dest
+ grub_strlen (argv
[i
]) + 1 < (grub_linux_tmp_addr
255 + GRUB_LINUX_CL_END_OFFSET
);
259 dest
= grub_stpcpy (dest
, argv
[i
]);
263 if (grub_file_read (file
, (char *) GRUB_LINUX_BZIMAGE_ADDR
, len
) != len
)
264 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
266 if (grub_errno
== GRUB_ERR_NONE
)
268 grub_linux_prot_size
= prot_size
;
269 grub_loader_set (grub_linux_boot
, grub_linux_unload
, 1);
276 grub_file_close (file
);
278 if (grub_errno
!= GRUB_ERR_NONE
)
280 grub_dl_unref (my_mod
);
286 grub_rescue_cmd_initrd (int argc
, char *argv
[])
288 grub_file_t file
= 0;
290 grub_addr_t addr_max
, addr_min
, addr
;
291 struct linux_kernel_header
*lh
;
295 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module specified");
301 grub_error (GRUB_ERR_BAD_ARGUMENT
, "You need to load the kernel first.");
305 lh
= (struct linux_kernel_header
*) grub_linux_tmp_addr
;
307 if (!(lh
->header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
308 && grub_le_to_cpu16 (lh
->version
) >= 0x0200))
310 grub_error (GRUB_ERR_BAD_OS
, "The kernel is too old for initrd.");
314 /* Get the highest address available for the initrd. */
315 if (grub_le_to_cpu16 (lh
->version
) >= 0x0203)
317 addr_max
= grub_cpu_to_le32 (lh
->initrd_addr_max
);
319 /* XXX in reality, Linux specifies a bogus value, so
320 it is necessary to make sure that ADDR_MAX does not exceed
322 if (addr_max
> GRUB_LINUX_INITRD_MAX_ADDRESS
)
323 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
326 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
328 if (linux_mem_size
!= 0 && linux_mem_size
< addr_max
)
329 addr_max
= linux_mem_size
;
331 /* Linux 2.3.xx has a bug in the memory range check, so avoid
333 Linux 2.2.xx has a bug in the memory range check, which is
334 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
337 if (addr_max
> grub_os_area_addr
+ grub_os_area_size
)
338 addr_max
= grub_os_area_addr
+ grub_os_area_size
;
340 addr_min
= (grub_addr_t
) grub_linux_tmp_addr
+ GRUB_LINUX_CL_END_OFFSET
;
342 file
= grub_file_open (argv
[0]);
346 size
= grub_file_size (file
);
348 /* Put the initrd as high as possible, 4Ki aligned. */
349 addr
= (addr_max
- size
) & ~0xFFF;
353 grub_error (GRUB_ERR_OUT_OF_RANGE
, "The initrd is too big");
357 if (grub_file_read (file
, (void *)addr
, size
) != size
)
359 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
363 lh
->ramdisk_image
= addr
;
364 lh
->ramdisk_size
= size
;
368 grub_file_close (file
);
374 grub_rescue_register_command ("linux",
375 grub_rescue_cmd_linux
,
377 grub_rescue_register_command ("initrd",
378 grub_rescue_cmd_initrd
,
385 grub_rescue_unregister_command ("linux");
386 grub_rescue_unregister_command ("initrd");