linux.c 30.8 KB
Newer Older
1 2
/*
 *  GRUB  --  GRand Unified Bootloader
3
 *  Copyright (C) 2006,2007,2008,2009,2010  Free Software Foundation, Inc.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/loader.h>
20
#include <grub/memory.h>
21
#include <grub/normal.h>
22 23 24 25 26 27 28 29 30
#include <grub/file.h>
#include <grub/disk.h>
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/term.h>
#include <grub/cpu/linux.h>
31
#include <grub/video.h>
32
#include <grub/video_fb.h>
bean's avatar
bean committed
33
#include <grub/command.h>
34
#include <grub/i386/relocator.h>
35
#include <grub/i18n.h>
36
#include <grub/lib/cmdline.h>
37
#include <grub/linux.h>
38

39 40
GRUB_MOD_LICENSE ("GPLv3+");

41 42 43 44
#ifdef GRUB_MACHINE_PCBIOS
#include <grub/i386/pc/vesa_modes_table.h>
#endif

45 46 47
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#define HAS_VGA_TEXT 0
48
#define DEFAULT_VIDEO_MODE "auto"
49
#define ACCEPTS_PURE_TEXT 0
50 51 52 53
#elif defined (GRUB_MACHINE_IEEE1275)
#include <grub/ieee1275/ieee1275.h>
#define HAS_VGA_TEXT 0
#define DEFAULT_VIDEO_MODE "text"
54
#define ACCEPTS_PURE_TEXT 1
55 56 57 58 59
#else
#include <grub/i386/pc/vbe.h>
#include <grub/i386/pc/console.h>
#define HAS_VGA_TEXT 1
#define DEFAULT_VIDEO_MODE "text"
60
#define ACCEPTS_PURE_TEXT 1
61 62
#endif

63 64 65 66 67
static grub_dl_t my_mod;

static grub_size_t linux_mem_size;
static int loaded;
static void *prot_mode_mem;
68
static grub_addr_t prot_mode_target;
69
static void *initrd_mem;
70
static grub_addr_t initrd_mem_target;
71
static grub_size_t prot_init_space;
72
static struct grub_relocator *relocator = NULL;
73
static void *efi_mmap_buf;
74
static grub_size_t maximal_cmdline_size;
75 76
static struct linux_kernel_params linux_params;
static char *linux_cmdline;
77 78 79 80 81
#ifdef GRUB_MACHINE_EFI
static grub_efi_uintn_t efi_mmap_size;
#else
static const grub_size_t efi_mmap_size = 0;
#endif
82

83 84
/* FIXME */
#if 0
85 86 87 88
struct idt_descriptor
{
  grub_uint16_t limit;
  void *base;
89
} GRUB_PACKED;
90 91 92 93 94 95

static struct idt_descriptor idt_desc =
  {
    0,
    0
  };
96
#endif
97 98 99 100 101 102 103

static inline grub_size_t
page_align (grub_size_t size)
{
  return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
}

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
#ifdef GRUB_MACHINE_EFI
/* Find the optimal number of pages for the memory map. Is it better to
   move this code to efi/mm.c?  */
static grub_efi_uintn_t
find_efi_mmap_size (void)
{
  static grub_efi_uintn_t mmap_size = 0;

  if (mmap_size != 0)
    return mmap_size;

  mmap_size = (1 << 12);
  while (1)
    {
      int ret;
      grub_efi_memory_descriptor_t *mmap;
      grub_efi_uintn_t desc_size;
121
      grub_efi_uintn_t cur_mmap_size = mmap_size;
122

123
      mmap = grub_malloc (cur_mmap_size);
124 125 126
      if (! mmap)
	return 0;

127
      ret = grub_efi_get_memory_map (&cur_mmap_size, mmap, 0, &desc_size, 0);
128 129 130
      grub_free (mmap);

      if (ret < 0)
131 132 133 134
	{
	  grub_error (GRUB_ERR_IO, "cannot get memory map");
	  return 0;
	}
135 136 137
      else if (ret > 0)
	break;

138 139
      if (mmap_size < cur_mmap_size)
	mmap_size = cur_mmap_size;
140 141 142 143 144
      mmap_size += (1 << 12);
    }

  /* Increase the size a bit for safety, because GRUB allocates more on
     later, and EFI itself may allocate more.  */
145
  mmap_size += (3 << 12);
146

147 148
  mmap_size = page_align (mmap_size);
  return mmap_size;
149 150 151 152
}

#endif

153 154 155 156 157 158 159 160 161 162 163 164
/* Helper for find_mmap_size.  */
static int
count_hook (grub_uint64_t addr __attribute__ ((unused)),
	    grub_uint64_t size __attribute__ ((unused)),
	    grub_memory_type_t type __attribute__ ((unused)), void *data)
{
  grub_size_t *count = data;

  (*count)++;
  return 0;
}

165 166 167 168 169 170
/* Find the optimal number of pages for the memory map. */
static grub_size_t
find_mmap_size (void)
{
  grub_size_t count = 0, mmap_size;

171
  grub_mmap_iterate (count_hook, &count);
fzielcke's avatar
fzielcke committed
172

173 174 175 176 177
  mmap_size = count * sizeof (struct grub_e820_mmap);

  /* Increase the size a bit for safety, because GRUB allocates more on
     later.  */
  mmap_size += (1 << 12);
fzielcke's avatar
fzielcke committed
178

179 180 181 182 183 184
  return page_align (mmap_size);
}

static void
free_pages (void)
{
185 186
  grub_relocator_unload (relocator);
  relocator = NULL;
187 188
  prot_mode_mem = initrd_mem = 0;
  prot_mode_target = initrd_mem_target = 0;
189 190 191 192
}

/* Allocate pages for the real mode code and the protected mode code
   for linux as well as a memory map buffer.  */
193
static grub_err_t
194 195
allocate_pages (grub_size_t prot_size, grub_size_t *align,
		grub_size_t min_align, int relocatable,
196
		grub_uint64_t preferred_address)
197
{
198
  grub_err_t err;
199

200 201 202
  if (prot_size == 0)
    prot_size = 1;

203
  prot_size = page_align (prot_size);
fzielcke's avatar
fzielcke committed
204

205
  /* Initialize the memory pointers with NULL for convenience.  */
206
  free_pages ();
fzielcke's avatar
fzielcke committed
207

208 209 210 211 212 213 214
  relocator = grub_relocator_new ();
  if (!relocator)
    {
      err = grub_errno;
      goto fail;
    }

215 216
  /* FIXME: Should request low memory from the heap when this feature is
     implemented.  */
217

218 219
  {
    grub_relocator_chunk_t ch;
220 221 222
    if (relocatable)
      {
	err = grub_relocator_alloc_chunk_align (relocator, &ch,
223 224
						preferred_address,
						preferred_address,
225 226 227
						prot_size, 1,
						GRUB_RELOCATOR_PREFERENCE_LOW,
						1);
228
	for (; err && *align + 1 > min_align; (*align)--)
229 230 231
	  {
	    grub_errno = GRUB_ERR_NONE;
	    err = grub_relocator_alloc_chunk_align (relocator, &ch,
232 233
						    0x1000000,
						    0xffffffff & ~prot_size,
234 235 236 237 238 239 240 241 242
						    prot_size, 1 << *align,
						    GRUB_RELOCATOR_PREFERENCE_LOW,
						    1);
	  }
	if (err)
	  goto fail;
      }
    else
      err = grub_relocator_alloc_chunk_addr (relocator, &ch,
243
					     preferred_address,
244
					     prot_size);
245 246 247
    if (err)
      goto fail;
    prot_mode_mem = get_virtual_current_address (ch);
248
    prot_mode_target = get_physical_target_address (ch);
249
  }
250

251 252
  grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n",
                prot_mode_mem, (unsigned long) prot_mode_target,
253
		(unsigned) prot_size);
254
  return GRUB_ERR_NONE;
255 256 257

 fail:
  free_pages ();
258
  return err;
259 260
}

261
static grub_err_t
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
                      grub_uint64_t start, grub_uint64_t size,
                      grub_uint32_t type)
{
  int n = *e820_num;

  if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
      (e820_map[n - 1].type == type))
      e820_map[n - 1].size += size;
  else
    {
      e820_map[n].addr = start;
      e820_map[n].size = size;
      e820_map[n].type = type;
      (*e820_num)++;
    }
278
  return GRUB_ERR_NONE;
279 280
}

281
static grub_err_t
282 283 284
grub_linux_setup_video (struct linux_kernel_params *params)
{
  struct grub_video_mode_info mode_info;
285
  void *framebuffer;
286
  grub_err_t err;
287
  grub_video_driver_id_t driver_id;
288
  const char *gfxlfbvar = grub_env_get ("gfxpayloadforcelfb");
289 290 291 292 293

  driver_id = grub_video_get_driver_id ();

  if (driver_id == GRUB_VIDEO_DRIVER_NONE)
    return 1;
294 295

  err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
296

297
  if (err)
298 299 300 301
    {
      grub_errno = GRUB_ERR_NONE;
      return 1;
    }
302 303 304 305 306 307

  params->lfb_width = mode_info.width;
  params->lfb_height = mode_info.height;
  params->lfb_depth = mode_info.bpp;
  params->lfb_line_len = mode_info.pitch;

308
  params->lfb_base = (grub_size_t) framebuffer;
309
  params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height, 65536);
310

311 312 313 314 315 316 317 318
  params->red_mask_size = mode_info.red_mask_size;
  params->red_field_pos = mode_info.red_field_pos;
  params->green_mask_size = mode_info.green_mask_size;
  params->green_field_pos = mode_info.green_field_pos;
  params->blue_mask_size = mode_info.blue_mask_size;
  params->blue_field_pos = mode_info.blue_field_pos;
  params->reserved_mask_size = mode_info.reserved_mask_size;
  params->reserved_field_pos = mode_info.reserved_field_pos;
319

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
  if (gfxlfbvar && (gfxlfbvar[0] == '1' || gfxlfbvar[0] == 'y'))
    params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
  else
    {
      switch (driver_id)
	{
	case GRUB_VIDEO_DRIVER_VBE:
	  params->lfb_size >>= 16;
	  params->have_vga = GRUB_VIDEO_LINUX_TYPE_VESA;
	  break;
	
	case GRUB_VIDEO_DRIVER_EFI_UGA:
	case GRUB_VIDEO_DRIVER_EFI_GOP:
	  params->have_vga = GRUB_VIDEO_LINUX_TYPE_EFIFB;
	  break;

	  /* FIXME: check if better id is available.  */
	case GRUB_VIDEO_DRIVER_SM712:
338
	case GRUB_VIDEO_DRIVER_SIS315PRO:
339 340 341
	case GRUB_VIDEO_DRIVER_VGA:
	case GRUB_VIDEO_DRIVER_CIRRUS:
	case GRUB_VIDEO_DRIVER_BOCHS:
342
	case GRUB_VIDEO_DRIVER_RADEON_FULOONG2E:
343
	case GRUB_VIDEO_DRIVER_RADEON_YEELOONG3A:
344 345
	case GRUB_VIDEO_DRIVER_IEEE1275:
	case GRUB_VIDEO_DRIVER_COREBOOT:
346
	  /* Make gcc happy. */
347
	case GRUB_VIDEO_DRIVER_XEN:
348 349
	case GRUB_VIDEO_DRIVER_SDL:
	case GRUB_VIDEO_DRIVER_NONE:
350
	case GRUB_VIDEO_ADAPTER_CAPTURE:
351 352 353 354
	  params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
	  break;
	}
    }
355 356 357 358 359

#ifdef GRUB_MACHINE_PCBIOS
  /* VESA packed modes may come with zeroed mask sizes, which need
     to be set here according to DAC Palette width.  If we don't,
     this results in Linux displaying a black screen.  */
360
  if (driver_id == GRUB_VIDEO_DRIVER_VBE && mode_info.bpp <= 8)
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    {
      struct grub_vbe_info_block controller_info;
      int status;
      int width = 8;

      status = grub_vbe_bios_get_controller_info (&controller_info);

      if (status == GRUB_VBE_STATUS_OK &&
	  (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
	status = grub_vbe_bios_set_dac_palette_width (&width);

      if (status != GRUB_VBE_STATUS_OK)
	/* 6 is default after mode reset.  */
	width = 6;

      params->red_mask_size = params->green_mask_size
	= params->blue_mask_size = width;
      params->reserved_mask_size = 0;
    }
#endif

382
  return GRUB_ERR_NONE;
383 384
}

385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
/* Context for grub_linux_boot.  */
struct grub_linux_boot_ctx
{
  grub_addr_t real_mode_target;
  grub_size_t real_size;
  struct linux_kernel_params *params;
  int e820_num;
};

/* Helper for grub_linux_boot.  */
static int
grub_linux_boot_mmap_find (grub_uint64_t addr, grub_uint64_t size,
			   grub_memory_type_t type, void *data)
{
  struct grub_linux_boot_ctx *ctx = data;

  /* We must put real mode code in the traditional space.  */
  if (type != GRUB_MEMORY_AVAILABLE || addr > 0x90000)
    return 0;

  if (addr + size < 0x10000)
    return 0;

  if (addr < 0x10000)
    {
      size += addr - 0x10000;
      addr = 0x10000;
    }

  if (addr + size > 0x90000)
    size = 0x90000 - addr;

  if (ctx->real_size + efi_mmap_size > size)
    return 0;

  grub_dprintf ("linux", "addr = %lx, size = %x, need_size = %x\n",
		(unsigned long) addr,
		(unsigned) size,
		(unsigned) (ctx->real_size + efi_mmap_size));
  ctx->real_mode_target = ((addr + size) - (ctx->real_size + efi_mmap_size));
  return 1;
}

428
/* GRUB types conveniently match E820 types.  */
429 430 431 432 433 434 435
static int
grub_linux_boot_mmap_fill (grub_uint64_t addr, grub_uint64_t size,
			   grub_memory_type_t type, void *data)
{
  struct grub_linux_boot_ctx *ctx = data;

  if (grub_e820_add_region (ctx->params->e820_map, &ctx->e820_num,
436
			    addr, size, type))
437 438 439 440 441
    return 1;

  return 0;
}

442
static grub_err_t
443
grub_linux_boot (void)
444
{
445
  grub_err_t err = 0;
446 447
  const char *modevar;
  char *tmp;
448
  struct grub_relocator32_state state;
449
  void *real_mode_mem;
450 451 452 453
  struct grub_linux_boot_ctx ctx = {
    .real_mode_target = 0
  };
  grub_size_t mmap_size;
454 455
  grub_size_t cl_offset;

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
#ifdef GRUB_MACHINE_IEEE1275
  {
    const char *bootpath;
    grub_ssize_t len;

    bootpath = grub_env_get ("root");
    if (bootpath)
      grub_ieee1275_set_property (grub_ieee1275_chosen,
				  "bootpath", bootpath,
				  grub_strlen (bootpath) + 1,
				  &len);
    linux_params.ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
    linux_params.ofw_num_items = 1;
    linux_params.ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
    linux_params.ofw_idt = 0;
  }
#endif

  modevar = grub_env_get ("gfxpayload");

  /* Now all graphical modes are acceptable.
     May change in future if we have modes without framebuffer.  */
  if (modevar && *modevar != 0)
    {
      tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
      if (! tmp)
	return grub_errno;
#if ACCEPTS_PURE_TEXT
      err = grub_video_set_mode (tmp, 0, 0);
#else
      err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
#endif
      grub_free (tmp);
    }
490 491 492 493 494 495 496
  else       /* We can't go back to text mode from coreboot fb.  */
#ifdef GRUB_MACHINE_COREBOOT
    if (grub_video_get_driver_id () == GRUB_VIDEO_DRIVER_COREBOOT)
      err = GRUB_ERR_NONE;
    else
#endif
      {
497
#if ACCEPTS_PURE_TEXT
498
	err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
499
#else
500
	err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
501 502
				 GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
#endif
503
      }
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536

  if (err)
    {
      grub_print_error ();
      grub_puts_ (N_("Booting in blind mode"));
      grub_errno = GRUB_ERR_NONE;
    }

  if (grub_linux_setup_video (&linux_params))
    {
#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
      linux_params.have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT;
      linux_params.video_mode = 0x3;
#else
      linux_params.have_vga = 0;
      linux_params.video_mode = 0;
      linux_params.video_width = 0;
      linux_params.video_height = 0;
#endif
    }


#ifndef GRUB_MACHINE_IEEE1275
  if (linux_params.have_vga == GRUB_VIDEO_LINUX_TYPE_TEXT)
#endif
    {
      grub_term_output_t term;
      int found = 0;
      FOR_ACTIVE_TERM_OUTPUTS(term)
	if (grub_strcmp (term->name, "vga_text") == 0
	    || grub_strcmp (term->name, "console") == 0
	    || grub_strcmp (term->name, "ofconsole") == 0)
	  {
537 538 539
	    struct grub_term_coordinate pos = grub_term_getxy (term);
	    linux_params.video_cursor_x = pos.x;
	    linux_params.video_cursor_y = pos.y;
540 541 542 543 544 545 546 547 548 549 550 551 552 553
	    linux_params.video_width = grub_term_width (term);
	    linux_params.video_height = grub_term_height (term);
	    found = 1;
	    break;
	  }
      if (!found)
	{
	  linux_params.video_cursor_x = 0;
	  linux_params.video_cursor_y = 0;
	  linux_params.video_width = 80;
	  linux_params.video_height = 25;
	}
    }

554 555
  mmap_size = find_mmap_size ();
  /* Make sure that each size is aligned to a page boundary.  */
556 557 558
  cl_offset = ALIGN_UP (mmap_size + sizeof (linux_params), 4096);
  if (cl_offset < ((grub_size_t) linux_params.setup_sects << GRUB_DISK_SECTOR_BITS))
    cl_offset = ALIGN_UP ((grub_size_t) (linux_params.setup_sects
559
					 << GRUB_DISK_SECTOR_BITS), 4096);
560
  ctx.real_size = ALIGN_UP (cl_offset + maximal_cmdline_size, 4096);
561 562 563 564 565 566 567 568

#ifdef GRUB_MACHINE_EFI
  efi_mmap_size = find_efi_mmap_size ();
  if (efi_mmap_size == 0)
    return grub_errno;
#endif

  grub_dprintf ("linux", "real_size = %x, mmap_size = %x\n",
569
		(unsigned) ctx.real_size, (unsigned) mmap_size);
570 571

#ifdef GRUB_MACHINE_EFI
572 573 574
  grub_efi_mmap_iterate (grub_linux_boot_mmap_find, &ctx, 1);
  if (! ctx.real_mode_target)
    grub_efi_mmap_iterate (grub_linux_boot_mmap_find, &ctx, 0);
575
#else
576
  grub_mmap_iterate (grub_linux_boot_mmap_find, &ctx);
577
#endif
578
  grub_dprintf ("linux", "real_mode_target = %lx, real_size = %x, efi_mmap_size = %x\n",
579 580
                (unsigned long) ctx.real_mode_target,
		(unsigned) ctx.real_size,
581 582
		(unsigned) efi_mmap_size);

583
  if (! ctx.real_mode_target)
584 585 586 587 588
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");

  {
    grub_relocator_chunk_t ch;
    err = grub_relocator_alloc_chunk_addr (relocator, &ch,
589 590
					   ctx.real_mode_target,
					   (ctx.real_size + efi_mmap_size));
591 592 593 594
    if (err)
     return err;
    real_mode_mem = get_virtual_current_address (ch);
  }
595
  efi_mmap_buf = (grub_uint8_t *) real_mode_mem + ctx.real_size;
596

597 598
  grub_dprintf ("linux", "real_mode_mem = %p\n",
                real_mode_mem);
599

600
  ctx.params = real_mode_mem;
601

602 603 604
  *ctx.params = linux_params;
  ctx.params->cmd_line_ptr = ctx.real_mode_target + cl_offset;
  grub_memcpy ((char *) ctx.params + cl_offset, linux_cmdline,
605 606
	       maximal_cmdline_size);

607
  grub_dprintf ("linux", "code32_start = %x\n",
608
		(unsigned) ctx.params->code32_start);
609

610 611
  ctx.e820_num = 0;
  if (grub_mmap_iterate (grub_linux_boot_mmap_fill, &ctx))
612
    return grub_errno;
613
  ctx.params->mmap_size = ctx.e820_num;
614

615 616
#ifdef GRUB_MACHINE_EFI
  {
617
    grub_efi_uintn_t efi_desc_size;
618
    grub_size_t efi_mmap_target;
619
    grub_efi_uint32_t efi_desc_version;
620 621 622 623
    err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL,
					 &efi_desc_size, &efi_desc_version);
    if (err)
      return err;
624 625
    
    /* Note that no boot services are available from here.  */
626
    efi_mmap_target = ctx.real_mode_target 
627
      + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem);
628
    /* Pass EFI parameters.  */
629
    if (grub_le_to_cpu16 (ctx.params->version) >= 0x0208)
630
      {
631 632 633 634
	ctx.params->v0208.efi_mem_desc_size = efi_desc_size;
	ctx.params->v0208.efi_mem_desc_version = efi_desc_version;
	ctx.params->v0208.efi_mmap = efi_mmap_target;
	ctx.params->v0208.efi_mmap_size = efi_mmap_size;
635 636

#ifdef __x86_64__
637
	ctx.params->v0208.efi_mmap_hi = (efi_mmap_target >> 32);
638 639
#endif
      }
640
    else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0206)
641
      {
642 643 644 645
	ctx.params->v0206.efi_mem_desc_size = efi_desc_size;
	ctx.params->v0206.efi_mem_desc_version = efi_desc_version;
	ctx.params->v0206.efi_mmap = efi_mmap_target;
	ctx.params->v0206.efi_mmap_size = efi_mmap_size;
646
      }
647
    else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0204)
648
      {
649 650 651 652
	ctx.params->v0204.efi_mem_desc_size = efi_desc_size;
	ctx.params->v0204.efi_mem_desc_version = efi_desc_version;
	ctx.params->v0204.efi_mmap = efi_mmap_target;
	ctx.params->v0204.efi_mmap_size = efi_mmap_size;
653 654 655 656
      }
  }
#endif

657 658
  /* FIXME.  */
  /*  asm volatile ("lidt %0" : : "m" (idt_desc)); */
659
  state.ebp = state.edi = state.ebx = 0;
660 661 662
  state.esi = ctx.real_mode_target;
  state.esp = ctx.real_mode_target;
  state.eip = ctx.params->code32_start;
663
  return grub_relocator32_boot (relocator, state, 0);
664 665 666 667 668 669 670
}

static grub_err_t
grub_linux_unload (void)
{
  grub_dl_unref (my_mod);
  loaded = 0;
671 672
  grub_free (linux_cmdline);
  linux_cmdline = 0;
673 674 675
  return GRUB_ERR_NONE;
}

bean's avatar
bean committed
676 677 678
static grub_err_t
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
		int argc, char *argv[])
679 680
{
  grub_file_t file = 0;
681
  struct linux_i386_kernel_header lh;
682
  grub_uint8_t setup_sects;
683
  grub_size_t real_size, prot_size, prot_file_size;
684 685
  grub_ssize_t len;
  int i;
686 687
  grub_size_t align, min_align;
  int relocatable;
688
  grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
689 690

  grub_dl_ref (my_mod);
fzielcke's avatar
fzielcke committed
691

692 693
  if (argc == 0)
    {
694
      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
695 696 697 698 699
      goto fail;
    }

  file = grub_file_open (argv[0]);
  if (! file)
700
    goto fail;
701

702
  if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
703
    {
704
      if (!grub_errno)
705
	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
706
		    argv[0]);
707 708 709
      goto fail;
    }

710
  if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55))
711 712 713 714 715 716 717 718 719 720 721
    {
      grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
      goto fail;
    }

  if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
    {
      grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
      goto fail;
    }

722 723
  /* FIXME: 2.03 is not always good enough (Linux 2.4 can be 2.03 and
     still not support 32-bit boot.  */
724
  if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE)
725
      || grub_le_to_cpu16 (lh.version) < 0x0203)
726
    {
727
      grub_error (GRUB_ERR_BAD_OS, "version too old for 32-bit boot"
728 729 730 731
#ifdef GRUB_MACHINE_PCBIOS
		  " (try with `linux16')"
#endif
		  );
732 733 734
      goto fail;
    }

735
  if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
736
    {
737
      grub_error (GRUB_ERR_BAD_OS, "zImage doesn't support 32-bit boot"
738 739 740 741
#ifdef GRUB_MACHINE_PCBIOS
		  " (try with `linux16')"
#endif
		  );
742 743 744
      goto fail;
    }

745 746 747 748 749 750 751 752
  if (grub_le_to_cpu16 (lh.version) >= 0x0206)
    maximal_cmdline_size = grub_le_to_cpu32 (lh.cmdline_size) + 1;
  else
    maximal_cmdline_size = 256;

  if (maximal_cmdline_size < 128)
    maximal_cmdline_size = 128;

753
  setup_sects = lh.setup_sects;
fzielcke's avatar
fzielcke committed
754

755 756 757 758 759
  /* If SETUP_SECTS is not set, set it to the default (4).  */
  if (! setup_sects)
    setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;

  real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
760
  prot_file_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
fzielcke's avatar
fzielcke committed
761

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
  if (grub_le_to_cpu16 (lh.version) >= 0x205
      && lh.kernel_alignment != 0
      && ((lh.kernel_alignment - 1) & lh.kernel_alignment) == 0)
    {
      for (align = 0; align < 32; align++)
	if (grub_le_to_cpu32 (lh.kernel_alignment) & (1 << align))
	  break;
      relocatable = grub_le_to_cpu32 (lh.relocatable);
    }
  else
    {
      align = 0;
      relocatable = 0;
    }
    
  if (grub_le_to_cpu16 (lh.version) >= 0x020a)
    {
      min_align = lh.min_alignment;
      prot_size = grub_le_to_cpu32 (lh.init_size);
781
      prot_init_space = page_align (prot_size);
782
      if (relocatable)
783
	preferred_address = grub_le_to_cpu64 (lh.pref_address);
784
      else
785
	preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
786 787 788
    }
  else
    {
789
      min_align = align;
790
      prot_size = prot_file_size;
791
      preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
792 793
      /* Usually, the compression ratio is about 50%.  */
      prot_init_space = page_align (prot_size) * 3;
794 795 796 797
    }

  if (allocate_pages (prot_size, &align,
		      min_align, relocatable,
798
		      preferred_address))
799
    goto fail;
fzielcke's avatar
fzielcke committed
800

801 802
  grub_memset (&linux_params, 0, sizeof (linux_params));
  grub_memcpy (&linux_params.setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
803

804 805 806
  linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR;
  linux_params.kernel_alignment = (1 << align);
  linux_params.ps_mouse = linux_params.padding10 =  0;
807

808 809
  len = sizeof (linux_params) - sizeof (lh);
  if (grub_file_read (file, (char *) &linux_params + sizeof (lh), len) != len)
810
    {
811
      if (!grub_errno)
812
	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
813
		    argv[0]);
814 815 816
      goto fail;
    }

817
  linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
818

819 820
  /* These two are used (instead of cmd_line_ptr) by older versions of Linux,
     and otherwise ignored.  */
821 822
  linux_params.cl_magic = GRUB_LINUX_CL_MAGIC;
  linux_params.cl_offset = 0x1000;
823

824 825
  linux_params.ramdisk_image = 0;
  linux_params.ramdisk_size = 0;
826

827 828
  linux_params.heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
  linux_params.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
829 830 831 832

  /* These are not needed to be precise, because Linux uses these values
     only to raise an error when the decompression code cannot find good
     space.  */
833 834
  linux_params.ext_mem = ((32 * 0x100000) >> 10);
  linux_params.alt_mem = ((32 * 0x100000) >> 10);
fzielcke's avatar
fzielcke committed
835

836
  /* Ignored by Linux.  */
837
  linux_params.video_page = 0;
838 839

  /* Only used when `video_mode == 0x7', otherwise ignored.  */
840
  linux_params.video_ega_bx = 0;
841

842
  linux_params.font_size = 16; /* XXX */
843

844
#ifdef GRUB_MACHINE_EFI
845
#ifdef __x86_64__
846
  if (grub_le_to_cpu16 (linux_params.version) < 0x0208 &&
847 848 849 850 851
      ((grub_addr_t) grub_efi_system_table >> 32) != 0)
    return grub_error(GRUB_ERR_BAD_OS,
		      "kernel does not support 64-bit addressing");
#endif

852
  if (grub_le_to_cpu16 (linux_params.version) >= 0x0208)
853
    {
854
      linux_params.v0208.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
855
      linux_params.v0208.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
856
#ifdef __x86_64__
857
      linux_params.v0208.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
858 859
#endif
    }
860
  else if (grub_le_to_cpu16 (linux_params.version) >= 0x0206)
861
    {
862
      linux_params.v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
863
      linux_params.v0206.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
864
    }
865
  else if (grub_le_to_cpu16 (linux_params.version) >= 0x0204)
866
    {
867
      linux_params.v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
868
      linux_params.v0204.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
869 870 871
    }
#endif

872 873 874 875
  /* The other parameters are filled when booting.  */

  grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);

876 877
  grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
		(unsigned) real_size, (unsigned) prot_size);
878

879
  /* Look for memory size and video mode specified on the command line.  */
880 881
  linux_mem_size = 0;
  for (i = 1; i < argc; i++)
882
#ifdef GRUB_MACHINE_PCBIOS
883 884 885 886
    if (grub_memcmp (argv[i], "vga=", 4) == 0)
      {
	/* Video mode selection support.  */
	char *val = argv[i] + 4;
887
	unsigned vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
888
	struct grub_vesa_mode_table_entry *linux_mode;
889 890
	grub_err_t err;
	char *buf;
891

892 893
	grub_dl_load ("vbe");

894 895 896 897
	if (grub_strcmp (val, "normal") == 0)
	  vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
	else if (grub_strcmp (val, "ext") == 0)
	  vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
898
	else if (grub_strcmp (val, "ask") == 0)
899
	  {
900
	    grub_puts_ (N_("Legacy `ask' parameter no longer supported."));
901 902 903 904 905

	    /* We usually would never do this in a loader, but "vga=ask" means user
	       requested interaction, so it can't hurt to request keyboard input.  */
	    grub_wait_after_message ();

906 907
	    goto fail;
	  }
908 909 910
	else
	  vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);

911 912 913
	switch (vid_mode)
	  {
	  case 0:
914 915
	  case GRUB_LINUX_VID_MODE_NORMAL:
	    grub_env_set ("gfxpayload", "text");
916
	    grub_printf_ (N_("%s is deprecated. "
917
			     "Use set gfxpayload=%s before "
918 919
			     "linux command instead.\n"),
			  argv[i], "text");
920
	    break;
921

922
	  case 1:
923 924 925
	  case GRUB_LINUX_VID_MODE_EXTENDED:
	    /* FIXME: support 80x50 text. */
	    grub_env_set ("gfxpayload", "text");
926
	    grub_printf_ (N_("%s is deprecated. "
927
			     "Use set gfxpayload=%s before "
928 929
			     "linux command instead.\n"),
			  argv[i], "text");
930 931 932
	    break;
	  default:
	    /* Ignore invalid values.  */
933 934
	    if (vid_mode < GRUB_VESA_MODE_TABLE_START ||
		vid_mode > GRUB_VESA_MODE_TABLE_END)
935 936
	      {
		grub_env_set ("gfxpayload", "text");
937 938
		/* TRANSLATORS: "x" has to be entered in, like an identifier,
		   so please don't use better Unicode codepoints.  */
939
		grub_printf_ (N_("%s is deprecated. VGA mode %d isn't recognized. "
940 941
				 "Use set gfxpayload=WIDTHxHEIGHT[xDEPTH] "
				 "before linux command instead.\n"),
fzielcke's avatar
fzielcke committed
942
			     argv[i], vid_mode);
943 944 945
		break;
	      }

946 947
	    linux_mode = &grub_vesa_mode_table[vid_mode
					       - GRUB_VESA_MODE_TABLE_START];
fzielcke's avatar
fzielcke committed
948

949
	    buf = grub_xasprintf ("%ux%ux%u,%ux%u",
950
				 linux_mode->width, linux_mode->height,
951
				 linux_mode->depth,
952
				 linux_mode->width, linux_mode->height);
953 954 955
	    if (! buf)
	      goto fail;

956 957 958
	    grub_printf_ (N_("%s is deprecated. "
			     "Use set gfxpayload=%s before "
			     "linux command instead.\n"),
fzielcke's avatar
fzielcke committed