]> git.proxmox.com Git - grub2.git/blame - loader/i386/pc/linux.c
2004-03-14 Marco Gerards <metgerards@student.han.nl>
[grub2.git] / loader / i386 / pc / linux.c
CommitLineData
c04da074 1/* linux.c - boot Linux zImage or bzImage */
2/*
3 * PUPA -- Preliminary Universal Programming Architecture for GRUB
8367695c 4 * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
c04da074 5 *
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#include <pupa/loader.h>
22#include <pupa/machine/loader.h>
23#include <pupa/file.h>
24#include <pupa/err.h>
25#include <pupa/device.h>
26#include <pupa/disk.h>
27#include <pupa/misc.h>
28#include <pupa/types.h>
29#include <pupa/machine/init.h>
30#include <pupa/machine/memory.h>
31#include <pupa/rescue.h>
32#include <pupa/dl.h>
33#include <pupa/machine/linux.h>
34
35static pupa_dl_t my_mod;
36
37static int big_linux;
38static pupa_size_t linux_mem_size;
39static int loaded;
40
41static pupa_err_t
42pupa_linux_boot (void)
43{
44 if (big_linux)
45 pupa_linux_boot_bzimage ();
46 else
47 pupa_linux_boot_zimage ();
48
49 /* Never reach here. */
50 return PUPA_ERR_NONE;
51}
52
53static pupa_err_t
54pupa_linux_unload (void)
55{
56 pupa_dl_unref (my_mod);
57 loaded = 0;
58 return PUPA_ERR_NONE;
59}
60
ce5bf700 61void
c04da074 62pupa_rescue_cmd_linux (int argc, char *argv[])
63{
64 pupa_file_t file = 0;
65 struct linux_kernel_header lh;
66 pupa_uint8_t setup_sects;
67 pupa_size_t real_size, prot_size;
e6eced71 68 pupa_ssize_t len;
c04da074 69 int i;
70 char *dest;
71
72 pupa_dl_ref (my_mod);
73
74 if (argc == 0)
75 {
76 pupa_error (PUPA_ERR_BAD_ARGUMENT, "no kernel specified");
77 goto fail;
78 }
79
80 file = pupa_file_open (argv[0]);
81 if (! file)
82 goto fail;
83
84 if (pupa_file_size (file) > (pupa_ssize_t) pupa_os_area_size)
85 {
86 pupa_error (PUPA_ERR_OUT_OF_RANGE, "too big kernel");
87 goto fail;
88 }
89
90 if (pupa_file_read (file, (char *) &lh, sizeof (lh)) != sizeof (lh))
91 {
92 pupa_error (PUPA_ERR_READ_ERROR, "cannot read the linux header");
93 goto fail;
94 }
95
96 if (lh.boot_flag != pupa_cpu_to_le16 (0xaa55))
97 {
98 pupa_error (PUPA_ERR_BAD_OS, "invalid magic number");
99 goto fail;
100 }
101
102 if (lh.setup_sects > PUPA_LINUX_MAX_SETUP_SECTS)
103 {
104 pupa_error (PUPA_ERR_BAD_OS, "too many setup sectors");
105 goto fail;
106 }
107
108 big_linux = 0;
109 setup_sects = lh.setup_sects;
110 linux_mem_size = 0;
111
112 if (lh.header == pupa_cpu_to_le32 (PUPA_LINUX_MAGIC_SIGNATURE)
113 && pupa_le_to_cpu16 (lh.version) >= 0x0200)
114 {
115 big_linux = (lh.loadflags & PUPA_LINUX_FLAG_BIG_KERNEL);
116 lh.type_of_loader = PUPA_LINUX_BOOT_LOADER_TYPE;
117
118 /* Put the real mode part at as a high location as possible. */
119 pupa_linux_real_addr = (char *) (pupa_lower_mem
120 - PUPA_LINUX_SETUP_MOVE_SIZE);
121 /* But it must not exceed the traditional area. */
122 if (pupa_linux_real_addr > (char *) PUPA_LINUX_OLD_REAL_MODE_ADDR)
123 pupa_linux_real_addr = (char *) PUPA_LINUX_OLD_REAL_MODE_ADDR;
124
125 if (pupa_le_to_cpu16 (lh.version) >= 0x0201)
126 {
127 lh.heap_end_ptr = pupa_cpu_to_le16 (PUPA_LINUX_HEAP_END_OFFSET);
128 lh.loadflags |= PUPA_LINUX_FLAG_CAN_USE_HEAP;
129 }
130
131 if (pupa_le_to_cpu16 (lh.version) >= 0x0202)
132 lh.cmd_line_ptr = pupa_linux_real_addr + PUPA_LINUX_CL_OFFSET;
133 else
134 {
135 lh.cl_magic = pupa_cpu_to_le16 (PUPA_LINUX_CL_MAGIC);
136 lh.cl_offset = pupa_cpu_to_le16 (PUPA_LINUX_CL_OFFSET);
137 lh.setup_move_size = pupa_cpu_to_le16 (PUPA_LINUX_SETUP_MOVE_SIZE);
138 }
139 }
140 else
141 {
142 /* Your kernel is quite old... */
143 lh.cl_magic = pupa_cpu_to_le16 (PUPA_LINUX_CL_MAGIC);
144 lh.cl_offset = pupa_cpu_to_le16 (PUPA_LINUX_CL_OFFSET);
145
146 setup_sects = PUPA_LINUX_DEFAULT_SETUP_SECTS;
147
148 pupa_linux_real_addr = (char *) PUPA_LINUX_OLD_REAL_MODE_ADDR;
149 }
150
151 /* If SETUP_SECTS is not set, set it to the default (4). */
152 if (! setup_sects)
153 setup_sects = PUPA_LINUX_DEFAULT_SETUP_SECTS;
154
155 real_size = setup_sects << PUPA_DISK_SECTOR_BITS;
156 prot_size = pupa_file_size (file) - real_size - PUPA_DISK_SECTOR_SIZE;
157
158 pupa_linux_tmp_addr = (char *) PUPA_LINUX_BZIMAGE_ADDR + prot_size;
159
160 if (! big_linux
161 && prot_size > (pupa_size_t) (pupa_linux_real_addr
162 - (char *) PUPA_LINUX_ZIMAGE_ADDR))
163 {
164 pupa_error (PUPA_ERR_BAD_OS, "too big zImage, use bzImage instead");
165 goto fail;
166 }
167
168 if (pupa_linux_real_addr + PUPA_LINUX_SETUP_MOVE_SIZE
169 > (char *) pupa_lower_mem)
170 {
171 pupa_error (PUPA_ERR_OUT_OF_RANGE, "too small lower memory");
172 goto fail;
173 }
174
175 pupa_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
176 big_linux ? "bzImage" : "zImage", real_size, prot_size);
177
178 for (i = 1; i < argc; i++)
179 if (pupa_memcmp (argv[i], "vga=", 4) == 0)
180 {
181 /* Video mode selection support. */
182 pupa_uint16_t vid_mode;
183 char *val = argv[i] + 4;
184
185 if (pupa_strcmp (val, "normal") == 0)
186 vid_mode = PUPA_LINUX_VID_MODE_NORMAL;
187 else if (pupa_strcmp (val, "ext") == 0)
188 vid_mode = PUPA_LINUX_VID_MODE_EXTENDED;
189 else if (pupa_strcmp (val, "ask") == 0)
190 vid_mode = PUPA_LINUX_VID_MODE_ASK;
191 else
192 vid_mode = (pupa_uint16_t) pupa_strtoul (val, 0, 0);
193
194 if (pupa_errno)
195 goto fail;
196
197 lh.vid_mode = pupa_cpu_to_le16 (vid_mode);
198 }
199 else if (pupa_memcmp (argv[i], "mem=", 4) == 0)
200 {
201 char *val = argv[i] + 4;
202
203 linux_mem_size = pupa_strtoul (val, &val, 0);
204
205 if (pupa_errno)
206 {
207 pupa_errno = PUPA_ERR_NONE;
208 linux_mem_size = 0;
209 }
210 else
211 {
212 int shift = 0;
213
214 switch (pupa_tolower (val[0]))
215 {
216 case 'g':
217 shift += 10;
218 case 'm':
219 shift += 10;
220 case 'k':
221 shift += 10;
222 default:
223 break;
224 }
225
226 /* Check an overflow. */
227 if (linux_mem_size > (~0UL >> shift))
228 linux_mem_size = 0;
229 else
230 linux_mem_size <<= shift;
231 }
232 }
233
234 /* Put the real mode code at the temporary address. */
235 pupa_memmove (pupa_linux_tmp_addr, &lh, sizeof (lh));
e6eced71 236
237 len = real_size + PUPA_DISK_SECTOR_SIZE - sizeof (lh);
238 if (pupa_file_read (file, pupa_linux_tmp_addr + sizeof (lh), len) != len)
239 {
240 pupa_error (PUPA_ERR_FILE_READ_ERROR, "Couldn't read file");
241 goto fail;
242 }
c04da074 243
244 if (lh.header != pupa_cpu_to_le32 (PUPA_LINUX_MAGIC_SIGNATURE)
245 || pupa_le_to_cpu16 (lh.version) < 0x0200)
246 /* Clear the heap space. */
247 pupa_memset (pupa_linux_tmp_addr
248 + ((setup_sects + 1) << PUPA_DISK_SECTOR_BITS),
249 0,
250 ((PUPA_LINUX_MAX_SETUP_SECTS - setup_sects - 1)
251 << PUPA_DISK_SECTOR_BITS));
252
253 /* Copy kernel parameters. */
254 for (i = 1, dest = pupa_linux_tmp_addr + PUPA_LINUX_CL_OFFSET;
255 i < argc
256 && dest + pupa_strlen (argv[i]) < (pupa_linux_tmp_addr
257 + PUPA_LINUX_CL_END_OFFSET);
258 i++, *dest++ = ' ')
259 {
260 pupa_strcpy (dest, argv[i]);
261 dest += pupa_strlen (argv[i]);
262 }
263
264 if (i != 1)
265 dest--;
266
267 *dest = '\0';
268
e6eced71 269 len = prot_size;
270 if (pupa_file_read (file, (char *) PUPA_LINUX_BZIMAGE_ADDR, len) != len)
271 pupa_error (PUPA_ERR_FILE_READ_ERROR, "Couldn't read file");
272
c04da074 273 if (pupa_errno == PUPA_ERR_NONE)
274 {
275 pupa_linux_prot_size = prot_size;
276 pupa_loader_set (pupa_linux_boot, pupa_linux_unload);
277 loaded = 1;
278 }
279
280 fail:
281
282 if (file)
283 pupa_file_close (file);
284
285 if (pupa_errno != PUPA_ERR_NONE)
286 {
287 pupa_dl_unref (my_mod);
288 loaded = 0;
289 }
290}
291
ce5bf700 292void
db1771cf 293pupa_rescue_cmd_initrd (int argc __attribute__ ((unused)),
294 char *argv[] __attribute__ ((unused)))
c04da074 295{
e6eced71 296 pupa_file_t file = 0;
297 pupa_ssize_t size;
298 pupa_addr_t addr_max, addr_min, addr;
299 struct linux_kernel_header *lh;
300
301 if (argc == 0)
302 {
303 pupa_error (PUPA_ERR_BAD_ARGUMENT, "No module specified");
304 goto fail;
305 }
306
307 if (!loaded)
308 {
309 pupa_error (PUPA_ERR_BAD_ARGUMENT, "You need to load the kernel first.");
310 goto fail;
311 }
312
313 lh = (struct linux_kernel_header *) pupa_linux_tmp_addr;
314
315 if (!(lh->header == pupa_cpu_to_le32 (PUPA_LINUX_MAGIC_SIGNATURE)
316 && pupa_le_to_cpu16 (lh->version) >= 0x0200))
317 {
318 pupa_error (PUPA_ERR_BAD_OS, "The kernel is too old for initrd.");
319 goto fail;
320 }
321
322 /* Get the highest address available for the initrd. */
323 if (pupa_le_to_cpu16 (lh->version) >= 0x0203)
324 addr_max = pupa_cpu_to_le32 (lh->initrd_addr_max);
325 else
326 addr_max = PUPA_LINUX_INITRD_MAX_ADDRESS;
327
328 if (!linux_mem_size && linux_mem_size < addr_max)
329 addr_max = linux_mem_size;
330
331 /* Linux 2.3.xx has a bug in the memory range check, so avoid
332 the last page.
333 Linux 2.2.xx has a bug in the memory range check, which is
334 worse than that of Linux 2.3.xx, so avoid the last 64kb. */
335 addr_max -= 0x10000;
336
337 if (addr_max > pupa_os_area_addr + pupa_os_area_size)
338 addr_max = pupa_os_area_addr + pupa_os_area_size;
339
340 addr_min = (pupa_addr_t) pupa_linux_tmp_addr + PUPA_LINUX_CL_END_OFFSET;
341
342 file = pupa_file_open (argv[0]);
343 if (!file)
344 goto fail;
345
346 size = pupa_file_size (file);
347
348 /* Put the initrd as high as possible, 4Ki aligned. */
349 addr = (addr_max - size) & ~0xFFF;
350
351 if (addr < addr_min)
352 {
353 pupa_error (PUPA_ERR_OUT_OF_RANGE, "The initrd is too big");
354 goto fail;
355 }
356
357 if (pupa_file_read (file, (void *)addr, size) != size)
358 {
359 pupa_error (PUPA_ERR_FILE_READ_ERROR, "Couldn't read file");
360 goto fail;
361 }
362
363 lh->ramdisk_image = addr;
364 lh->ramdisk_size = size;
365
366 fail:
367 if (file)
368 pupa_file_close (file);
c04da074 369}
370
e6eced71 371
c04da074 372PUPA_MOD_INIT
373{
374 pupa_rescue_register_command ("linux",
375 pupa_rescue_cmd_linux,
376 "load linux");
377 pupa_rescue_register_command ("initrd",
378 pupa_rescue_cmd_initrd,
379 "load initrd");
380 my_mod = mod;
381}
382
383PUPA_MOD_FINI
384{
385 pupa_rescue_unregister_command ("linux");
386 pupa_rescue_unregister_command ("initrd");
387}