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

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Robert Millan
  • Date: 2009-07-02 13:23:51 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702132351-tanpn0ryyijp93gu
Tags: 1.96+20090702-1
* New SVN snapshot.
* rules: Remove duplicated files in sparc64-ieee1275 port.
* rules: Comment out -DGRUB_ASSUME_LINUX_HAS_FB_SUPPORT=1 setting.  We'll
  re-evaluate using it when it's more mature.  (Closes: #535026).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* multiboot.c - boot a multiboot OS image. */
2
 
/*
3
 
 *  GRUB  --  GRand Unified Bootloader
4
 
 *  Copyright (C) 2003,2004,2005,2007  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
 
/* 
21
 
 *  FIXME: The following features from the Multiboot specification still
22
 
 *         need to be implemented:
23
 
 *  - VBE support
24
 
 *  - a.out support
25
 
 *  - boot device
26
 
 *  - symbol table
27
 
 *  - memory map
28
 
 *  - drives table
29
 
 *  - ROM configuration table
30
 
 *  - APM table
31
 
 */
32
 
 
33
 
#include <grub/loader.h>
34
 
#include <grub/machine/loader.h>
35
 
#include <grub/multiboot.h>
36
 
#include <grub/machine/init.h>
37
 
#include <grub/machine/memory.h>
38
 
#include <grub/elf.h>
39
 
#include <grub/file.h>
40
 
#include <grub/err.h>
41
 
#include <grub/rescue.h>
42
 
#include <grub/dl.h>
43
 
#include <grub/mm.h>
44
 
#include <grub/misc.h>
45
 
#include <grub/gzio.h>
46
 
 
47
 
extern grub_dl_t my_mod;
48
 
static struct grub_multiboot_info *mbi;
49
 
static grub_addr_t entry;
50
 
 
51
 
static grub_err_t
52
 
grub_multiboot_boot (void)
53
 
{
54
 
  grub_multiboot_real_boot (entry, mbi);
55
 
 
56
 
  /* Not reached.  */
57
 
  return GRUB_ERR_NONE;
58
 
}
59
 
 
60
 
static grub_err_t
61
 
grub_multiboot_unload (void)
62
 
{
63
 
  if (mbi)
64
 
    {
65
 
      unsigned int i;
66
 
      for (i = 0; i < mbi->mods_count; i++)
67
 
        {
68
 
          grub_free ((void *)
69
 
                     ((struct grub_mod_list *) mbi->mods_addr)[i].mod_start);
70
 
          grub_free ((void *)
71
 
                     ((struct grub_mod_list *) mbi->mods_addr)[i].cmdline);
72
 
        }
73
 
      grub_free ((void *) mbi->mods_addr);
74
 
      grub_free ((void *) mbi->cmdline);
75
 
      grub_free (mbi);
76
 
    }
77
 
 
78
 
 
79
 
  mbi = 0;
80
 
  grub_dl_unref (my_mod);
81
 
 
82
 
  return GRUB_ERR_NONE;
83
 
}
84
 
 
85
 
/* Check if BUFFER contains ELF32.  */
86
 
static int
87
 
grub_multiboot_is_elf32 (void *buffer)
88
 
{
89
 
  Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
90
 
  
91
 
  return ehdr->e_ident[EI_CLASS] == ELFCLASS32;
92
 
}
93
 
 
94
 
static grub_err_t
95
 
grub_multiboot_load_elf32 (grub_file_t file, void *buffer)
96
 
{
97
 
  Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
98
 
  Elf32_Phdr *phdr;
99
 
  int i;
100
 
 
101
 
  if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
102
 
    return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF class");
103
 
  
104
 
  if (grub_dl_check_header (ehdr, sizeof(Elf32_Ehdr)))
105
 
    return grub_error (GRUB_ERR_UNKNOWN_OS, "no valid ELF header found");
106
 
  
107
 
  if (ehdr->e_type != ET_EXEC)
108
 
    return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF file type");
109
 
  
110
 
  /* FIXME: Should we support program headers at strange locations?  */
111
 
  if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
112
 
    return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
113
 
  
114
 
  entry = ehdr->e_entry;
115
 
  
116
 
  /* Load every loadable segment in memory.  */
117
 
  for (i = 0; i < ehdr->e_phnum; i++)
118
 
    {
119
 
      phdr = (Elf32_Phdr *) ((char *) buffer + ehdr->e_phoff
120
 
                             + i * ehdr->e_phentsize);
121
 
      if (phdr->p_type == PT_LOAD)
122
 
        {
123
 
          /* The segment should fit in the area reserved for the OS.  */
124
 
          if (phdr->p_paddr < grub_os_area_addr)
125
 
            return grub_error (GRUB_ERR_BAD_OS,
126
 
                               "segment doesn't fit in memory reserved for the OS (0x%lx < 0x%lx)",
127
 
                               phdr->p_paddr, grub_os_area_addr);
128
 
          if (phdr->p_paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size)
129
 
            return grub_error (GRUB_ERR_BAD_OS,
130
 
                               "segment doesn't fit in memory reserved for the OS (0x%lx > 0x%lx)",
131
 
                               phdr->p_paddr + phdr->p_memsz,
132
 
                               grub_os_area_addr + grub_os_area_size);
133
 
 
134
 
          if (grub_file_seek (file, (grub_off_t) phdr->p_offset)
135
 
              == (grub_off_t) -1)
136
 
            return grub_error (GRUB_ERR_BAD_OS,
137
 
                               "invalid offset in program header");
138
 
          
139
 
          if (grub_file_read (file, (void *) phdr->p_paddr, phdr->p_filesz)
140
 
              != (grub_ssize_t) phdr->p_filesz)
141
 
            return grub_error (GRUB_ERR_BAD_OS,
142
 
                               "couldn't read segment from file");
143
 
 
144
 
          if (phdr->p_filesz < phdr->p_memsz)
145
 
            grub_memset ((char *) phdr->p_paddr + phdr->p_filesz, 0,
146
 
                         phdr->p_memsz - phdr->p_filesz);
147
 
        }
148
 
    }
149
 
  
150
 
  return grub_errno;
151
 
}
152
 
 
153
 
/* Check if BUFFER contains ELF64.  */
154
 
static int
155
 
grub_multiboot_is_elf64 (void *buffer)
156
 
{
157
 
  Elf64_Ehdr *ehdr = (Elf64_Ehdr *) buffer;
158
 
  
159
 
  return ehdr->e_ident[EI_CLASS] == ELFCLASS64;
160
 
}
161
 
 
162
 
static grub_err_t
163
 
grub_multiboot_load_elf64 (grub_file_t file, void *buffer)
164
 
{
165
 
  Elf64_Ehdr *ehdr = (Elf64_Ehdr *) buffer;
166
 
  Elf64_Phdr *phdr;
167
 
  int i;
168
 
 
169
 
  if (ehdr->e_ident[EI_CLASS] != ELFCLASS64)
170
 
    return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF class");
171
 
 
172
 
  if (ehdr->e_ident[EI_MAG0] != ELFMAG0
173
 
      || ehdr->e_ident[EI_MAG1] != ELFMAG1
174
 
      || ehdr->e_ident[EI_MAG2] != ELFMAG2
175
 
      || ehdr->e_ident[EI_MAG3] != ELFMAG3
176
 
      || ehdr->e_version != EV_CURRENT
177
 
      || ehdr->e_ident[EI_DATA] != ELFDATA2LSB
178
 
      || ehdr->e_machine != EM_X86_64)
179
 
    return grub_error(GRUB_ERR_UNKNOWN_OS, "no valid ELF header found");
180
 
 
181
 
  if (ehdr->e_type != ET_EXEC)
182
 
    return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF file type");
183
 
 
184
 
  /* FIXME: Should we support program headers at strange locations?  */
185
 
  if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
186
 
    return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
187
 
 
188
 
  /* We still in 32-bit mode */
189
 
  if (ehdr->e_entry > 0xffffffff)
190
 
    return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
191
 
 
192
 
  entry = ehdr->e_entry;
193
 
 
194
 
  /* Load every loadable segment in memory.  */
195
 
  for (i = 0; i < ehdr->e_phnum; i++)
196
 
    {
197
 
      phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff
198
 
                             + i * ehdr->e_phentsize);
199
 
      if (phdr->p_type == PT_LOAD)
200
 
        {
201
 
          /* The segment should fit in the area reserved for the OS.  */
202
 
          if (phdr->p_paddr < (grub_uint64_t) grub_os_area_addr)
203
 
            return grub_error (GRUB_ERR_BAD_OS,
204
 
                               "segment doesn't fit in memory reserved for the OS (0x%lx < 0x%lx)",
205
 
                               phdr->p_paddr, (grub_uint64_t) grub_os_area_addr);
206
 
          if (phdr->p_paddr + phdr->p_memsz
207
 
                  > (grub_uint64_t) grub_os_area_addr + (grub_uint64_t) grub_os_area_size)
208
 
            return grub_error (GRUB_ERR_BAD_OS,
209
 
                               "segment doesn't fit in memory reserved for the OS (0x%lx > 0x%lx)",
210
 
                               phdr->p_paddr + phdr->p_memsz,
211
 
                               (grub_uint64_t) grub_os_area_addr + (grub_uint64_t) grub_os_area_size);
212
 
          
213
 
          if (grub_file_seek (file, (grub_off_t) phdr->p_offset)
214
 
              == (grub_off_t) -1)
215
 
            return grub_error (GRUB_ERR_BAD_OS,
216
 
                               "invalid offset in program header");
217
 
 
218
 
          if (grub_file_read (file, (void *) ((grub_uint32_t) phdr->p_paddr),
219
 
                              phdr->p_filesz)
220
 
              != (grub_ssize_t) phdr->p_filesz)
221
 
            return grub_error (GRUB_ERR_BAD_OS,
222
 
                               "couldn't read segment from file");
223
 
          
224
 
          if (phdr->p_filesz < phdr->p_memsz)
225
 
            grub_memset (((char *) ((grub_uint32_t) phdr->p_paddr)
226
 
                          + phdr->p_filesz),
227
 
                         0,
228
 
                         phdr->p_memsz - phdr->p_filesz);
229
 
        }
230
 
    }
231
 
  
232
 
  return grub_errno;
233
 
}
234
 
 
235
 
/* Load ELF32 or ELF64.  */
236
 
static grub_err_t
237
 
grub_multiboot_load_elf (grub_file_t file, void *buffer)
238
 
{
239
 
  if (grub_multiboot_is_elf32 (buffer))
240
 
    return grub_multiboot_load_elf32 (file, buffer);
241
 
  else if (grub_multiboot_is_elf64 (buffer))
242
 
    return grub_multiboot_load_elf64 (file, buffer);
243
 
  
244
 
  return grub_error (GRUB_ERR_UNKNOWN_OS, "unknown ELF class");
245
 
}
246
 
 
247
 
void
248
 
grub_multiboot (int argc, char *argv[])
249
 
{
250
 
  grub_file_t file = 0;
251
 
  char buffer[MULTIBOOT_SEARCH], *cmdline = 0, *p;
252
 
  struct grub_multiboot_header *header;
253
 
  grub_ssize_t len;
254
 
  int i;
255
 
 
256
 
  grub_loader_unset ();
257
 
    
258
 
  if (argc == 0)
259
 
    {
260
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, "No kernel specified");
261
 
      goto fail;
262
 
    }
263
 
 
264
 
  file = grub_gzfile_open (argv[0], 1);
265
 
  if (! file)
266
 
    {
267
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, "Couldn't open file");
268
 
      goto fail;
269
 
    }
270
 
 
271
 
  len = grub_file_read (file, buffer, MULTIBOOT_SEARCH);
272
 
  if (len < 32)
273
 
    {
274
 
      grub_error (GRUB_ERR_BAD_OS, "File too small");
275
 
      goto fail;
276
 
    }
277
 
 
278
 
  /* Look for the multiboot header in the buffer.  The header should
279
 
     be at least 12 bytes and aligned on a 4-byte boundary.  */
280
 
  for (header = (struct grub_multiboot_header *) buffer; 
281
 
       ((char *) header <= buffer + len - 12) || (header = 0);
282
 
       header = (struct grub_multiboot_header *) ((char *) header + 4))
283
 
    {
284
 
      if (header->magic == MULTIBOOT_MAGIC 
285
 
          && !(header->magic + header->flags + header->checksum))
286
 
        break;
287
 
    }
288
 
  
289
 
  if (header == 0)
290
 
    {
291
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, "No multiboot header found");
292
 
      goto fail;
293
 
    }
294
 
 
295
 
  if (header->flags & MULTIBOOT_UNSUPPORTED)
296
 
    {
297
 
      grub_error (GRUB_ERR_UNKNOWN_OS,
298
 
                  "Unsupported flag: 0x%x", header->flags);
299
 
      goto fail;
300
 
    }
301
 
 
302
 
  if (grub_multiboot_load_elf (file, buffer) != GRUB_ERR_NONE)
303
 
    goto fail;
304
 
  
305
 
  mbi = grub_malloc (sizeof (struct grub_multiboot_info));
306
 
  if (! mbi)
307
 
    goto fail;
308
 
 
309
 
  mbi->flags = MULTIBOOT_INFO_MEMORY;
310
 
 
311
 
  /* Convert from bytes to kilobytes.  */
312
 
  mbi->mem_lower = grub_lower_mem / 1024;
313
 
  mbi->mem_upper = grub_upper_mem / 1024;
314
 
 
315
 
  for (i = 0, len = 0; i < argc; i++)
316
 
    len += grub_strlen (argv[i]) + 1;
317
 
  
318
 
  cmdline = p = grub_malloc (len);
319
 
  if (! cmdline)
320
 
    goto fail;
321
 
  
322
 
  for (i = 0; i < argc; i++)
323
 
    {
324
 
      p = grub_stpcpy (p, argv[i]);
325
 
      *(p++) = ' ';
326
 
    }
327
 
  
328
 
  /* Remove the space after the last word.  */
329
 
  *(--p) = '\0';
330
 
  
331
 
  mbi->flags |= MULTIBOOT_INFO_CMDLINE;
332
 
  mbi->cmdline = (grub_uint32_t) cmdline;
333
 
 
334
 
  mbi->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME;
335
 
  mbi->boot_loader_name = (grub_uint32_t) grub_strdup (PACKAGE_STRING);
336
 
 
337
 
  grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 1);
338
 
 
339
 
 fail:
340
 
  if (file)
341
 
    grub_file_close (file);
342
 
 
343
 
  if (grub_errno != GRUB_ERR_NONE)
344
 
    {
345
 
      grub_free (cmdline);
346
 
      grub_free (mbi);
347
 
      grub_dl_unref (my_mod);
348
 
    }
349
 
}
350
 
 
351
 
 
352
 
void
353
 
grub_module  (int argc, char *argv[])
354
 
{
355
 
  grub_file_t file = 0;
356
 
  grub_ssize_t size, len = 0;
357
 
  char *module = 0, *cmdline = 0, *p;
358
 
  int i;
359
 
 
360
 
  if (argc == 0)
361
 
    {
362
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, "No module specified");
363
 
      goto fail;
364
 
    }
365
 
 
366
 
  if (!mbi)
367
 
    {
368
 
      grub_error (GRUB_ERR_BAD_ARGUMENT, 
369
 
                  "You need to load the multiboot kernel first");
370
 
      goto fail;
371
 
    }
372
 
 
373
 
  file = grub_gzfile_open (argv[0], 1);
374
 
  if (! file)
375
 
    goto fail;
376
 
 
377
 
  size = grub_file_size (file);
378
 
  module = grub_memalign (MULTIBOOT_MOD_ALIGN, size);
379
 
  if (! module)
380
 
    goto fail;
381
 
 
382
 
  if (grub_file_read (file, module, size) != size)
383
 
    {
384
 
      grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
385
 
      goto fail;
386
 
    }
387
 
  
388
 
  for (i = 0; i < argc; i++)
389
 
    len += grub_strlen (argv[i]) + 1;
390
 
  
391
 
  cmdline = p = grub_malloc (len);
392
 
  if (! cmdline)
393
 
    goto fail;
394
 
  
395
 
  for (i = 0; i < argc; i++)
396
 
    {
397
 
      p = grub_stpcpy (p, argv[i]);
398
 
      *(p++) = ' ';
399
 
    }
400
 
  
401
 
  /* Remove the space after the last word.  */
402
 
  *(--p) = '\0';
403
 
 
404
 
  if (mbi->flags & MULTIBOOT_INFO_MODS)
405
 
    {
406
 
      struct grub_mod_list *modlist = (struct grub_mod_list *) mbi->mods_addr;
407
 
 
408
 
      modlist = grub_realloc (modlist, (mbi->mods_count + 1) 
409
 
                                       * sizeof (struct grub_mod_list));
410
 
      if (! modlist)
411
 
        goto fail;
412
 
      mbi->mods_addr = (grub_uint32_t) modlist;
413
 
      modlist += mbi->mods_count;
414
 
      modlist->mod_start = (grub_uint32_t) module;
415
 
      modlist->mod_end = (grub_uint32_t) module + size;
416
 
      modlist->cmdline = (grub_uint32_t) cmdline;
417
 
      modlist->pad = 0;
418
 
      mbi->mods_count++;
419
 
    }
420
 
  else
421
 
    {
422
 
      struct grub_mod_list *modlist = grub_malloc (sizeof (struct grub_mod_list));
423
 
      if (! modlist)
424
 
        goto fail;
425
 
      modlist->mod_start = (grub_uint32_t) module;
426
 
      modlist->mod_end = (grub_uint32_t) module + size;
427
 
      modlist->cmdline = (grub_uint32_t) cmdline;
428
 
      modlist->pad = 0;
429
 
      mbi->mods_count = 1;
430
 
      mbi->mods_addr = (grub_uint32_t) modlist;
431
 
      mbi->flags |= MULTIBOOT_INFO_MODS;
432
 
    }
433
 
 
434
 
 fail:
435
 
  if (file)
436
 
    grub_file_close (file);
437
 
 
438
 
  if (grub_errno != GRUB_ERR_NONE)
439
 
    {
440
 
      grub_free (module);
441
 
      grub_free (cmdline);
442
 
    }
443
 
}