~ubuntu-branches/debian/sid/grub2/sid-200907171837

« back to all changes in this revision

Viewing changes to loader/i386/pc/linux.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060105152040-1ab076d4n3y2o5yf
Tags: 1.92-1
* New upstream release.
  - Add support for GPT partition table format.
  - Add a new command "play" to play an audio file on PC.
  - Add support for Linux/ADFS partition table format.
  - Add support for BASH-like scripting.
  - Add support for Apple HFS+ filesystems.
* 01_fix_grub-install.patch: Added. Fix grub-install to use
  /bin/grub-mkimage instead of /sbin/grub-mkimage. Closes: #338824
* Do not use CDBS tarball mode anymore. Closes: #344272  

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* linux.c - boot Linux zImage or bzImage */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005  Free Software Foundation, Inc.
 
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 <grub/loader.h>
 
22
#include <grub/machine/loader.h>
 
23
#include <grub/file.h>
 
24
#include <grub/err.h>
 
25
#include <grub/device.h>
 
26
#include <grub/disk.h>
 
27
#include <grub/misc.h>
 
28
#include <grub/types.h>
 
29
#include <grub/machine/init.h>
 
30
#include <grub/machine/memory.h>
 
31
#include <grub/rescue.h>
 
32
#include <grub/dl.h>
 
33
#include <grub/machine/linux.h>
 
34
 
 
35
static grub_dl_t my_mod;
 
36
 
 
37
static int big_linux;
 
38
static grub_size_t linux_mem_size;
 
39
static int loaded;
 
40
 
 
41
static grub_err_t
 
42
grub_linux_boot (void)
 
43
{
 
44
  if (big_linux)
 
45
    grub_linux_boot_bzimage ();
 
46
  else
 
47
    grub_linux_boot_zimage ();
 
48
 
 
49
  /* Never reach here.  */
 
50
  return GRUB_ERR_NONE;
 
51
}
 
52
 
 
53
static grub_err_t
 
54
grub_linux_unload (void)
 
55
{
 
56
  grub_dl_unref (my_mod);
 
57
  loaded = 0;
 
58
  return GRUB_ERR_NONE;
 
59
}
 
60
 
 
61
void
 
62
grub_rescue_cmd_linux (int argc, char *argv[])
 
63
{
 
64
  grub_file_t file = 0;
 
65
  struct linux_kernel_header lh;
 
66
  grub_uint8_t setup_sects;
 
67
  grub_size_t real_size, prot_size;
 
68
  grub_ssize_t len;
 
69
  int i;
 
70
  char *dest;
 
71
 
 
72
  grub_dl_ref (my_mod);
 
73
  
 
74
  if (argc == 0)
 
75
    {
 
76
      grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
 
77
      goto fail;
 
78
    }
 
79
 
 
80
  file = grub_file_open (argv[0]);
 
81
  if (! file)
 
82
    goto fail;
 
83
 
 
84
  if (grub_file_size (file) > (grub_ssize_t) grub_os_area_size)
 
85
    {
 
86
      grub_error (GRUB_ERR_OUT_OF_RANGE, "too big kernel");
 
87
      goto fail;
 
88
    }
 
89
 
 
90
  if (grub_file_read (file, (char *) &lh, sizeof (lh)) != sizeof (lh))
 
91
    {
 
92
      grub_error (GRUB_ERR_READ_ERROR, "cannot read the linux header");
 
93
      goto fail;
 
94
    }
 
95
 
 
96
  if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
 
97
    {
 
98
      grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
 
99
      goto fail;
 
100
    }
 
101
 
 
102
  if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
 
103
    {
 
104
      grub_error (GRUB_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 == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
 
113
      && grub_le_to_cpu16 (lh.version) >= 0x0200)
 
114
    {
 
115
      big_linux = (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL);
 
116
      lh.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
 
117
      
 
118
      /* Put the real mode part at as a high location as possible.  */
 
119
      grub_linux_real_addr = (char *) (grub_lower_mem
 
120
                                       - GRUB_LINUX_SETUP_MOVE_SIZE);
 
121
      /* But it must not exceed the traditional area.  */
 
122
      if (grub_linux_real_addr > (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR)
 
123
        grub_linux_real_addr = (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR;
 
124
      
 
125
      if (grub_le_to_cpu16 (lh.version) >= 0x0201)
 
126
        {
 
127
          lh.heap_end_ptr = grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET);
 
128
          lh.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
 
129
        }
 
130
      
 
131
      if (grub_le_to_cpu16 (lh.version) >= 0x0202)
 
132
        lh.cmd_line_ptr = grub_linux_real_addr + GRUB_LINUX_CL_OFFSET;
 
133
      else
 
134
        {
 
135
          lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
 
136
          lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
 
137
          lh.setup_move_size = grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE);
 
138
        }
 
139
    }
 
140
  else
 
141
    {
 
142
      /* Your kernel is quite old...  */
 
143
      lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
 
144
      lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
 
145
      
 
146
      setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
 
147
      
 
148
      grub_linux_real_addr = (char *) GRUB_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 = GRUB_LINUX_DEFAULT_SETUP_SECTS;
 
154
  
 
155
  real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
 
156
  prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
 
157
  
 
158
  grub_linux_tmp_addr = (char *) GRUB_LINUX_BZIMAGE_ADDR + prot_size;
 
159
 
 
160
  if (! big_linux
 
161
      && prot_size > (grub_size_t) (grub_linux_real_addr
 
162
                                    - (char *) GRUB_LINUX_ZIMAGE_ADDR))
 
163
    {
 
164
      grub_error (GRUB_ERR_BAD_OS, "too big zImage, use bzImage instead");
 
165
      goto fail;
 
166
    }
 
167
  
 
168
  if (grub_linux_real_addr + GRUB_LINUX_SETUP_MOVE_SIZE
 
169
      > (char *) grub_lower_mem)
 
170
    {
 
171
      grub_error (GRUB_ERR_OUT_OF_RANGE, "too small lower memory");
 
172
      goto fail;
 
173
    }
 
174
 
 
175
  grub_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 (grub_memcmp (argv[i], "vga=", 4) == 0)
 
180
      {
 
181
        /* Video mode selection support.  */
 
182
        grub_uint16_t vid_mode;
 
183
        char *val = argv[i] + 4;
 
184
 
 
185
        if (grub_strcmp (val, "normal") == 0)
 
186
          vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
 
187
        else if (grub_strcmp (val, "ext") == 0)
 
188
          vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
 
189
        else if (grub_strcmp (val, "ask") == 0)
 
190
          vid_mode = GRUB_LINUX_VID_MODE_ASK;
 
191
        else
 
192
          vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
 
193
 
 
194
        if (grub_errno)
 
195
          goto fail;
 
196
 
 
197
        lh.vid_mode = grub_cpu_to_le16 (vid_mode);
 
198
      }
 
199
    else if (grub_memcmp (argv[i], "mem=", 4) == 0)
 
200
      {
 
201
        char *val = argv[i] + 4;
 
202
          
 
203
        linux_mem_size = grub_strtoul (val, &val, 0);
 
204
        
 
205
        if (grub_errno)
 
206
          {
 
207
            grub_errno = GRUB_ERR_NONE;
 
208
            linux_mem_size = 0;
 
209
          }
 
210
        else
 
211
          {
 
212
            int shift = 0;
 
213
            
 
214
            switch (grub_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
  grub_memmove (grub_linux_tmp_addr, &lh, sizeof (lh));
 
236
 
 
237
  len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh);
 
238
  if (grub_file_read (file, grub_linux_tmp_addr + sizeof (lh), len) != len)
 
239
    {
 
240
      grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
 
241
      goto fail;
 
242
    }
 
243
 
 
244
  if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
 
245
      || grub_le_to_cpu16 (lh.version) < 0x0200)
 
246
    /* Clear the heap space.  */
 
247
    grub_memset (grub_linux_tmp_addr
 
248
                 + ((setup_sects + 1) << GRUB_DISK_SECTOR_BITS),
 
249
                 0,
 
250
                 ((GRUB_LINUX_MAX_SETUP_SECTS - setup_sects - 1)
 
251
                  << GRUB_DISK_SECTOR_BITS));
 
252
 
 
253
  /* Specify the boot file.  */
 
254
  dest = grub_stpcpy (grub_linux_tmp_addr + GRUB_LINUX_CL_OFFSET,
 
255
                      "BOOT_IMAGE=");
 
256
  dest = grub_stpcpy (dest, argv[0]);
 
257
  
 
258
  /* Copy kernel parameters.  */
 
259
  for (i = 1;
 
260
       i < argc
 
261
         && dest + grub_strlen (argv[i]) + 1 < (grub_linux_tmp_addr
 
262
                                                + GRUB_LINUX_CL_END_OFFSET);
 
263
       i++)
 
264
    {
 
265
      *dest++ = ' ';
 
266
      dest = grub_stpcpy (dest, argv[i]);
 
267
    }
 
268
 
 
269
  len = prot_size;
 
270
  if (grub_file_read (file, (char *) GRUB_LINUX_BZIMAGE_ADDR, len) != len)
 
271
    grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
 
272
 
 
273
  if (grub_errno == GRUB_ERR_NONE)
 
274
    {
 
275
      grub_linux_prot_size = prot_size;
 
276
      grub_loader_set (grub_linux_boot, grub_linux_unload);
 
277
      loaded = 1;
 
278
    }
 
279
 
 
280
 fail:
 
281
  
 
282
  if (file)
 
283
    grub_file_close (file);
 
284
 
 
285
  if (grub_errno != GRUB_ERR_NONE)
 
286
    {
 
287
      grub_dl_unref (my_mod);
 
288
      loaded = 0;
 
289
    }
 
290
}
 
291
 
 
292
void
 
293
grub_rescue_cmd_initrd (int argc, char *argv[])
 
294
{
 
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;
 
299
 
 
300
  if (argc == 0)
 
301
    {
 
302
      grub_error (GRUB_ERR_BAD_ARGUMENT, "No module specified");
 
303
      goto fail;
 
304
    }
 
305
  
 
306
  if (!loaded)
 
307
    {
 
308
      grub_error (GRUB_ERR_BAD_ARGUMENT, "You need to load the kernel first.");
 
309
      goto fail;
 
310
    }
 
311
 
 
312
  lh = (struct linux_kernel_header *) grub_linux_tmp_addr;
 
313
 
 
314
  if (!(lh->header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
 
315
        && grub_le_to_cpu16 (lh->version) >= 0x0200))
 
316
    {
 
317
      grub_error (GRUB_ERR_BAD_OS, "The kernel is too old for initrd.");
 
318
      goto fail;
 
319
    }
 
320
 
 
321
  /* Get the highest address available for the initrd.  */
 
322
  if (grub_le_to_cpu16 (lh->version) >= 0x0203)
 
323
    addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
 
324
  else
 
325
    addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
 
326
 
 
327
  if (!linux_mem_size && linux_mem_size < addr_max)
 
328
    addr_max = linux_mem_size;
 
329
 
 
330
  /* Linux 2.3.xx has a bug in the memory range check, so avoid
 
331
     the last page.
 
332
     Linux 2.2.xx has a bug in the memory range check, which is
 
333
     worse than that of Linux 2.3.xx, so avoid the last 64kb.  */
 
334
  addr_max -= 0x10000;
 
335
 
 
336
  if (addr_max > grub_os_area_addr + grub_os_area_size)
 
337
    addr_max = grub_os_area_addr + grub_os_area_size;
 
338
 
 
339
  addr_min = (grub_addr_t) grub_linux_tmp_addr + GRUB_LINUX_CL_END_OFFSET;
 
340
 
 
341
  file = grub_file_open (argv[0]);
 
342
  if (!file)
 
343
    goto fail;
 
344
 
 
345
  size = grub_file_size (file);
 
346
 
 
347
  /* Put the initrd as high as possible, 4Ki aligned.  */
 
348
  addr = (addr_max - size) & ~0xFFF;
 
349
 
 
350
  if (addr < addr_min)
 
351
    {
 
352
      grub_error (GRUB_ERR_OUT_OF_RANGE, "The initrd is too big");
 
353
      goto fail;
 
354
    }
 
355
 
 
356
  if (grub_file_read (file, (void *)addr, size) != size)
 
357
    {
 
358
      grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
 
359
      goto fail;
 
360
    }
 
361
 
 
362
  lh->ramdisk_image = addr;
 
363
  lh->ramdisk_size = size;
 
364
  
 
365
 fail:
 
366
  if (file)
 
367
    grub_file_close (file);
 
368
}
 
369
 
 
370
 
 
371
GRUB_MOD_INIT(linux)
 
372
{
 
373
  grub_rescue_register_command ("linux",
 
374
                                grub_rescue_cmd_linux,
 
375
                                "load linux");
 
376
  grub_rescue_register_command ("initrd",
 
377
                                grub_rescue_cmd_initrd,
 
378
                                "load initrd");
 
379
  my_mod = mod;
 
380
}
 
381
 
 
382
GRUB_MOD_FINI(linux)
 
383
{
 
384
  grub_rescue_unregister_command ("linux");
 
385
  grub_rescue_unregister_command ("initrd");
 
386
}