~ubuntu-branches/ubuntu/trusty/grub2/trusty

1.26.4 by Colin Watson
Import upstream version 1.99~20101122
1
/* linux.c - boot Linux */
2
/*
3
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2003, 2004, 2005, 2007, 2009  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/ieee1275/ieee1275.h>
27
#include <grub/command.h>
28
#include <grub/i18n.h>
29
#include <grub/memory.h>
1.17.12 by Colin Watson
Import upstream version 1.99~20110111
30
#include <grub/lib/cmdline.h>
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
31
#include <grub/linux.h>
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
32
1.17.15 by Colin Watson
Import upstream version 1.99
33
GRUB_MOD_LICENSE ("GPLv3+");
34
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
35
static grub_dl_t my_mod;
36
37
static int loaded;
38
39
/* /virtual-memory/translations property layout  */
40
struct grub_ieee1275_translation {
41
  grub_uint64_t vaddr;
42
  grub_uint64_t size;
43
  grub_uint64_t data;
44
};
45
46
static struct grub_ieee1275_translation *of_trans;
47
static int of_num_trans;
48
49
static grub_addr_t phys_base;
50
static grub_addr_t grub_phys_start;
51
static grub_addr_t grub_phys_end;
52
53
static grub_addr_t initrd_addr;
54
static grub_addr_t initrd_paddr;
55
static grub_size_t initrd_size;
56
57
static Elf64_Addr linux_entry;
58
static grub_addr_t linux_addr;
59
static grub_addr_t linux_paddr;
60
static grub_size_t linux_size;
61
62
static char *linux_args;
63
64
struct linux_bootstr_info {
65
	int len, valid;
66
	char buf[];
67
};
68
69
struct linux_hdrs {
70
	/* All HdrS versions support these fields.  */
71
	unsigned int start_insns[2];
72
	char magic[4]; /* "HdrS" */
73
	unsigned int linux_kernel_version; /* LINUX_VERSION_CODE */
74
	unsigned short hdrs_version;
75
	unsigned short root_flags;
76
	unsigned short root_dev;
77
	unsigned short ram_flags;
78
	unsigned int __deprecated_ramdisk_image;
79
	unsigned int ramdisk_size;
80
81
	/* HdrS versions 0x0201 and higher only */
82
	char *reboot_command;
83
84
	/* HdrS versions 0x0202 and higher only */
85
	struct linux_bootstr_info *bootstr_info;
86
87
	/* HdrS versions 0x0301 and higher only */
88
	unsigned long ramdisk_image;
89
};
90
91
static grub_err_t
92
grub_linux_boot (void)
93
{
94
  struct linux_bootstr_info *bp;
95
  struct linux_hdrs *hp;
96
  grub_addr_t addr;
97
98
  hp = (struct linux_hdrs *) linux_addr;
99
100
  /* Any pointer we dereference in the kernel image must be relocated
101
     to where we actually loaded the kernel.  */
102
  addr = (grub_addr_t) hp->bootstr_info;
103
  addr += (linux_addr - linux_entry);
104
  bp = (struct linux_bootstr_info *) addr;
105
106
  /* Set the command line arguments, unless the kernel has been
107
     built with a fixed CONFIG_CMDLINE.  */
108
  if (!bp->valid)
109
    {
110
      int len = grub_strlen (linux_args) + 1;
111
      if (bp->len < len)
112
	len = bp->len;
113
      memcpy(bp->buf, linux_args, len);
114
      bp->buf[len-1] = '\0';
115
      bp->valid = 1;
116
    }
117
118
  if (initrd_addr)
119
    {
120
      /* The kernel expects the physical address, adjusted relative
121
	 to the lowest address advertised in "/memory"'s available
122
	 property.
123
124
	 The history of this is that back when the kernel only supported
125
	 specifying a 32-bit ramdisk address, this was the way to still
126
	 be able to specify the ramdisk physical address even if memory
127
	 started at some place above 4GB.
128
129
	 The magic 0x400000 is KERNBASE, I have no idea why SILO adds
130
	 that term into the address, but it does and thus we have to do
131
	 it too as this is what the kernel expects.  */
132
      hp->ramdisk_image = initrd_paddr - phys_base + 0x400000;
133
      hp->ramdisk_size = initrd_size;
134
    }
135
136
  grub_dprintf ("loader", "Entry point: 0x%lx\n", linux_addr);
137
  grub_dprintf ("loader", "Initrd at: 0x%lx, size 0x%lx\n", initrd_addr,
138
		initrd_size);
139
  grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
140
  grub_dprintf ("loader", "Jumping to Linux...\n");
141
142
  /* Boot the kernel.  */
143
  asm volatile ("sethi	%hi(grub_ieee1275_entry_fn), %o1\n"
144
		"ldx	[%o1 + %lo(grub_ieee1275_entry_fn)], %o4\n"
145
		"sethi	%hi(grub_ieee1275_original_stack), %o1\n"
146
		"ldx	[%o1 + %lo(grub_ieee1275_original_stack)], %o6\n"
147
		"sethi	%hi(linux_addr), %o1\n"
148
		"ldx	[%o1 + %lo(linux_addr)], %o5\n"
149
		"mov    %g0, %o0\n"
150
		"mov    %g0, %o2\n"
151
		"mov    %g0, %o3\n"
152
		"jmp    %o5\n"
153
	        "mov    %g0, %o1\n");
154
155
  return GRUB_ERR_NONE;
156
}
157
158
static grub_err_t
159
grub_linux_release_mem (void)
160
{
161
  grub_free (linux_args);
162
  linux_args = 0;
163
  linux_addr = 0;
164
  initrd_addr = 0;
165
166
  return GRUB_ERR_NONE;
167
}
168
169
static grub_err_t
170
grub_linux_unload (void)
171
{
172
  grub_err_t err;
173
174
  err = grub_linux_release_mem ();
175
  grub_dl_unref (my_mod);
176
177
  loaded = 0;
178
179
  return err;
180
}
181
182
#define FOUR_MB	(4 * 1024 * 1024)
183
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
184
/* Context for alloc_phys.  */
185
struct alloc_phys_ctx
186
{
187
  grub_addr_t size;
188
  grub_addr_t ret;
189
};
190
191
/* Helper for alloc_phys.  */
192
static int
193
alloc_phys_choose (grub_uint64_t addr, grub_uint64_t len,
194
		   grub_memory_type_t type, void *data)
195
{
196
  struct alloc_phys_ctx *ctx = data;
197
  grub_addr_t end = addr + len;
198
199
  if (type != 1)
200
    return 0;
201
202
  addr = ALIGN_UP (addr, FOUR_MB);
203
  if (addr + ctx->size >= end)
204
    return 0;
205
206
  if (addr >= grub_phys_start && addr < grub_phys_end)
207
    {
208
      addr = ALIGN_UP (grub_phys_end, FOUR_MB);
209
      if (addr + ctx->size >= end)
210
	return 0;
211
    }
212
  if ((addr + ctx->size) >= grub_phys_start
213
      && (addr + ctx->size) < grub_phys_end)
214
    {
215
      addr = ALIGN_UP (grub_phys_end, FOUR_MB);
216
      if (addr + ctx->size >= end)
217
	return 0;
218
    }
219
220
  if (loaded)
221
    {
222
      grub_addr_t linux_end = ALIGN_UP (linux_paddr + linux_size, FOUR_MB);
223
224
      if (addr >= linux_paddr && addr < linux_end)
225
	{
226
	  addr = linux_end;
227
	  if (addr + ctx->size >= end)
228
	    return 0;
229
	}
230
      if ((addr + ctx->size) >= linux_paddr
231
	  && (addr + ctx->size) < linux_end)
232
	{
233
	  addr = linux_end;
234
	  if (addr + ctx->size >= end)
235
	    return 0;
236
	}
237
    }
238
239
  ctx->ret = addr;
240
  return 1;
241
}
242
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
243
static grub_addr_t
244
alloc_phys (grub_addr_t size)
245
{
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
246
  struct alloc_phys_ctx ctx = {
247
    .size = size,
248
    .ret = (grub_addr_t) -1
249
  };
250
251
  grub_machine_mmap_iterate (alloc_phys_choose, &ctx);
252
253
  return ctx.ret;
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
254
}
255
256
static grub_err_t
1.17.16 by Colin Watson
Import upstream version 2.00
257
grub_linux_load64 (grub_elf_t elf, const char *filename)
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
258
{
259
  grub_addr_t off, paddr, base;
260
  int ret;
261
262
  linux_entry = elf->ehdr.ehdr64.e_entry;
263
  linux_addr = 0x40004000;
264
  off = 0x4000;
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
265
  linux_size = grub_elf64_size (elf, 0, 0);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
266
  if (linux_size == 0)
267
    return grub_errno;
268
269
  grub_dprintf ("loader", "Attempting to claim at 0x%lx, size 0x%lx.\n",
270
		linux_addr, linux_size);
271
272
  paddr = alloc_phys (linux_size + off);
273
  if (paddr == (grub_addr_t) -1)
274
    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
275
		       "couldn't allocate physical memory");
276
  ret = grub_ieee1275_map (paddr, linux_addr - off,
277
			   linux_size + off, IEEE1275_MAP_DEFAULT);
278
  if (ret)
279
    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
280
		       "couldn't map physical memory");
281
282
  grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
283
		linux_addr, paddr, linux_size);
284
285
  linux_paddr = paddr;
286
287
  base = linux_entry - off;
288
289
  /* Now load the segments into the area we claimed.  */
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
290
  return grub_elf64_load (elf, filename, (void *) (linux_addr - off - base), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
291
}
292
293
static grub_err_t
294
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
295
		int argc, char *argv[])
296
{
297
  grub_file_t file = 0;
298
  grub_elf_t elf = 0;
299
  int size;
300
301
  grub_dl_ref (my_mod);
302
303
  if (argc == 0)
304
    {
1.17.16 by Colin Watson
Import upstream version 2.00
305
      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
306
      goto out;
307
    }
308
309
  file = grub_file_open (argv[0]);
310
  if (!file)
311
    goto out;
312
1.17.16 by Colin Watson
Import upstream version 2.00
313
  elf = grub_elf_file (file, argv[0]);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
314
  if (! elf)
315
    goto out;
316
317
  if (elf->ehdr.ehdr32.e_type != ET_EXEC)
318
    {
319
      grub_error (GRUB_ERR_UNKNOWN_OS,
1.17.16 by Colin Watson
Import upstream version 2.00
320
		  N_("this ELF file is not of the right type"));
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
321
      goto out;
322
    }
323
324
  /* Release the previously used memory.  */
325
  grub_loader_unset ();
326
327
  if (grub_elf_is_elf64 (elf))
1.17.16 by Colin Watson
Import upstream version 2.00
328
    grub_linux_load64 (elf, argv[0]);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
329
  else
330
    {
1.17.16 by Colin Watson
Import upstream version 2.00
331
      grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
332
      goto out;
333
    }
334
1.17.12 by Colin Watson
Import upstream version 1.99~20110111
335
  size = grub_loader_cmdline_size(argc, argv);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
336
1.17.12 by Colin Watson
Import upstream version 1.99~20110111
337
  linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
338
  if (! linux_args)
339
    goto out;
340
1.17.12 by Colin Watson
Import upstream version 1.99~20110111
341
  /* Create kernel command line.  */
342
  grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
343
  grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
344
			      size);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
345
346
out:
347
  if (elf)
348
    grub_elf_close (elf);
349
  else if (file)
350
    grub_file_close (file);
351
352
  if (grub_errno != GRUB_ERR_NONE)
353
    {
354
      grub_linux_release_mem ();
355
      grub_dl_unref (my_mod);
356
      loaded = 0;
357
    }
358
  else
359
    {
360
      grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
361
      initrd_addr = 0;
362
      loaded = 1;
363
    }
364
365
  return grub_errno;
366
}
367
368
static grub_err_t
369
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
370
		 int argc, char *argv[])
371
{
1.17.16 by Colin Watson
Import upstream version 2.00
372
  grub_size_t size = 0;
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
373
  grub_addr_t paddr;
374
  grub_addr_t addr;
375
  int ret;
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
376
  struct grub_linux_initrd_context initrd_ctx;
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
377
378
  if (argc == 0)
379
    {
1.17.16 by Colin Watson
Import upstream version 2.00
380
      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
381
      goto fail;
382
    }
383
384
  if (!loaded)
385
    {
1.17.16 by Colin Watson
Import upstream version 2.00
386
      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
387
      goto fail;
388
    }
389
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
390
  if (grub_initrd_init (argc, argv, &initrd_ctx))
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
391
    goto fail;
392
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
393
  size = grub_get_initrd_size (&initrd_ctx);
1.17.16 by Colin Watson
Import upstream version 2.00
394
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
395
  addr = 0x60000000;
396
397
  paddr = alloc_phys (size);
398
  if (paddr == (grub_addr_t) -1)
399
    {
400
      grub_error (GRUB_ERR_OUT_OF_MEMORY,
401
		  "couldn't allocate physical memory");
402
      goto fail;
403
    }
404
  ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT);
405
  if (ret)
406
    {
407
      grub_error (GRUB_ERR_OUT_OF_MEMORY,
408
		  "couldn't map physical memory");
409
      goto fail;
410
    }
411
412
  grub_dprintf ("loader", "Loading initrd at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
413
		addr, paddr, size);
414
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
415
  if (grub_initrd_load (&initrd_ctx, argv, (void *) addr))
416
    goto fail;
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
417
418
  initrd_addr = addr;
419
  initrd_paddr = paddr;
420
  initrd_size = size;
421
422
 fail:
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
423
  grub_initrd_close (&initrd_ctx);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
424
425
  return grub_errno;
426
}
427
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
428
/* Helper for determine_phys_base.  */
429
static int
430
get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__ ((unused)),
431
	      grub_memory_type_t type, void *data __attribute__ ((unused)))
432
{
433
  if (type != 1)
434
    return 0;
435
  if (addr < phys_base)
436
    phys_base = addr;
437
  return 0;
438
}
439
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
440
static void
441
determine_phys_base (void)
442
{
443
  phys_base = ~(grub_uint64_t) 0;
1.17.17 by Colin Watson
Import upstream version 2.00+20131208
444
  grub_machine_mmap_iterate (get_physbase, NULL);
1.26.4 by Colin Watson
Import upstream version 1.99~20101122
445
}
446
447
static void
448
fetch_translations (void)
449
{
450
  grub_ieee1275_phandle_t node;
451
  grub_ssize_t actual;
452
  int i;
453
454
  if (grub_ieee1275_finddevice ("/virtual-memory", &node))
455
    {
456
      grub_printf ("Cannot find /virtual-memory node.\n");
457
      return;
458
    }
459
460
  if (grub_ieee1275_get_property_length (node, "translations", &actual))
461
    {
462
      grub_printf ("Cannot find /virtual-memory/translations size.\n");
463
      return;
464
    }
465
466
  of_trans = grub_malloc (actual);
467
  if (!of_trans)
468
    {
469
      grub_printf ("Cannot allocate translations buffer.\n");
470
      return;
471
    }
472
473
  if (grub_ieee1275_get_property (node, "translations", of_trans, actual, &actual))
474
    {
475
      grub_printf ("Cannot fetch /virtual-memory/translations property.\n");
476
      return;
477
    }
478
479
  of_num_trans = actual / sizeof(struct grub_ieee1275_translation);
480
481
  for (i = 0; i < of_num_trans; i++)
482
    {
483
      struct grub_ieee1275_translation *p = &of_trans[i];
484
485
      if (p->vaddr == 0x2000)
486
	{
487
	  grub_addr_t phys, tte = p->data;
488
489
	  phys = tte & ~(0xff00000000001fffULL);
490
491
	  grub_phys_start = phys;
492
	  grub_phys_end = grub_phys_start + p->size;
493
	  grub_dprintf ("loader", "Grub lives at phys_start[%lx] phys_end[%lx]\n",
494
			(unsigned long) grub_phys_start,
495
			(unsigned long) grub_phys_end);
496
	  break;
497
	}
498
    }
499
}
500
501

502
static grub_command_t cmd_linux, cmd_initrd;
503
504
GRUB_MOD_INIT(linux)
505
{
506
  determine_phys_base ();
507
  fetch_translations ();
508
509
  cmd_linux = grub_register_command ("linux", grub_cmd_linux,
510
				     0, N_("Load Linux."));
511
  cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
512
				      0, N_("Load initrd."));
513
  my_mod = mod;
514
}
515
516
GRUB_MOD_FINI(linux)
517
{
518
  grub_unregister_command (cmd_linux);
519
  grub_unregister_command (cmd_initrd);
520
}