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,2009,2010 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>
31 #include <grub/cpu/linux.h>
32 #include <grub/command.h>
33 #include <grub/i18n.h>
35 #include <grub/video.h>
37 #define GRUB_LINUX_CL_OFFSET 0x9000
38 #define GRUB_LINUX_CL_END_OFFSET 0x90FF
40 static grub_dl_t my_mod
;
42 static grub_size_t linux_mem_size
;
46 grub_linux_unload (void)
48 grub_dl_unref (my_mod
);
54 grub_linux16_boot (void)
56 grub_video_set_mode ("text", 0, 0);
57 grub_linux16_real_boot ();
64 grub_cmd_linux (grub_command_t cmd
__attribute__ ((unused
)),
65 int argc
, char *argv
[])
68 struct linux_kernel_header lh
;
69 grub_uint8_t setup_sects
;
70 grub_size_t real_size
, prot_size
;
79 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no kernel specified");
83 file
= grub_file_open (argv
[0]);
87 if ((grub_size_t
) grub_file_size (file
) > grub_os_area_size
)
89 grub_error (GRUB_ERR_OUT_OF_RANGE
, "too big kernel (0x%x > 0x%x)",
90 (grub_size_t
) grub_file_size (file
),
95 if (grub_file_read (file
, &lh
, sizeof (lh
)) != sizeof (lh
))
97 grub_error (GRUB_ERR_READ_ERROR
, "cannot read the Linux header");
101 if (lh
.boot_flag
!= grub_cpu_to_le16 (0xaa55))
103 grub_error (GRUB_ERR_BAD_OS
, "invalid magic number");
107 if (lh
.setup_sects
> GRUB_LINUX_MAX_SETUP_SECTS
)
109 grub_error (GRUB_ERR_BAD_OS
, "too many setup sectors");
113 grub_linux_is_bzimage
= 0;
114 setup_sects
= lh
.setup_sects
;
117 if (lh
.header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
118 && grub_le_to_cpu16 (lh
.version
) >= 0x0200)
120 grub_linux_is_bzimage
= (lh
.loadflags
& GRUB_LINUX_FLAG_BIG_KERNEL
);
121 lh
.type_of_loader
= GRUB_LINUX_BOOT_LOADER_TYPE
;
123 /* Put the real mode part at as a high location as possible. */
125 = (char *) UINT_TO_PTR (grub_mmap_get_lower ()
126 - GRUB_LINUX_SETUP_MOVE_SIZE
);
127 /* But it must not exceed the traditional area. */
128 if (grub_linux_real_addr
> (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
)
129 grub_linux_real_addr
= (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
;
131 if (grub_le_to_cpu16 (lh
.version
) >= 0x0201)
133 lh
.heap_end_ptr
= grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET
);
134 lh
.loadflags
|= GRUB_LINUX_FLAG_CAN_USE_HEAP
;
137 if (grub_le_to_cpu16 (lh
.version
) >= 0x0202)
138 lh
.cmd_line_ptr
= grub_linux_real_addr
+ GRUB_LINUX_CL_OFFSET
;
141 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
142 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
143 lh
.setup_move_size
= grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE
);
148 /* Your kernel is quite old... */
149 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
150 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
152 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
154 grub_linux_real_addr
= (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
;
157 /* If SETUP_SECTS is not set, set it to the default (4). */
159 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
161 real_size
= setup_sects
<< GRUB_DISK_SECTOR_BITS
;
162 prot_size
= grub_file_size (file
) - real_size
- GRUB_DISK_SECTOR_SIZE
;
164 grub_linux_tmp_addr
= (char *) GRUB_LINUX_BZIMAGE_ADDR
+ prot_size
;
166 if (! grub_linux_is_bzimage
167 && ((char *) GRUB_LINUX_ZIMAGE_ADDR
+ prot_size
> grub_linux_real_addr
))
169 grub_error (GRUB_ERR_BAD_OS
, "too big zImage (0x%x > 0x%x), use bzImage instead",
170 (char *) GRUB_LINUX_ZIMAGE_ADDR
+ prot_size
,
171 (grub_size_t
) grub_linux_real_addr
);
175 if (grub_linux_real_addr
+ GRUB_LINUX_SETUP_MOVE_SIZE
176 > (char *) UINT_TO_PTR (grub_mmap_get_lower ()))
178 grub_error (GRUB_ERR_OUT_OF_RANGE
,
179 "too small lower memory (0x%x > 0x%x)",
180 grub_linux_real_addr
+ GRUB_LINUX_SETUP_MOVE_SIZE
,
181 (int) grub_mmap_get_lower ());
185 grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
186 grub_linux_is_bzimage
? "bzImage" : "zImage", real_size
, prot_size
);
188 for (i
= 1; i
< argc
; i
++)
189 if (grub_memcmp (argv
[i
], "vga=", 4) == 0)
191 /* Video mode selection support. */
192 grub_uint16_t vid_mode
;
193 char *val
= argv
[i
] + 4;
195 if (grub_strcmp (val
, "normal") == 0)
196 vid_mode
= GRUB_LINUX_VID_MODE_NORMAL
;
197 else if (grub_strcmp (val
, "ext") == 0)
198 vid_mode
= GRUB_LINUX_VID_MODE_EXTENDED
;
199 else if (grub_strcmp (val
, "ask") == 0)
200 vid_mode
= GRUB_LINUX_VID_MODE_ASK
;
202 vid_mode
= (grub_uint16_t
) grub_strtoul (val
, 0, 0);
207 lh
.vid_mode
= grub_cpu_to_le16 (vid_mode
);
209 else if (grub_memcmp (argv
[i
], "mem=", 4) == 0)
211 char *val
= argv
[i
] + 4;
213 linux_mem_size
= grub_strtoul (val
, &val
, 0);
217 grub_errno
= GRUB_ERR_NONE
;
224 switch (grub_tolower (val
[0]))
236 /* Check an overflow. */
237 if (linux_mem_size
> (~0UL >> shift
))
240 linux_mem_size
<<= shift
;
244 /* Put the real mode code at the temporary address. */
245 grub_memmove (grub_linux_tmp_addr
, &lh
, sizeof (lh
));
247 len
= real_size
+ GRUB_DISK_SECTOR_SIZE
- sizeof (lh
);
248 if (grub_file_read (file
, grub_linux_tmp_addr
+ sizeof (lh
), len
) != len
)
250 grub_error (GRUB_ERR_FILE_READ_ERROR
, "couldn't read file");
254 if (lh
.header
!= grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
255 || grub_le_to_cpu16 (lh
.version
) < 0x0200)
256 /* Clear the heap space. */
257 grub_memset (grub_linux_tmp_addr
258 + ((setup_sects
+ 1) << GRUB_DISK_SECTOR_BITS
),
260 ((GRUB_LINUX_MAX_SETUP_SECTS
- setup_sects
- 1)
261 << GRUB_DISK_SECTOR_BITS
));
263 /* Specify the boot file. */
264 dest
= grub_stpcpy (grub_linux_tmp_addr
+ GRUB_LINUX_CL_OFFSET
,
266 dest
= grub_stpcpy (dest
, argv
[0]);
268 /* Copy kernel parameters. */
271 && dest
+ grub_strlen (argv
[i
]) + 1 < (grub_linux_tmp_addr
272 + GRUB_LINUX_CL_END_OFFSET
);
276 dest
= grub_stpcpy (dest
, argv
[i
]);
280 if (grub_file_read (file
, (void *) GRUB_LINUX_BZIMAGE_ADDR
, len
) != len
)
281 grub_error (GRUB_ERR_FILE_READ_ERROR
, "couldn't read file");
283 if (grub_errno
== GRUB_ERR_NONE
)
285 grub_linux_prot_size
= prot_size
;
286 grub_loader_set (grub_linux16_boot
, grub_linux_unload
, 1);
293 grub_file_close (file
);
295 if (grub_errno
!= GRUB_ERR_NONE
)
297 grub_dl_unref (my_mod
);
305 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
306 int argc
, char *argv
[])
308 grub_file_t file
= 0;
310 grub_addr_t addr_max
, addr_min
, addr
;
311 struct linux_kernel_header
*lh
;
315 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no module specified");
321 grub_error (GRUB_ERR_BAD_ARGUMENT
, "you need to load the kernel first");
325 lh
= (struct linux_kernel_header
*) grub_linux_tmp_addr
;
327 if (!(lh
->header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
328 && grub_le_to_cpu16 (lh
->version
) >= 0x0200))
330 grub_error (GRUB_ERR_BAD_OS
, "the kernel is too old for initrd");
334 /* Get the highest address available for the initrd. */
335 if (grub_le_to_cpu16 (lh
->version
) >= 0x0203)
337 addr_max
= grub_cpu_to_le32 (lh
->initrd_addr_max
);
339 /* XXX in reality, Linux specifies a bogus value, so
340 it is necessary to make sure that ADDR_MAX does not exceed
342 if (addr_max
> GRUB_LINUX_INITRD_MAX_ADDRESS
)
343 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
346 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
348 if (linux_mem_size
!= 0 && linux_mem_size
< addr_max
)
349 addr_max
= linux_mem_size
;
351 /* Linux 2.3.xx has a bug in the memory range check, so avoid
353 Linux 2.2.xx has a bug in the memory range check, which is
354 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
357 if (addr_max
> grub_os_area_addr
+ grub_os_area_size
)
358 addr_max
= grub_os_area_addr
+ grub_os_area_size
;
360 addr_min
= (grub_addr_t
) grub_linux_tmp_addr
+ GRUB_LINUX_CL_END_OFFSET
;
362 file
= grub_file_open (argv
[0]);
366 size
= grub_file_size (file
);
368 /* Put the initrd as high as possible, 4KiB aligned. */
369 addr
= (addr_max
- size
) & ~0xFFF;
373 grub_error (GRUB_ERR_OUT_OF_RANGE
, "the initrd is too big");
377 if (grub_file_read (file
, (void *) addr
, size
) != size
)
379 grub_error (GRUB_ERR_FILE_READ_ERROR
, "couldn't read file");
383 lh
->ramdisk_image
= addr
;
384 lh
->ramdisk_size
= size
;
388 grub_file_close (file
);
393 static grub_command_t cmd_linux
, cmd_initrd
;
395 GRUB_MOD_INIT(linux16
)
398 grub_register_command ("linux16", grub_cmd_linux
,
399 0, N_("Load Linux."));
401 grub_register_command ("initrd16", grub_cmd_initrd
,
402 0, N_("Load initrd."));
406 GRUB_MOD_FINI(linux16
)
408 grub_unregister_command (cmd_linux
);
409 grub_unregister_command (cmd_initrd
);