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

« back to all changes in this revision

Viewing changes to grub-core/loader/mips/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 */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2003,2004,2005,2007,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/elf.h>
 
21
#include <grub/elfload.h>
 
22
#include <grub/loader.h>
 
23
#include <grub/dl.h>
 
24
#include <grub/mm.h>
 
25
#include <grub/misc.h>
 
26
#include <grub/command.h>
 
27
#include <grub/mips/relocator.h>
 
28
#include <grub/memory.h>
 
29
#include <grub/i18n.h>
 
30
 
 
31
/* For frequencies.  */
 
32
#include <grub/pci.h>
 
33
#include <grub/machine/time.h>
 
34
 
 
35
#ifdef GRUB_MACHINE_MIPS_YEELOONG
 
36
/* This can be detected on runtime from PMON, but:
 
37
     a) it wouldn't work when GRUB is the firmware
 
38
   and
 
39
     b) for now we only support Yeeloong anyway.  */
 
40
#define LOONGSON_MACHTYPE "machtype=lemote-yeeloong-2f-8.9inches"
 
41
#endif
 
42
 
 
43
static grub_dl_t my_mod;
 
44
 
 
45
static int loaded;
 
46
 
 
47
static grub_size_t linux_size;
 
48
 
 
49
static struct grub_relocator *relocator;
 
50
static grub_uint8_t *playground;
 
51
static grub_addr_t target_addr, entry_addr;
 
52
static int linux_argc;
 
53
static grub_off_t argv_off, envp_off;
 
54
static grub_off_t rd_addr_arg_off, rd_size_arg_off;
 
55
static int initrd_loaded = 0;
 
56
 
 
57
static grub_err_t
 
58
grub_linux_boot (void)
 
59
{
 
60
  struct grub_relocator32_state state;
 
61
 
 
62
  /* Boot the kernel.  */
 
63
  state.gpr[1] = entry_addr;
 
64
  state.gpr[4] = linux_argc;
 
65
  state.gpr[5] = target_addr + argv_off;
 
66
  state.gpr[6] = target_addr + envp_off;
 
67
  state.jumpreg = 1;
 
68
  grub_relocator32_boot (relocator, state);
 
69
 
 
70
  return GRUB_ERR_NONE;
 
71
}
 
72
 
 
73
static grub_err_t
 
74
grub_linux_unload (void)
 
75
{
 
76
  grub_relocator_unload (relocator);
 
77
  grub_dl_unref (my_mod);
 
78
 
 
79
  loaded = 0;
 
80
 
 
81
  return GRUB_ERR_NONE;
 
82
}
 
83
 
 
84
static grub_err_t
 
85
grub_linux_load32 (grub_elf_t elf, void **extra_mem, grub_size_t extra_size)
 
86
{
 
87
  Elf32_Addr base;
 
88
  int extraoff;
 
89
  grub_err_t err;
 
90
 
 
91
  /* Linux's entry point incorrectly contains a virtual address.  */
 
92
  entry_addr = elf->ehdr.ehdr32.e_entry;
 
93
 
 
94
  linux_size = grub_elf32_size (elf, &base, 0);
 
95
  if (linux_size == 0)
 
96
    return grub_errno;
 
97
  target_addr = base;
 
98
  /* Pad it; the kernel scribbles over memory beyond its load address.  */
 
99
  linux_size += 0x100000;
 
100
  linux_size = ALIGN_UP (base + linux_size, 4) - base;
 
101
  extraoff = linux_size;
 
102
  linux_size += extra_size;
 
103
 
 
104
  relocator = grub_relocator_new ();
 
105
  if (!relocator)
 
106
    return grub_errno;
 
107
 
 
108
  {
 
109
    grub_relocator_chunk_t ch;
 
110
    err = grub_relocator_alloc_chunk_addr (relocator, &ch,
 
111
                                           target_addr & 0x1fffffff,
 
112
                                           linux_size);
 
113
    if (err)
 
114
      return err;
 
115
    playground = get_virtual_current_address (ch);
 
116
  }
 
117
 
 
118
  *extra_mem = playground + extraoff;
 
119
 
 
120
  /* Now load the segments into the area we claimed.  */
 
121
  auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load);
 
122
  grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load)
 
123
    {
 
124
      if (phdr->p_type != PT_LOAD)
 
125
        {
 
126
          *do_load = 0;
 
127
          return 0;
 
128
        }
 
129
      *do_load = 1;
 
130
 
 
131
      /* Linux's program headers incorrectly contain virtual addresses.
 
132
       * Translate those to physical, and offset to the area we claimed.  */
 
133
      *addr = (grub_addr_t) (phdr->p_paddr - base + playground);
 
134
      return 0;
 
135
    }
 
136
  return grub_elf32_load (elf, offset_phdr, 0, 0);
 
137
}
 
138
 
 
139
static grub_err_t
 
140
grub_linux_load64 (grub_elf_t elf, void **extra_mem, grub_size_t extra_size)
 
141
{
 
142
  Elf64_Addr base;
 
143
  int extraoff;
 
144
  grub_err_t err;
 
145
 
 
146
  /* Linux's entry point incorrectly contains a virtual address.  */
 
147
  entry_addr = elf->ehdr.ehdr64.e_entry;
 
148
 
 
149
  linux_size = grub_elf64_size (elf, &base, 0);
 
150
  if (linux_size == 0)
 
151
    return grub_errno;
 
152
  target_addr = base;
 
153
  /* Pad it; the kernel scribbles over memory beyond its load address.  */
 
154
  linux_size += 0x100000;
 
155
  linux_size = ALIGN_UP (base + linux_size, 4) - base;
 
156
  extraoff = linux_size;
 
157
  linux_size += extra_size;
 
158
 
 
159
  relocator = grub_relocator_new ();
 
160
  if (!relocator)
 
161
    return grub_errno;
 
162
 
 
163
  {
 
164
    grub_relocator_chunk_t ch;
 
165
    err = grub_relocator_alloc_chunk_addr (relocator, &ch,
 
166
                                           target_addr & 0x1fffffff,
 
167
                                           linux_size);
 
168
    if (err)
 
169
      return err;
 
170
    playground = get_virtual_current_address (ch);
 
171
  }
 
172
 
 
173
  *extra_mem = playground + extraoff;
 
174
 
 
175
  /* Now load the segments into the area we claimed.  */
 
176
  auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load);
 
177
  grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load)
 
178
    {
 
179
      if (phdr->p_type != PT_LOAD)
 
180
        {
 
181
          *do_load = 0;
 
182
          return 0;
 
183
        }
 
184
      *do_load = 1;
 
185
      /* Linux's program headers incorrectly contain virtual addresses.
 
186
       * Translate those to physical, and offset to the area we claimed.  */
 
187
      *addr = (grub_addr_t) (phdr->p_paddr - base + playground);
 
188
      return 0;
 
189
    }
 
190
  return grub_elf64_load (elf, offset_phdr, 0, 0);
 
191
}
 
192
 
 
193
static grub_err_t
 
194
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 
195
                int argc, char *argv[])
 
196
{
 
197
  grub_elf_t elf = 0;
 
198
  int i;
 
199
  int size;
 
200
  void *extra = NULL;
 
201
  grub_uint32_t *linux_argv, *linux_envp;
 
202
  char *linux_args, *linux_envs;
 
203
  grub_err_t err;
 
204
 
 
205
  if (argc == 0)
 
206
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
 
207
 
 
208
  elf = grub_elf_open (argv[0]);
 
209
  if (! elf)
 
210
    return grub_errno;
 
211
 
 
212
  if (elf->ehdr.ehdr32.e_type != ET_EXEC)
 
213
    {
 
214
      grub_elf_close (elf);
 
215
      return grub_error (GRUB_ERR_UNKNOWN_OS,
 
216
                         "this ELF file is not of the right type\n");
 
217
    }
 
218
 
 
219
  /* Release the previously used memory.  */
 
220
  grub_loader_unset ();
 
221
  loaded = 0;
 
222
 
 
223
  /* For arguments.  */
 
224
  linux_argc = argc;
 
225
#ifdef LOONGSON_MACHTYPE
 
226
  linux_argc++;
 
227
#endif
 
228
  /* Main arguments.  */
 
229
  size = (linux_argc) * sizeof (grub_uint32_t); 
 
230
  /* Initrd address and size.  */
 
231
  size += 2 * sizeof (grub_uint32_t); 
 
232
  /* NULL terminator.  */
 
233
  size += sizeof (grub_uint32_t); 
 
234
 
 
235
  /* First argument is always "a0".  */
 
236
  size += ALIGN_UP (sizeof ("a0"), 4);
 
237
  /* Normal arguments.  */
 
238
  for (i = 1; i < argc; i++)
 
239
    size += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
 
240
#ifdef LOONGSON_MACHTYPE
 
241
  size += ALIGN_UP (sizeof (LOONGSON_MACHTYPE), 4);
 
242
#endif
 
243
 
 
244
  /* rd arguments.  */
 
245
  size += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
 
246
  size += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
 
247
 
 
248
  /* For the environment.  */
 
249
  size += sizeof (grub_uint32_t);
 
250
  size += 4 * sizeof (grub_uint32_t);
 
251
  size += ALIGN_UP (sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"), 4)
 
252
    + ALIGN_UP (sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"), 4)
 
253
    + ALIGN_UP (sizeof ("busclock=XXXXXXXXXX"), 4)
 
254
    + ALIGN_UP (sizeof ("cpuclock=XXXXXXXXXX"), 4);
 
255
 
 
256
  if (grub_elf_is_elf32 (elf))
 
257
    err = grub_linux_load32 (elf, &extra, size);
 
258
  else
 
259
  if (grub_elf_is_elf64 (elf))
 
260
    err = grub_linux_load64 (elf, &extra, size);
 
261
  else
 
262
    err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "unknown ELF class");
 
263
 
 
264
  grub_elf_close (elf);
 
265
 
 
266
  if (err)
 
267
    return err;
 
268
 
 
269
  linux_argv = extra;
 
270
  argv_off = (grub_uint8_t *) linux_argv - (grub_uint8_t *) playground;
 
271
  extra = linux_argv + (linux_argc + 1 + 2);
 
272
  linux_args = extra;
 
273
 
 
274
  grub_memcpy (linux_args, "a0", sizeof ("a0"));
 
275
  *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
 
276
    + target_addr;
 
277
  linux_argv++;
 
278
  linux_args += ALIGN_UP (sizeof ("a0"), 4);
 
279
 
 
280
#ifdef LOONGSON_MACHTYPE
 
281
  /* In Loongson platform, it is the responsibility of the bootloader/firmware
 
282
     to supply the OS kernel with machine type information.  */
 
283
  grub_memcpy (linux_args, LOONGSON_MACHTYPE, sizeof (LOONGSON_MACHTYPE));
 
284
  *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
 
285
    + target_addr;
 
286
  linux_argv++;
 
287
  linux_args += ALIGN_UP (sizeof (LOONGSON_MACHTYPE), 4);
 
288
#endif
 
289
 
 
290
  for (i = 1; i < argc; i++)
 
291
    {
 
292
      grub_memcpy (linux_args, argv[i], grub_strlen (argv[i]) + 1);
 
293
      *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
 
294
        + target_addr;
 
295
      linux_argv++;
 
296
      linux_args += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
 
297
    }
 
298
 
 
299
  /* Reserve space for rd arguments.  */
 
300
  rd_addr_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
 
301
  linux_args += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
 
302
  *linux_argv = 0;
 
303
  linux_argv++;
 
304
 
 
305
  rd_size_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
 
306
  linux_args += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
 
307
  *linux_argv = 0;
 
308
  linux_argv++;
 
309
 
 
310
  *linux_argv = 0;
 
311
 
 
312
  extra = linux_args;
 
313
 
 
314
  linux_envp = extra;
 
315
  envp_off = (grub_uint8_t *) linux_envp - (grub_uint8_t *) playground;
 
316
  linux_envs = (char *) (linux_envp + 5);
 
317
  grub_snprintf (linux_envs, sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"),
 
318
                 "memsize=%lld",
 
319
                 (unsigned long long) grub_mmap_get_lower () >> 20);
 
320
  linux_envp[0] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
 
321
    + target_addr;
 
322
  linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
 
323
  grub_snprintf (linux_envs, sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"),
 
324
                 "highmemsize=%lld",
 
325
                 (unsigned long long) grub_mmap_get_upper () >> 20);
 
326
  linux_envp[1] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
 
327
    + target_addr;
 
328
  linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
 
329
 
 
330
  grub_snprintf (linux_envs, sizeof ("busclock=XXXXXXXXXX"),
 
331
                 "busclock=%d", grub_arch_busclock);
 
332
  linux_envp[2] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
 
333
    + target_addr;
 
334
  linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
 
335
  grub_snprintf (linux_envs, sizeof ("cpuclock=XXXXXXXXXX"),
 
336
                 "cpuclock=%d", grub_arch_cpuclock);
 
337
  linux_envp[3] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
 
338
    + target_addr;
 
339
  linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
 
340
 
 
341
 
 
342
  linux_envp[4] = 0;
 
343
 
 
344
  grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
 
345
  initrd_loaded = 0;
 
346
  loaded = 1;
 
347
  grub_dl_ref (my_mod);
 
348
 
 
349
  return GRUB_ERR_NONE;
 
350
}
 
351
 
 
352
static grub_err_t
 
353
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
 
354
                 int argc, char *argv[])
 
355
{
 
356
  grub_file_t file = 0;
 
357
  grub_ssize_t size;
 
358
  void *initrd_src;
 
359
  grub_addr_t initrd_dest;
 
360
  grub_err_t err;
 
361
 
 
362
  if (argc == 0)
 
363
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no initrd specified");
 
364
 
 
365
  if (!loaded)
 
366
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load Linux first.");
 
367
 
 
368
  if (initrd_loaded)
 
369
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd can be loaded.");
 
370
 
 
371
  grub_file_filter_disable_compression ();
 
372
  file = grub_file_open (argv[0]);
 
373
  if (! file)
 
374
    return grub_errno;
 
375
 
 
376
  size = grub_file_size (file);
 
377
 
 
378
  {
 
379
    grub_relocator_chunk_t ch;
 
380
 
 
381
    err = grub_relocator_alloc_chunk_align (relocator, &ch,
 
382
                                            target_addr + linux_size + 0x10000,
 
383
                                            (0xffffffff - size) + 1,
 
384
                                            size, 0x10000,
 
385
                                            GRUB_RELOCATOR_PREFERENCE_NONE);
 
386
 
 
387
    if (err)
 
388
      {
 
389
        grub_file_close (file);
 
390
        return err;
 
391
      }
 
392
    initrd_src = get_virtual_current_address (ch);
 
393
    initrd_dest = get_physical_target_address (ch) | 0x80000000;
 
394
  }
 
395
 
 
396
  if (grub_file_read (file, initrd_src, size) != size)
 
397
    {
 
398
      grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
 
399
      grub_file_close (file);
 
400
 
 
401
      return grub_errno;
 
402
    }
 
403
 
 
404
  grub_snprintf ((char *) playground + rd_addr_arg_off,
 
405
                 sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), "rd_start=0x%llx",
 
406
                (unsigned long long) initrd_dest);
 
407
  ((grub_uint32_t *) (playground + argv_off))[linux_argc]
 
408
    = target_addr + rd_addr_arg_off;
 
409
  linux_argc++;
 
410
 
 
411
  grub_snprintf ((char *) playground + rd_size_arg_off,
 
412
                sizeof ("rd_size=0xXXXXXXXXXXXXXXXXX"), "rd_size=0x%llx",
 
413
                (unsigned long long) size);
 
414
  ((grub_uint32_t *) (playground + argv_off))[linux_argc]
 
415
    = target_addr + rd_size_arg_off;
 
416
  linux_argc++;
 
417
 
 
418
  initrd_loaded = 1;
 
419
 
 
420
  grub_file_close (file);
 
421
 
 
422
  return GRUB_ERR_NONE;
 
423
}
 
424
 
 
425
static grub_command_t cmd_linux, cmd_initrd;
 
426
 
 
427
GRUB_MOD_INIT(linux)
 
428
{
 
429
  cmd_linux = grub_register_command ("linux", grub_cmd_linux,
 
430
                                     0, N_("Load Linux."));
 
431
  cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
 
432
                                      0, N_("Load initrd."));
 
433
  my_mod = mod;
 
434
}
 
435
 
 
436
GRUB_MOD_FINI(linux)
 
437
{
 
438
  grub_unregister_command (cmd_linux);
 
439
  grub_unregister_command (cmd_initrd);
 
440
}