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/file.h>
23 #include <grub/device.h>
24 #include <grub/disk.h>
25 #include <grub/misc.h>
26 #include <grub/types.h>
27 #include <grub/memory.h>
29 #include <grub/cpu/linux.h>
30 #include <grub/command.h>
31 #include <grub/i18n.h>
33 #include <grub/cpu/relocator.h>
34 #include <grub/video.h>
35 #include <grub/i386/floppy.h>
36 #include <grub/lib/cmdline.h>
38 GRUB_MOD_LICENSE ("GPLv3+");
40 #define GRUB_LINUX_CL_OFFSET 0x9000
42 static grub_dl_t my_mod
;
44 static grub_size_t linux_mem_size
;
46 static struct grub_relocator
*relocator
= NULL
;
47 static grub_addr_t grub_linux_real_target
;
48 static char *grub_linux_real_chunk
;
49 static grub_size_t grub_linux16_prot_size
;
50 static grub_size_t maximal_cmdline_size
;
53 grub_linux16_boot (void)
55 grub_uint16_t segment
;
56 struct grub_relocator16_state state
;
58 segment
= grub_linux_real_target
>> 4;
59 state
.gs
= state
.fs
= state
.es
= state
.ds
= state
.ss
= segment
;
60 state
.sp
= GRUB_LINUX_SETUP_STACK
;
61 state
.cs
= segment
+ 0x20;
65 grub_video_set_mode ("text", 0, 0);
69 return grub_relocator16_boot (relocator
, state
);
73 grub_linux_unload (void)
75 grub_dl_unref (my_mod
);
77 grub_relocator_unload (relocator
);
83 grub_cmd_linux (grub_command_t cmd
__attribute__ ((unused
)),
84 int argc
, char *argv
[])
87 struct linux_kernel_header lh
;
88 grub_uint8_t setup_sects
;
89 grub_size_t real_size
;
92 char *grub_linux_prot_chunk
;
93 int grub_linux_is_bzimage
;
94 grub_addr_t grub_linux_prot_target
;
101 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
105 file
= grub_file_open (argv
[0]);
109 if (grub_file_read (file
, &lh
, sizeof (lh
)) != sizeof (lh
))
112 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"),
117 if (lh
.boot_flag
!= grub_cpu_to_le16 (0xaa55))
119 grub_error (GRUB_ERR_BAD_OS
, "invalid magic number");
123 if (lh
.setup_sects
> GRUB_LINUX_MAX_SETUP_SECTS
)
125 grub_error (GRUB_ERR_BAD_OS
, "too many setup sectors");
129 grub_linux_is_bzimage
= 0;
130 setup_sects
= lh
.setup_sects
;
133 maximal_cmdline_size
= 256;
135 if (lh
.header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
136 && grub_le_to_cpu16 (lh
.version
) >= 0x0200)
138 grub_linux_is_bzimage
= (lh
.loadflags
& GRUB_LINUX_FLAG_BIG_KERNEL
);
139 lh
.type_of_loader
= GRUB_LINUX_BOOT_LOADER_TYPE
;
141 if (grub_le_to_cpu16 (lh
.version
) >= 0x0206)
142 maximal_cmdline_size
= grub_le_to_cpu32 (lh
.cmdline_size
) + 1;
144 /* Put the real mode part at as a high location as possible. */
145 grub_linux_real_target
= grub_mmap_get_lower ()
146 - (GRUB_LINUX_CL_OFFSET
+ maximal_cmdline_size
);
147 /* But it must not exceed the traditional area. */
148 if (grub_linux_real_target
> GRUB_LINUX_OLD_REAL_MODE_ADDR
)
149 grub_linux_real_target
= GRUB_LINUX_OLD_REAL_MODE_ADDR
;
151 if (grub_le_to_cpu16 (lh
.version
) >= 0x0201)
153 lh
.heap_end_ptr
= grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET
);
154 lh
.loadflags
|= GRUB_LINUX_FLAG_CAN_USE_HEAP
;
157 if (grub_le_to_cpu16 (lh
.version
) >= 0x0202)
158 lh
.cmd_line_ptr
= grub_linux_real_target
+ GRUB_LINUX_CL_OFFSET
;
161 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
162 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
163 lh
.setup_move_size
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
164 + maximal_cmdline_size
);
169 /* Your kernel is quite old... */
170 lh
.cl_magic
= grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC
);
171 lh
.cl_offset
= grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET
);
173 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
175 grub_linux_real_target
= GRUB_LINUX_OLD_REAL_MODE_ADDR
;
178 /* If SETUP_SECTS is not set, set it to the default (4). */
180 setup_sects
= GRUB_LINUX_DEFAULT_SETUP_SECTS
;
182 real_size
= setup_sects
<< GRUB_DISK_SECTOR_BITS
;
183 grub_linux16_prot_size
= grub_file_size (file
)
184 - real_size
- GRUB_DISK_SECTOR_SIZE
;
186 if (! grub_linux_is_bzimage
187 && GRUB_LINUX_ZIMAGE_ADDR
+ grub_linux16_prot_size
188 > grub_linux_real_target
)
190 grub_error (GRUB_ERR_BAD_OS
, "too big zImage (0x%x > 0x%x), use bzImage instead",
191 (char *) GRUB_LINUX_ZIMAGE_ADDR
+ grub_linux16_prot_size
,
192 (grub_size_t
) grub_linux_real_target
);
196 if (grub_linux_real_target
+ GRUB_LINUX_CL_OFFSET
+ maximal_cmdline_size
197 > grub_mmap_get_lower ())
199 grub_error (GRUB_ERR_OUT_OF_RANGE
,
200 "too small lower memory (0x%x > 0x%x)",
201 grub_linux_real_target
+ GRUB_LINUX_CL_OFFSET
202 + maximal_cmdline_size
,
203 (int) grub_mmap_get_lower ());
207 grub_dprintf ("linux", "[Linux-%s, setup=0x%x, size=0x%x]\n",
208 grub_linux_is_bzimage
? "bzImage" : "zImage", real_size
,
209 grub_linux16_prot_size
);
211 relocator
= grub_relocator_new ();
215 for (i
= 1; i
< argc
; i
++)
216 if (grub_memcmp (argv
[i
], "vga=", 4) == 0)
218 /* Video mode selection support. */
219 grub_uint16_t vid_mode
;
220 char *val
= argv
[i
] + 4;
222 if (grub_strcmp (val
, "normal") == 0)
223 vid_mode
= GRUB_LINUX_VID_MODE_NORMAL
;
224 else if (grub_strcmp (val
, "ext") == 0)
225 vid_mode
= GRUB_LINUX_VID_MODE_EXTENDED
;
226 else if (grub_strcmp (val
, "ask") == 0)
227 vid_mode
= GRUB_LINUX_VID_MODE_ASK
;
229 vid_mode
= (grub_uint16_t
) grub_strtoul (val
, 0, 0);
234 lh
.vid_mode
= grub_cpu_to_le16 (vid_mode
);
236 else if (grub_memcmp (argv
[i
], "mem=", 4) == 0)
238 char *val
= argv
[i
] + 4;
240 linux_mem_size
= grub_strtoul (val
, &val
, 0);
244 grub_errno
= GRUB_ERR_NONE
;
251 switch (grub_tolower (val
[0]))
263 /* Check an overflow. */
264 if (linux_mem_size
> (~0UL >> shift
))
267 linux_mem_size
<<= shift
;
272 grub_relocator_chunk_t ch
;
273 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
274 grub_linux_real_target
,
276 + maximal_cmdline_size
);
279 grub_linux_real_chunk
= get_virtual_current_address (ch
);
282 /* Put the real mode code at the temporary address. */
283 grub_memmove (grub_linux_real_chunk
, &lh
, sizeof (lh
));
285 len
= real_size
+ GRUB_DISK_SECTOR_SIZE
- sizeof (lh
);
286 if (grub_file_read (file
, grub_linux_real_chunk
+ sizeof (lh
), len
) != len
)
289 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"),
294 if (lh
.header
!= grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
295 || grub_le_to_cpu16 (lh
.version
) < 0x0200)
296 /* Clear the heap space. */
297 grub_memset (grub_linux_real_chunk
298 + ((setup_sects
+ 1) << GRUB_DISK_SECTOR_BITS
),
300 ((GRUB_LINUX_MAX_SETUP_SECTS
- setup_sects
- 1)
301 << GRUB_DISK_SECTOR_BITS
));
303 /* Create kernel command line. */
304 grub_memcpy ((char *)grub_linux_real_chunk
+ GRUB_LINUX_CL_OFFSET
,
305 LINUX_IMAGE
, sizeof (LINUX_IMAGE
));
306 grub_create_loader_cmdline (argc
, argv
,
307 (char *)grub_linux_real_chunk
308 + GRUB_LINUX_CL_OFFSET
+ sizeof (LINUX_IMAGE
) - 1,
310 - (sizeof (LINUX_IMAGE
) - 1));
312 if (grub_linux_is_bzimage
)
313 grub_linux_prot_target
= GRUB_LINUX_BZIMAGE_ADDR
;
315 grub_linux_prot_target
= GRUB_LINUX_ZIMAGE_ADDR
;
317 grub_relocator_chunk_t ch
;
318 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
319 grub_linux_prot_target
,
320 grub_linux16_prot_size
);
323 grub_linux_prot_chunk
= get_virtual_current_address (ch
);
326 len
= grub_linux16_prot_size
;
327 if (grub_file_read (file
, grub_linux_prot_chunk
, grub_linux16_prot_size
)
328 != (grub_ssize_t
) grub_linux16_prot_size
&& !grub_errno
)
329 grub_error (GRUB_ERR_BAD_OS
, N_("premature end of file %s"),
332 if (grub_errno
== GRUB_ERR_NONE
)
334 grub_loader_set (grub_linux16_boot
, grub_linux_unload
, 0);
341 grub_file_close (file
);
343 if (grub_errno
!= GRUB_ERR_NONE
)
345 grub_dl_unref (my_mod
);
347 grub_relocator_unload (relocator
);
354 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
355 int argc
, char *argv
[])
357 grub_file_t
*files
= 0;
358 grub_size_t size
= 0;
359 grub_addr_t addr_max
, addr_min
;
360 struct linux_kernel_header
*lh
;
361 grub_uint8_t
*initrd_chunk
;
362 grub_addr_t initrd_addr
;
369 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
375 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("you need to load the kernel first"));
379 lh
= (struct linux_kernel_header
*) grub_linux_real_chunk
;
381 if (!(lh
->header
== grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE
)
382 && grub_le_to_cpu16 (lh
->version
) >= 0x0200))
384 grub_error (GRUB_ERR_BAD_OS
, "the kernel is too old for initrd");
388 /* Get the highest address available for the initrd. */
389 if (grub_le_to_cpu16 (lh
->version
) >= 0x0203)
391 addr_max
= grub_cpu_to_le32 (lh
->initrd_addr_max
);
393 /* XXX in reality, Linux specifies a bogus value, so
394 it is necessary to make sure that ADDR_MAX does not exceed
396 if (addr_max
> GRUB_LINUX_INITRD_MAX_ADDRESS
)
397 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
400 addr_max
= GRUB_LINUX_INITRD_MAX_ADDRESS
;
402 if (linux_mem_size
!= 0 && linux_mem_size
< addr_max
)
403 addr_max
= linux_mem_size
;
405 /* Linux 2.3.xx has a bug in the memory range check, so avoid
407 Linux 2.2.xx has a bug in the memory range check, which is
408 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
411 addr_min
= GRUB_LINUX_BZIMAGE_ADDR
+ grub_linux16_prot_size
;
413 files
= grub_zalloc (argc
* sizeof (files
[0]));
417 for (i
= 0; i
< argc
; i
++)
419 grub_file_filter_disable_compression ();
420 files
[i
] = grub_file_open (argv
[i
]);
424 size
+= ALIGN_UP (grub_file_size (files
[i
]), 4);
428 grub_relocator_chunk_t ch
;
429 err
= grub_relocator_alloc_chunk_align (relocator
, &ch
,
430 addr_min
, addr_max
- size
,
432 GRUB_RELOCATOR_PREFERENCE_HIGH
, 0);
435 initrd_chunk
= get_virtual_current_address (ch
);
436 initrd_addr
= get_physical_target_address (ch
);
441 for (i
= 0; i
< nfiles
; i
++)
443 grub_ssize_t cursize
= grub_file_size (files
[i
]);
444 if (grub_file_read (files
[i
], ptr
, cursize
) != cursize
)
447 grub_error (GRUB_ERR_FILE_READ_ERROR
, N_("premature end of file %s"),
452 grub_memset (ptr
, 0, ALIGN_UP_OVERHEAD (cursize
, 4));
453 ptr
+= ALIGN_UP_OVERHEAD (cursize
, 4);
456 lh
->ramdisk_image
= initrd_addr
;
457 lh
->ramdisk_size
= size
;
460 for (i
= 0; i
< nfiles
; i
++)
461 grub_file_close (files
[i
]);
467 static grub_command_t cmd_linux
, cmd_initrd
;
469 GRUB_MOD_INIT(linux16
)
472 grub_register_command ("linux16", grub_cmd_linux
,
473 0, N_("Load Linux."));
475 grub_register_command ("initrd16", grub_cmd_initrd
,
476 0, N_("Load initrd."));
480 GRUB_MOD_FINI(linux16
)
482 grub_unregister_command (cmd_linux
);
483 grub_unregister_command (cmd_initrd
);