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>
31 #include <grub/cpu/linux.h>
32 #include <grub/command.h>
34 #define GRUB_LINUX_CL_OFFSET 0x9000
35 #define GRUB_LINUX_CL_END_OFFSET 0x90FF
37 static grub_dl_t my_mod
;
39 static grub_size_t linux_mem_size
;
43 grub_linux_unload (void)
45 grub_dl_unref (my_mod
);
51 grub_cmd_linux (grub_command_t cmd
__attribute__ ((unused
)),
52 int argc
, char *argv
[])
55 struct linux_kernel_header lh
;
56 grub_uint8_t setup_sects
;
57 grub_size_t real_size
, prot_size
;
66 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no kernel specified");
70 file
= grub_file_open (argv
[0]);
74 if ((grub_size_t
) grub_file_size (file
) > grub_os_area_size
)
76 grub_error (GRUB_ERR_OUT_OF_RANGE
, "too big kernel (0x%x > 0x%x)",
77 (grub_size_t
) grub_file_size (file
),
82 if (grub_file_read (file
, (char *) &lh
, sizeof (lh
)) != sizeof (lh
))
84 grub_error (GRUB_ERR_READ_ERROR
, "cannot read the linux header");
88 if (lh
.boot_flag
!= grub_cpu_to_le16 (0xaa55))
90 grub_error (GRUB_ERR_BAD_OS
, "invalid magic number");
94 if (lh
.setup_sects
> GRUB_LINUX_MAX_SETUP_SECTS
)
96 grub_error (GRUB_ERR_BAD_OS
, "too many setup sectors");
100 grub_linux_is_bzimage
= 0;
101 setup_sects
= lh
.setup_sects
;
104 if (lh
.header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
105 && grub_le_to_cpu16 (lh
.version
) >= 0x0200)
107 grub_linux_is_bzimage
= (lh
.loadflags
& GRUB_LINUX_FLAG_BIG_KERNEL
);
108 lh
.type_of_loader
= GRUB_LINUX_BOOT_LOADER_TYPE
;
110 /* Put the real mode part at as a high location as possible. */
112 = (char *) UINT_TO_PTR (grub_mmap_get_lower ()
113 - GRUB_LINUX_SETUP_MOVE_SIZE
);
114 /* But it must not exceed the traditional area. */
115 if (grub_linux_real_addr
> (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
)
116 grub_linux_real_addr
= (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
;
118 if (grub_le_to_cpu16 (lh
.version
) >= 0x0201)
120 lh
.heap_end_ptr
= grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET
);
121 lh
.loadflags
|= GRUB_LINUX_FLAG_CAN_USE_HEAP
;
124 if (grub_le_to_cpu16 (lh
.version
) >= 0x0202)
125 lh
.cmd_line_ptr
= grub_linux_real_addr
+ GRUB_LINUX_CL_OFFSET
;
128 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
129 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
130 lh
.setup_move_size
= grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE
);
135 /* Your kernel is quite old... */
136 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
137 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
139 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
141 grub_linux_real_addr
= (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR
;
144 /* If SETUP_SECTS is not set, set it to the default (4). */
146 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
148 real_size
= setup_sects
<< GRUB_DISK_SECTOR_BITS
;
149 prot_size
= grub_file_size (file
) - real_size
- GRUB_DISK_SECTOR_SIZE
;
151 grub_linux_tmp_addr
= (char *) GRUB_LINUX_BZIMAGE_ADDR
+ prot_size
;
153 if (! grub_linux_is_bzimage
154 && ((char *) GRUB_LINUX_ZIMAGE_ADDR
+ prot_size
> grub_linux_real_addr
))
156 grub_error (GRUB_ERR_BAD_OS
, "too big zImage (0x%x > 0x%x), use bzImage instead",
157 (char *) GRUB_LINUX_ZIMAGE_ADDR
+ prot_size
,
158 (grub_size_t
) grub_linux_real_addr
);
162 if (grub_linux_real_addr
+ GRUB_LINUX_SETUP_MOVE_SIZE
163 > (char *) UINT_TO_PTR (grub_mmap_get_lower ()))
165 grub_error (GRUB_ERR_OUT_OF_RANGE
,
166 "too small lower memory (0x%x > 0x%x)",
167 grub_linux_real_addr
+ GRUB_LINUX_SETUP_MOVE_SIZE
,
168 (int) grub_mmap_get_lower ());
172 grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
173 grub_linux_is_bzimage
? "bzImage" : "zImage", real_size
, prot_size
);
175 for (i
= 1; i
< argc
; i
++)
176 if (grub_memcmp (argv
[i
], "vga=", 4) == 0)
178 /* Video mode selection support. */
179 grub_uint16_t vid_mode
;
180 char *val
= argv
[i
] + 4;
182 if (grub_strcmp (val
, "normal") == 0)
183 vid_mode
= GRUB_LINUX_VID_MODE_NORMAL
;
184 else if (grub_strcmp (val
, "ext") == 0)
185 vid_mode
= GRUB_LINUX_VID_MODE_EXTENDED
;
186 else if (grub_strcmp (val
, "ask") == 0)
187 vid_mode
= GRUB_LINUX_VID_MODE_ASK
;
189 vid_mode
= (grub_uint16_t
) grub_strtoul (val
, 0, 0);
194 lh
.vid_mode
= grub_cpu_to_le16 (vid_mode
);
196 else if (grub_memcmp (argv
[i
], "mem=", 4) == 0)
198 char *val
= argv
[i
] + 4;
200 linux_mem_size
= grub_strtoul (val
, &val
, 0);
204 grub_errno
= GRUB_ERR_NONE
;
211 switch (grub_tolower (val
[0]))
223 /* Check an overflow. */
224 if (linux_mem_size
> (~0UL >> shift
))
227 linux_mem_size
<<= shift
;
231 /* Put the real mode code at the temporary address. */
232 grub_memmove (grub_linux_tmp_addr
, &lh
, sizeof (lh
));
234 len
= real_size
+ GRUB_DISK_SECTOR_SIZE
- sizeof (lh
);
235 if (grub_file_read (file
, grub_linux_tmp_addr
+ sizeof (lh
), len
) != len
)
237 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
241 if (lh
.header
!= grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
242 || grub_le_to_cpu16 (lh
.version
) < 0x0200)
243 /* Clear the heap space. */
244 grub_memset (grub_linux_tmp_addr
245 + ((setup_sects
+ 1) << GRUB_DISK_SECTOR_BITS
),
247 ((GRUB_LINUX_MAX_SETUP_SECTS
- setup_sects
- 1)
248 << GRUB_DISK_SECTOR_BITS
));
250 /* Specify the boot file. */
251 dest
= grub_stpcpy (grub_linux_tmp_addr
+ GRUB_LINUX_CL_OFFSET
,
253 dest
= grub_stpcpy (dest
, argv
[0]);
255 /* Copy kernel parameters. */
258 && dest
+ grub_strlen (argv
[i
]) + 1 < (grub_linux_tmp_addr
259 + GRUB_LINUX_CL_END_OFFSET
);
263 dest
= grub_stpcpy (dest
, argv
[i
]);
267 if (grub_file_read (file
, (char *) GRUB_LINUX_BZIMAGE_ADDR
, len
) != len
)
268 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
270 if (grub_errno
== GRUB_ERR_NONE
)
272 grub_linux_prot_size
= prot_size
;
273 grub_loader_set (grub_linux16_boot
, grub_linux_unload
, 1);
280 grub_file_close (file
);
282 if (grub_errno
!= GRUB_ERR_NONE
)
284 grub_dl_unref (my_mod
);
292 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
293 int argc
, char *argv
[])
295 grub_file_t file
= 0;
297 grub_addr_t addr_max
, addr_min
, addr
;
298 struct linux_kernel_header
*lh
;
302 grub_error (GRUB_ERR_BAD_ARGUMENT
, "No module specified");
308 grub_error (GRUB_ERR_BAD_ARGUMENT
, "You need to load the kernel first.");
312 lh
= (struct linux_kernel_header
*) grub_linux_tmp_addr
;
314 if (!(lh
->header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
315 && grub_le_to_cpu16 (lh
->version
) >= 0x0200))
317 grub_error (GRUB_ERR_BAD_OS
, "The kernel is too old for initrd.");
321 /* Get the highest address available for the initrd. */
322 if (grub_le_to_cpu16 (lh
->version
) >= 0x0203)
324 addr_max
= grub_cpu_to_le32 (lh
->initrd_addr_max
);
326 /* XXX in reality, Linux specifies a bogus value, so
327 it is necessary to make sure that ADDR_MAX does not exceed
329 if (addr_max
> GRUB_LINUX_INITRD_MAX_ADDRESS
)
330 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
333 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
335 if (linux_mem_size
!= 0 && linux_mem_size
< addr_max
)
336 addr_max
= linux_mem_size
;
338 /* Linux 2.3.xx has a bug in the memory range check, so avoid
340 Linux 2.2.xx has a bug in the memory range check, which is
341 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
344 if (addr_max
> grub_os_area_addr
+ grub_os_area_size
)
345 addr_max
= grub_os_area_addr
+ grub_os_area_size
;
347 addr_min
= (grub_addr_t
) grub_linux_tmp_addr
+ GRUB_LINUX_CL_END_OFFSET
;
349 file
= grub_file_open (argv
[0]);
353 size
= grub_file_size (file
);
355 /* Put the initrd as high as possible, 4KiB aligned. */
356 addr
= (addr_max
- size
) & ~0xFFF;
360 grub_error (GRUB_ERR_OUT_OF_RANGE
, "The initrd is too big");
364 if (grub_file_read (file
, (void *)addr
, size
) != size
)
366 grub_error (GRUB_ERR_FILE_READ_ERROR
, "Couldn't read file");
370 lh
->ramdisk_image
= addr
;
371 lh
->ramdisk_size
= size
;
375 grub_file_close (file
);
380 static grub_command_t cmd_linux
, cmd_initrd
;
382 GRUB_MOD_INIT(linux16
)
385 grub_register_command ("linux16", grub_cmd_linux
,
388 grub_register_command ("initrd16", grub_cmd_initrd
,
393 GRUB_MOD_FINI(linux16
)
395 grub_unregister_command (cmd_linux
);
396 grub_unregister_command (cmd_initrd
);