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

« back to all changes in this revision

Viewing changes to grub-core/loader/i386/pc/linux.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

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