~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

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,2007,2008,2009,2010  Free Software Foundation, Inc.
5
 
 *
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.
10
 
 *
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.
15
 
 *
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/>.
18
 
 */
19
 
 
20
 
#include <grub/loader.h>
21
 
#include <grub/cpu/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>
33
 
#include <grub/i18n.h>
34
 
#include <grub/mm.h>
35
 
#include <grub/video.h>
36
 
 
37
 
#define GRUB_LINUX_CL_OFFSET            0x9000
38
 
#define GRUB_LINUX_CL_END_OFFSET        0x90FF
39
 
 
40
 
extern grub_uint32_t grub_linux_prot_size;
41
 
extern char *grub_linux_tmp_addr;
42
 
extern char *grub_linux_real_addr;
43
 
extern grub_int32_t grub_linux_is_bzimage;
44
 
extern char grub_linux16_boot_end[];
45
 
grub_err_t grub_linux16_real_boot (void);
46
 
 
47
 
static grub_dl_t my_mod;
48
 
 
49
 
static grub_size_t linux_mem_size;
50
 
static int loaded;
51
 
 
52
 
static grub_err_t
53
 
grub_linux_unload (void)
54
 
{
55
 
  grub_dl_unref (my_mod);
56
 
  loaded = 0;
57
 
  return GRUB_ERR_NONE;
58
 
}
59
 
 
60
 
static grub_err_t
61
 
grub_linux16_boot (void)
62
 
{
63
 
  grub_video_set_mode ("text", 0, 0);
64
 
  grub_linux16_real_boot ();
65
 
 
66
 
  /* Not reached.  */
67
 
  return GRUB_ERR_NONE;
68
 
}
69
 
 
70
 
static grub_err_t
71
 
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
72
 
                int argc, char *argv[])
73
 
{
74
 
  grub_file_t file = 0;
75
 
  struct linux_kernel_header lh;
76
 
  grub_uint8_t setup_sects;
77
 
  grub_size_t real_size, prot_size;
78
 
  grub_ssize_t len;
79
 
  int i;
80
 
  char *dest;
81
 
 
82
 
  grub_dl_ref (my_mod);
83
 
 
84
 
  if (argc == 0)
85
 
    {
86
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
87
 
      goto fail;
88
 
    }
89
 
 
90
 
  file = grub_file_open (argv[0]);
91
 
  if (! file)
92
 
    goto fail;
93
 
 
94
 
  if ((grub_size_t) grub_file_size (file) > grub_os_area_size)
95
 
    {
96
 
      grub_error (GRUB_ERR_OUT_OF_RANGE, "too big kernel (0x%x > 0x%x)",
97
 
                  (grub_size_t) grub_file_size (file),
98
 
                  grub_os_area_size);
99
 
      goto fail;
100
 
    }
101
 
 
102
 
  if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
103
 
    {
104
 
      grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
105
 
      goto fail;
106
 
    }
107
 
 
108
 
  if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
109
 
    {
110
 
      grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
111
 
      goto fail;
112
 
    }
113
 
 
114
 
  if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
115
 
    {
116
 
      grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
117
 
      goto fail;
118
 
    }
119
 
 
120
 
  grub_linux_is_bzimage = 0;
121
 
  setup_sects = lh.setup_sects;
122
 
  linux_mem_size = 0;
123
 
 
124
 
  if (lh.header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
125
 
      && grub_le_to_cpu16 (lh.version) >= 0x0200)
126
 
    {
127
 
      grub_linux_is_bzimage = (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL);
128
 
      lh.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
129
 
 
130
 
      /* Put the real mode part at as a high location as possible.  */
131
 
      grub_linux_real_addr
132
 
        = (char *) UINT_TO_PTR (grub_mmap_get_lower ()
133
 
                                - GRUB_LINUX_SETUP_MOVE_SIZE);
134
 
      /* But it must not exceed the traditional area.  */
135
 
      if (grub_linux_real_addr > (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR)
136
 
        grub_linux_real_addr = (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR;
137
 
 
138
 
      /* Ensure that it doesn't overlap with helper.  */
139
 
      if (((char *) grub_linux16_boot <= grub_linux_real_addr
140
 
           && grub_linux16_boot_end >= grub_linux_real_addr)
141
 
          || ((char *) grub_linux16_boot 
142
 
              <= grub_linux_real_addr + GRUB_LINUX_SETUP_MOVE_SIZE
143
 
              && grub_linux16_boot_end
144
 
              >= grub_linux_real_addr + GRUB_LINUX_SETUP_MOVE_SIZE))
145
 
        grub_linux_real_addr = ((char *) grub_linux16_boot)
146
 
          - GRUB_LINUX_SETUP_MOVE_SIZE;
147
 
 
148
 
      if (grub_le_to_cpu16 (lh.version) >= 0x0201)
149
 
        {
150
 
          lh.heap_end_ptr = grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET);
151
 
          lh.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
152
 
        }
153
 
 
154
 
      if (grub_le_to_cpu16 (lh.version) >= 0x0202)
155
 
        lh.cmd_line_ptr = grub_linux_real_addr + GRUB_LINUX_CL_OFFSET;
156
 
      else
157
 
        {
158
 
          lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
159
 
          lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
160
 
          lh.setup_move_size = grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE);
161
 
        }
162
 
    }
163
 
  else
164
 
    {
165
 
      /* Your kernel is quite old...  */
166
 
      lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
167
 
      lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
168
 
 
169
 
      setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
170
 
 
171
 
      grub_linux_real_addr = (char *) GRUB_LINUX_OLD_REAL_MODE_ADDR;
172
 
 
173
 
      /* Ensure that it doesn't overlap with helper.  */
174
 
      if (((char *) grub_linux16_boot <= grub_linux_real_addr
175
 
           && grub_linux16_boot_end >= grub_linux_real_addr)
176
 
          || ((char *) grub_linux16_boot 
177
 
              <= grub_linux_real_addr + GRUB_LINUX_SETUP_MOVE_SIZE
178
 
              && grub_linux16_boot_end
179
 
              >= grub_linux_real_addr + GRUB_LINUX_SETUP_MOVE_SIZE))
180
 
        {
181
 
          grub_error (GRUB_ERR_OUT_OF_RANGE, "Too much low memory allocated and"
182
 
                      " your kernel is too old.");
183
 
          goto fail;
184
 
        }
185
 
    }
186
 
 
187
 
  /* If SETUP_SECTS is not set, set it to the default (4).  */
188
 
  if (! setup_sects)
189
 
    setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
190
 
 
191
 
  real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
192
 
  prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
193
 
 
194
 
  grub_linux_tmp_addr = (char *) GRUB_LINUX_BZIMAGE_ADDR + prot_size;
195
 
 
196
 
  if (! grub_linux_is_bzimage
197
 
      && ((char *) GRUB_LINUX_ZIMAGE_ADDR + prot_size > grub_linux_real_addr))
198
 
    {
199
 
      grub_error (GRUB_ERR_BAD_OS, "too big zImage (0x%x > 0x%x), use bzImage instead",
200
 
                  (char *) GRUB_LINUX_ZIMAGE_ADDR + prot_size,
201
 
                  (grub_size_t) grub_linux_real_addr);
202
 
      goto fail;
203
 
    }
204
 
 
205
 
  if (grub_linux_real_addr + GRUB_LINUX_SETUP_MOVE_SIZE
206
 
      > (char *) UINT_TO_PTR (grub_mmap_get_lower ()))
207
 
    {
208
 
      grub_error (GRUB_ERR_OUT_OF_RANGE,
209
 
                 "too small lower memory (0x%x > 0x%x)",
210
 
                  grub_linux_real_addr + GRUB_LINUX_SETUP_MOVE_SIZE,
211
 
                  (int) grub_mmap_get_lower ());
212
 
      goto fail;
213
 
    }
214
 
 
215
 
  grub_printf ("   [Linux-%s, setup=0x%x, size=0x%x]\n",
216
 
               grub_linux_is_bzimage ? "bzImage" : "zImage", real_size, prot_size);
217
 
 
218
 
  for (i = 1; i < argc; i++)
219
 
    if (grub_memcmp (argv[i], "vga=", 4) == 0)
220
 
      {
221
 
        /* Video mode selection support.  */
222
 
        grub_uint16_t vid_mode;
223
 
        char *val = argv[i] + 4;
224
 
 
225
 
        if (grub_strcmp (val, "normal") == 0)
226
 
          vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
227
 
        else if (grub_strcmp (val, "ext") == 0)
228
 
          vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
229
 
        else if (grub_strcmp (val, "ask") == 0)
230
 
          vid_mode = GRUB_LINUX_VID_MODE_ASK;
231
 
        else
232
 
          vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
233
 
 
234
 
        if (grub_errno)
235
 
          goto fail;
236
 
 
237
 
        lh.vid_mode = grub_cpu_to_le16 (vid_mode);
238
 
      }
239
 
    else if (grub_memcmp (argv[i], "mem=", 4) == 0)
240
 
      {
241
 
        char *val = argv[i] + 4;
242
 
 
243
 
        linux_mem_size = grub_strtoul (val, &val, 0);
244
 
 
245
 
        if (grub_errno)
246
 
          {
247
 
            grub_errno = GRUB_ERR_NONE;
248
 
            linux_mem_size = 0;
249
 
          }
250
 
        else
251
 
          {
252
 
            int shift = 0;
253
 
 
254
 
            switch (grub_tolower (val[0]))
255
 
              {
256
 
              case 'g':
257
 
                shift += 10;
258
 
              case 'm':
259
 
                shift += 10;
260
 
              case 'k':
261
 
                shift += 10;
262
 
              default:
263
 
                break;
264
 
              }
265
 
 
266
 
            /* Check an overflow.  */
267
 
            if (linux_mem_size > (~0UL >> shift))
268
 
              linux_mem_size = 0;
269
 
            else
270
 
              linux_mem_size <<= shift;
271
 
          }
272
 
      }
273
 
 
274
 
  /* Put the real mode code at the temporary address.  */
275
 
  grub_memmove (grub_linux_tmp_addr, &lh, sizeof (lh));
276
 
 
277
 
  len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh);
278
 
  if (grub_file_read (file, grub_linux_tmp_addr + sizeof (lh), len) != len)
279
 
    {
280
 
      grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
281
 
      goto fail;
282
 
    }
283
 
 
284
 
  if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
285
 
      || grub_le_to_cpu16 (lh.version) < 0x0200)
286
 
    /* Clear the heap space.  */
287
 
    grub_memset (grub_linux_tmp_addr
288
 
                 + ((setup_sects + 1) << GRUB_DISK_SECTOR_BITS),
289
 
                 0,
290
 
                 ((GRUB_LINUX_MAX_SETUP_SECTS - setup_sects - 1)
291
 
                  << GRUB_DISK_SECTOR_BITS));
292
 
 
293
 
  /* Specify the boot file.  */
294
 
  dest = grub_stpcpy (grub_linux_tmp_addr + GRUB_LINUX_CL_OFFSET,
295
 
                      "BOOT_IMAGE=");
296
 
  dest = grub_stpcpy (dest, argv[0]);
297
 
 
298
 
  /* Copy kernel parameters.  */
299
 
  for (i = 1;
300
 
       i < argc
301
 
         && dest + grub_strlen (argv[i]) + 1 < (grub_linux_tmp_addr
302
 
                                                + GRUB_LINUX_CL_END_OFFSET);
303
 
       i++)
304
 
    {
305
 
      *dest++ = ' ';
306
 
      dest = grub_stpcpy (dest, argv[i]);
307
 
    }
308
 
 
309
 
  len = prot_size;
310
 
  if (grub_file_read (file, (void *) GRUB_LINUX_BZIMAGE_ADDR, len) != len)
311
 
    grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
312
 
 
313
 
  if (grub_errno == GRUB_ERR_NONE)
314
 
    {
315
 
      grub_linux_prot_size = prot_size;
316
 
      grub_loader_set (grub_linux16_boot, grub_linux_unload, 1);
317
 
      loaded = 1;
318
 
    }
319
 
 
320
 
 fail:
321
 
 
322
 
  if (file)
323
 
    grub_file_close (file);
324
 
 
325
 
  if (grub_errno != GRUB_ERR_NONE)
326
 
    {
327
 
      grub_dl_unref (my_mod);
328
 
      loaded = 0;
329
 
    }
330
 
 
331
 
  return grub_errno;
332
 
}
333
 
 
334
 
static grub_err_t
335
 
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
336
 
                 int argc, char *argv[])
337
 
{
338
 
  grub_file_t file = 0;
339
 
  grub_ssize_t size;
340
 
  grub_addr_t addr_max, addr_min, addr;
341
 
  struct linux_kernel_header *lh;
342
 
 
343
 
  if (argc == 0)
344
 
    {
345
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
346
 
      goto fail;
347
 
    }
348
 
 
349
 
  if (!loaded)
350
 
    {
351
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
352
 
      goto fail;
353
 
    }
354
 
 
355
 
  lh = (struct linux_kernel_header *) grub_linux_tmp_addr;
356
 
 
357
 
  if (!(lh->header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
358
 
        && grub_le_to_cpu16 (lh->version) >= 0x0200))
359
 
    {
360
 
      grub_error (GRUB_ERR_BAD_OS, "the kernel is too old for initrd");
361
 
      goto fail;
362
 
    }
363
 
 
364
 
  /* Get the highest address available for the initrd.  */
365
 
  if (grub_le_to_cpu16 (lh->version) >= 0x0203)
366
 
    {
367
 
      addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
368
 
 
369
 
      /* XXX in reality, Linux specifies a bogus value, so
370
 
         it is necessary to make sure that ADDR_MAX does not exceed
371
 
         0x3fffffff.  */
372
 
      if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
373
 
        addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
374
 
    }
375
 
  else
376
 
    addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
377
 
 
378
 
  if (linux_mem_size != 0 && linux_mem_size < addr_max)
379
 
    addr_max = linux_mem_size;
380
 
 
381
 
  /* Linux 2.3.xx has a bug in the memory range check, so avoid
382
 
     the last page.
383
 
     Linux 2.2.xx has a bug in the memory range check, which is
384
 
     worse than that of Linux 2.3.xx, so avoid the last 64kb.  */
385
 
  addr_max -= 0x10000;
386
 
 
387
 
  if (addr_max > grub_os_area_addr + grub_os_area_size)
388
 
    addr_max = grub_os_area_addr + grub_os_area_size;
389
 
 
390
 
  addr_min = (grub_addr_t) grub_linux_tmp_addr + GRUB_LINUX_CL_END_OFFSET;
391
 
 
392
 
  file = grub_file_open (argv[0]);
393
 
  if (!file)
394
 
    goto fail;
395
 
 
396
 
  size = grub_file_size (file);
397
 
 
398
 
  /* Put the initrd as high as possible, 4KiB aligned.  */
399
 
  addr = (addr_max - size) & ~0xFFF;
400
 
 
401
 
  if (addr < addr_min)
402
 
    {
403
 
      grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big");
404
 
      goto fail;
405
 
    }
406
 
 
407
 
  if (grub_file_read (file, (void *) addr, size) != size)
408
 
    {
409
 
      grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
410
 
      goto fail;
411
 
    }
412
 
 
413
 
  lh->ramdisk_image = addr;
414
 
  lh->ramdisk_size = size;
415
 
 
416
 
 fail:
417
 
  if (file)
418
 
    grub_file_close (file);
419
 
 
420
 
  return grub_errno;
421
 
}
422
 
 
423
 
static grub_command_t cmd_linux, cmd_initrd;
424
 
 
425
 
GRUB_MOD_INIT(linux16)
426
 
{
427
 
  cmd_linux =
428
 
    grub_register_command ("linux16", grub_cmd_linux,
429
 
                           0, N_("Load Linux."));
430
 
  cmd_initrd =
431
 
    grub_register_command ("initrd16", grub_cmd_initrd,
432
 
                           0, N_("Load initrd."));
433
 
  my_mod = mod;
434
 
}
435
 
 
436
 
GRUB_MOD_FINI(linux16)
437
 
{
438
 
  grub_unregister_command (cmd_linux);
439
 
  grub_unregister_command (cmd_initrd);
440
 
}