remove all trailing whitespace
[grub2/phcoder/solaris.git] / loader / i386 / pc / linux.c
blob148cb7777a861b799ebbda1415786fbaf37aa863
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 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>
23 #include <grub/err.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/dl.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;
40 static int loaded;
42 static grub_err_t
43 grub_linux_unload (void)
45 grub_dl_unref (my_mod);
46 loaded = 0;
47 return GRUB_ERR_NONE;
50 static grub_err_t
51 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
52 int argc, char *argv[])
54 grub_file_t file = 0;
55 struct linux_kernel_header lh;
56 grub_uint8_t setup_sects;
57 grub_size_t real_size, prot_size;
58 grub_ssize_t len;
59 int i;
60 char *dest;
62 grub_dl_ref (my_mod);
64 if (argc == 0)
66 grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
67 goto fail;
70 file = grub_file_open (argv[0]);
71 if (! file)
72 goto fail;
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),
78 grub_os_area_size);
79 goto fail;
82 if (grub_file_read (file, (char *) &lh, sizeof (lh)) != sizeof (lh))
84 grub_error (GRUB_ERR_READ_ERROR, "cannot read the linux header");
85 goto fail;
88 if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
90 grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
91 goto fail;
94 if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
96 grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
97 goto fail;
100 grub_linux_is_bzimage = 0;
101 setup_sects = lh.setup_sects;
102 linux_mem_size = 0;
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. */
111 grub_linux_real_addr
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;
126 else
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);
133 else
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). */
145 if (! setup_sects)
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);
159 goto fail;
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 ());
169 goto fail;
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;
188 else
189 vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
191 if (grub_errno)
192 goto fail;
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);
202 if (grub_errno)
204 grub_errno = GRUB_ERR_NONE;
205 linux_mem_size = 0;
207 else
209 int shift = 0;
211 switch (grub_tolower (val[0]))
213 case 'g':
214 shift += 10;
215 case 'm':
216 shift += 10;
217 case 'k':
218 shift += 10;
219 default:
220 break;
223 /* Check an overflow. */
224 if (linux_mem_size > (~0UL >> shift))
225 linux_mem_size = 0;
226 else
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");
238 goto fail;
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,
252 "BOOT_IMAGE=");
253 dest = grub_stpcpy (dest, argv[0]);
255 /* Copy kernel parameters. */
256 for (i = 1;
257 i < argc
258 && dest + grub_strlen (argv[i]) + 1 < (grub_linux_tmp_addr
259 + GRUB_LINUX_CL_END_OFFSET);
260 i++)
262 *dest++ = ' ';
263 dest = grub_stpcpy (dest, argv[i]);
266 len = prot_size;
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);
274 loaded = 1;
277 fail:
279 if (file)
280 grub_file_close (file);
282 if (grub_errno != GRUB_ERR_NONE)
284 grub_dl_unref (my_mod);
285 loaded = 0;
288 return grub_errno;
291 static grub_err_t
292 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
293 int argc, char *argv[])
295 grub_file_t file = 0;
296 grub_ssize_t size;
297 grub_addr_t addr_max, addr_min, addr;
298 struct linux_kernel_header *lh;
300 if (argc == 0)
302 grub_error (GRUB_ERR_BAD_ARGUMENT, "No module specified");
303 goto fail;
306 if (!loaded)
308 grub_error (GRUB_ERR_BAD_ARGUMENT, "You need to load the kernel first.");
309 goto fail;
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.");
318 goto fail;
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
328 0x3fffffff. */
329 if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
330 addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
332 else
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
339 the last page.
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. */
342 addr_max -= 0x10000;
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]);
350 if (!file)
351 goto fail;
353 size = grub_file_size (file);
355 /* Put the initrd as high as possible, 4KiB aligned. */
356 addr = (addr_max - size) & ~0xFFF;
358 if (addr < addr_min)
360 grub_error (GRUB_ERR_OUT_OF_RANGE, "The initrd is too big");
361 goto fail;
364 if (grub_file_read (file, (void *)addr, size) != size)
366 grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
367 goto fail;
370 lh->ramdisk_image = addr;
371 lh->ramdisk_size = size;
373 fail:
374 if (file)
375 grub_file_close (file);
377 return grub_errno;
380 static grub_command_t cmd_linux, cmd_initrd;
382 GRUB_MOD_INIT(linux16)
384 cmd_linux =
385 grub_register_command ("linux16", grub_cmd_linux,
386 0, "load linux");
387 cmd_initrd =
388 grub_register_command ("initrd16", grub_cmd_initrd,
389 0, "load initrd");
390 my_mod = mod;
393 GRUB_MOD_FINI(linux16)
395 grub_unregister_command (cmd_linux);
396 grub_unregister_command (cmd_initrd);