Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / loader / i386 / pc / linux.c
blob4eeb1b6c2faa03bcbbd3fe3ea5488ecef538d3c2
1 /* linux.c - boot Linux zImage or bzImage */
2 /*
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>
22 #include <grub/err.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>
28 #include <grub/dl.h>
29 #include <grub/cpu/linux.h>
30 #include <grub/command.h>
31 #include <grub/i18n.h>
32 #include <grub/mm.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;
45 static int loaded;
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;
52 static grub_err_t
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;
62 state.ip = 0;
63 state.a20 = 1;
65 grub_video_set_mode ("text", 0, 0);
67 grub_stop_floppy ();
69 return grub_relocator16_boot (relocator, state);
72 static grub_err_t
73 grub_linux_unload (void)
75 grub_dl_unref (my_mod);
76 loaded = 0;
77 grub_relocator_unload (relocator);
78 relocator = NULL;
79 return GRUB_ERR_NONE;
82 static grub_err_t
83 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
84 int argc, char *argv[])
86 grub_file_t file = 0;
87 struct linux_kernel_header lh;
88 grub_uint8_t setup_sects;
89 grub_size_t real_size;
90 grub_ssize_t len;
91 int i;
92 char *grub_linux_prot_chunk;
93 int grub_linux_is_bzimage;
94 grub_addr_t grub_linux_prot_target;
95 grub_err_t err;
97 grub_dl_ref (my_mod);
99 if (argc == 0)
101 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
102 goto fail;
105 file = grub_file_open (argv[0]);
106 if (! file)
107 goto fail;
109 if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
111 if (!grub_errno)
112 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
113 argv[0]);
114 goto fail;
117 if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
119 grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
120 goto fail;
123 if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
125 grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
126 goto fail;
129 grub_linux_is_bzimage = 0;
130 setup_sects = lh.setup_sects;
131 linux_mem_size = 0;
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;
159 else
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);
167 else
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). */
179 if (! setup_sects)
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);
193 goto fail;
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 ());
204 goto fail;
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 ();
212 if (!relocator)
213 goto fail;
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;
228 else
229 vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
231 if (grub_errno)
232 goto fail;
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);
242 if (grub_errno)
244 grub_errno = GRUB_ERR_NONE;
245 linux_mem_size = 0;
247 else
249 int shift = 0;
251 switch (grub_tolower (val[0]))
253 case 'g':
254 shift += 10;
255 case 'm':
256 shift += 10;
257 case 'k':
258 shift += 10;
259 default:
260 break;
263 /* Check an overflow. */
264 if (linux_mem_size > (~0UL >> shift))
265 linux_mem_size = 0;
266 else
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,
275 GRUB_LINUX_CL_OFFSET
276 + maximal_cmdline_size);
277 if (err)
278 return err;
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)
288 if (!grub_errno)
289 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
290 argv[0]);
291 goto fail;
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,
309 maximal_cmdline_size
310 - (sizeof (LINUX_IMAGE) - 1));
312 if (grub_linux_is_bzimage)
313 grub_linux_prot_target = GRUB_LINUX_BZIMAGE_ADDR;
314 else
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);
321 if (err)
322 return err;
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"),
330 argv[0]);
332 if (grub_errno == GRUB_ERR_NONE)
334 grub_loader_set (grub_linux16_boot, grub_linux_unload, 0);
335 loaded = 1;
338 fail:
340 if (file)
341 grub_file_close (file);
343 if (grub_errno != GRUB_ERR_NONE)
345 grub_dl_unref (my_mod);
346 loaded = 0;
347 grub_relocator_unload (relocator);
350 return grub_errno;
353 static grub_err_t
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;
363 grub_err_t err;
364 int i, nfiles = 0;
365 grub_uint8_t *ptr;
367 if (argc == 0)
369 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
370 goto fail;
373 if (!loaded)
375 grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
376 goto fail;
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");
385 goto fail;
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
395 0x3fffffff. */
396 if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
397 addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
399 else
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
406 the last page.
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. */
409 addr_max -= 0x10000;
411 addr_min = GRUB_LINUX_BZIMAGE_ADDR + grub_linux16_prot_size;
413 files = grub_zalloc (argc * sizeof (files[0]));
414 if (!files)
415 goto fail;
417 for (i = 0; i < argc; i++)
419 grub_file_filter_disable_compression ();
420 files[i] = grub_file_open (argv[i]);
421 if (! files[i])
422 goto fail;
423 nfiles++;
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,
431 size, 0x1000,
432 GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
433 if (err)
434 return err;
435 initrd_chunk = get_virtual_current_address (ch);
436 initrd_addr = get_physical_target_address (ch);
439 ptr = initrd_chunk;
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)
446 if (!grub_errno)
447 grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
448 argv[i]);
449 goto fail;
451 ptr += cursize;
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;
459 fail:
460 for (i = 0; i < nfiles; i++)
461 grub_file_close (files[i]);
462 grub_free (files);
464 return grub_errno;
467 static grub_command_t cmd_linux, cmd_initrd;
469 GRUB_MOD_INIT(linux16)
471 cmd_linux =
472 grub_register_command ("linux16", grub_cmd_linux,
473 0, N_("Load Linux."));
474 cmd_initrd =
475 grub_register_command ("initrd16", grub_cmd_initrd,
476 0, N_("Load initrd."));
477 my_mod = mod;
480 GRUB_MOD_FINI(linux16)
482 grub_unregister_command (cmd_linux);
483 grub_unregister_command (cmd_initrd);