Merge i386/efi/linux.c into i386/linux.c

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-01-13 14:03:54 +01:00
parent ca732b36c1
commit c8142599fc
2 changed files with 162 additions and 28 deletions

View file

@ -79,9 +79,9 @@ struct grub_e820_mmap
grub_uint32_t type; grub_uint32_t type;
} __attribute__((packed)); } __attribute__((packed));
#define GRUB_VIDEO_TYPE_TEXT 0x01 #define GRUB_VIDEO_LINUX_TYPE_EGA_TEXT 0x01
#define GRUB_VIDEO_TYPE_VLFB 0x23 /* VESA VGA in graphic mode */ #define GRUB_VIDEO_LINUX_TYPE_VLFB 0x23 /* VESA VGA in graphic mode */
#define GRUB_VIDEO_TYPE_EFI 0x70 #define GRUB_VIDEO_LINUX_TYPE_SIMPLE_LFB 0x70 /* Linear framebuffer without any additional functions. */
/* For the Linux/i386 boot protocol version 2.03. */ /* For the Linux/i386 boot protocol version 2.03. */
struct linux_kernel_header struct linux_kernel_header

View file

@ -18,6 +18,7 @@
#include <grub/loader.h> #include <grub/loader.h>
#include <grub/machine/memory.h> #include <grub/machine/memory.h>
#include <grub/memory.h>
#include <grub/normal.h> #include <grub/normal.h>
#include <grub/file.h> #include <grub/file.h>
#include <grub/disk.h> #include <grub/disk.h>
@ -31,11 +32,20 @@
#include <grub/video.h> #include <grub/video.h>
#include <grub/video_fb.h> #include <grub/video_fb.h>
#include <grub/command.h> #include <grub/command.h>
#include <grub/i386/pc/vbe.h>
#include <grub/i386/pc/console.h>
#include <grub/i386/relocator.h> #include <grub/i386/relocator.h>
#include <grub/i18n.h> #include <grub/i18n.h>
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#define HAS_VGA_TEXT 0
#define DEFAULT_VIDEO_MODE "800x600"
#else
#include <grub/i386/pc/vbe.h>
#include <grub/i386/pc/console.h>
#define HAS_VGA_TEXT 1
#define DEFAULT_VIDEO_MODE "text"
#endif
#define GRUB_LINUX_CL_OFFSET 0x1000 #define GRUB_LINUX_CL_OFFSET 0x1000
#define GRUB_LINUX_CL_END_OFFSET 0x2000 #define GRUB_LINUX_CL_END_OFFSET 0x2000
@ -53,6 +63,8 @@ static grub_uint32_t real_mode_pages;
static grub_uint32_t prot_mode_pages; static grub_uint32_t prot_mode_pages;
static grub_uint32_t initrd_pages; static grub_uint32_t initrd_pages;
static struct grub_relocator *relocator = NULL; static struct grub_relocator *relocator = NULL;
static void *efi_mmap_buf;
static grub_size_t efi_mmap_size;
/* FIXME */ /* FIXME */
#if 0 #if 0
@ -244,6 +256,48 @@ page_align (grub_size_t size)
return (size + (1 << 12) - 1) & (~((1 << 12) - 1)); return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
} }
#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;
mmap = grub_malloc (mmap_size);
if (! mmap)
return 0;
ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
grub_free (mmap);
if (ret < 0)
grub_fatal ("cannot get memory map");
else if (ret > 0)
break;
mmap_size += (1 << 12);
}
/* Increase the size a bit for safety, because GRUB allocates more on
later, and EFI itself may allocate more. */
mmap_size += (1 << 12);
return page_align (mmap_size);
}
#endif
/* Find the optimal number of pages for the memory map. */ /* Find the optimal number of pages for the memory map. */
static grub_size_t static grub_size_t
find_mmap_size (void) find_mmap_size (void)
@ -292,12 +346,18 @@ allocate_pages (grub_size_t prot_size)
prot_size = page_align (prot_size); prot_size = page_align (prot_size);
mmap_size = find_mmap_size (); mmap_size = find_mmap_size ();
#ifdef GRUB_MACHINE_EFI
efi_mmap_size = find_efi_mmap_size ();
#else
efi_mmap_size = 0;
#endif
grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n", grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
(unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size); (unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
/* Calculate the number of pages; Combine the real mode code with /* Calculate the number of pages; Combine the real mode code with
the memory map buffer for simplicity. */ the memory map buffer for simplicity. */
real_mode_pages = ((real_size + mmap_size) >> 12); real_mode_pages = ((real_size + mmap_size + efi_mmap_size) >> 12);
prot_mode_pages = (prot_size >> 12); prot_mode_pages = (prot_size >> 12);
/* Initialize the memory pointers with NULL for convenience. */ /* Initialize the memory pointers with NULL for convenience. */
@ -330,10 +390,10 @@ allocate_pages (grub_size_t prot_size)
if (addr + size > 0x90000) if (addr + size > 0x90000)
size = 0x90000 - addr; size = 0x90000 - addr;
if (real_size + mmap_size > size) if (real_size + mmap_size + efi_mmap_size > size)
return 0; return 0;
real_mode_target = ((addr + size) - (real_size + mmap_size)); real_mode_target = ((addr + size) - (real_size + mmap_size + efi_mmap_size));
return 1; return 1;
} }
@ -348,11 +408,13 @@ allocate_pages (grub_size_t prot_size)
err = grub_relocator_alloc_chunk_addr (relocator, &real_mode_mem, err = grub_relocator_alloc_chunk_addr (relocator, &real_mode_mem,
real_mode_target, real_mode_target,
(real_size + mmap_size)); (real_size + mmap_size
+ efi_mmap_size));
if (err) if (err)
goto fail; goto fail;
efi_mmap_buf = (grub_uint8_t *) real_mode_mem + real_size + mmap_size;
prot_mode_target = 0x100000; prot_mode_target = GRUB_LINUX_BZIMAGE_ADDR;
err = grub_relocator_alloc_chunk_addr (relocator, &prot_mode_mem, err = grub_relocator_alloc_chunk_addr (relocator, &prot_mode_mem,
prot_mode_target, prot_size); prot_mode_target, prot_size);
@ -393,17 +455,28 @@ grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
} }
} }
static int static grub_err_t
grub_linux_setup_video (struct linux_kernel_params *params) grub_linux_setup_video (struct linux_kernel_params *params)
{ {
struct grub_video_mode_info mode_info; struct grub_video_mode_info mode_info;
void *framebuffer; void *framebuffer;
int ret; grub_err_t err;
ret = grub_video_get_info_and_fini (&mode_info, &framebuffer); switch (grub_video_get_driver_id ())
{
case GRUB_VIDEO_DRIVER_VBE:
params->have_vga = GRUB_VIDEO_LINUX_TYPE_VLFB;
break;
if (ret) default:
return 1; params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE_LFB;
break;
}
err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
if (err)
return err;
params->lfb_width = mode_info.width; params->lfb_width = mode_info.width;
params->lfb_height = mode_info.height; params->lfb_height = mode_info.height;
@ -449,7 +522,7 @@ grub_linux_setup_video (struct linux_kernel_params *params)
} }
#endif #endif
return 0; return GRUB_ERR_NONE;
} }
#ifdef __x86_64__ #ifdef __x86_64__
@ -520,15 +593,15 @@ grub_linux_boot (void)
if (modevar && *modevar != 0) if (modevar && *modevar != 0)
{ {
tmp = grub_malloc (grub_strlen (modevar) tmp = grub_malloc (grub_strlen (modevar)
+ sizeof (";text")); + sizeof (";" DEFAULT_VIDEO_MODE));
if (! tmp) if (! tmp)
return grub_errno; return grub_errno;
grub_sprintf (tmp, "%s;text", modevar); grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar);
err = grub_video_set_mode (tmp, 0); err = grub_video_set_mode (tmp, 0);
grub_free (tmp); grub_free (tmp);
} }
else else
err = grub_video_set_mode ("text", 0); err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0);
if (err) if (err)
{ {
@ -537,17 +610,26 @@ grub_linux_boot (void)
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
} }
if (! grub_linux_setup_video (params)) err = grub_linux_setup_video (params);
params->have_vga = GRUB_VIDEO_TYPE_VLFB; if (err)
else
{ {
params->have_vga = GRUB_VIDEO_TYPE_TEXT; grub_print_error ();
grub_errno = GRUB_ERR_NONE;
#if HAS_VGA_TEXT
params->have_vga = GRUB_VIDEO_LINUX_TYPE_EGA_TEXT;
params->video_mode = 0x3;
params->video_width = 80; params->video_width = 80;
params->video_height = 25; params->video_height = 25;
#else
params->have_vga = 0;
params->video_mode = 0;
params->video_width = 0;
params->video_height = 0;
#endif
} }
/* Initialize these last, because terminal position could be affected by printfs above. */ /* Initialize these last, because terminal position could be affected by printfs above. */
if (params->have_vga == GRUB_VIDEO_TYPE_TEXT) if (params->have_vga == GRUB_VIDEO_LINUX_TYPE_EGA_TEXT)
{ {
grub_term_output_t term; grub_term_output_t term;
int found = 0; int found = 0;
@ -568,6 +650,40 @@ grub_linux_boot (void)
} }
} }
#ifdef GRUB_MACHINE_EFI
{
grub_efi_uintn_t efi_map_key, efi_desc_size;
grub_efi_uint32_t efi_desc_version;
if (grub_efi_get_memory_map (&efi_mmap_size, efi_mmap_buf, &efi_map_key,
&efi_desc_size, &efi_desc_version) <= 0)
grub_fatal ("cannot get memory map");
if (! grub_efi_exit_boot_services (efi_map_key))
grub_fatal ("cannot exit boot services");
/* Note that no boot services are available from here. */
/* Pass EFI parameters. */
if (grub_le_to_cpu16 (params->version) >= 0x0206)
{
params->v0206.efi_mem_desc_size = efi_desc_size;
params->v0206.efi_mem_desc_version = efi_desc_version;
params->v0206.efi_mmap = (grub_uint32_t) (unsigned long) efi_mmap_buf;
params->v0206.efi_mmap_size = efi_mmap_size;
#ifdef __x86_64__
params->v0206.efi_mmap_hi = (grub_uint32_t) ((grub_uint64_t) efi_mmap_buf >> 32);
#endif
}
else if (grub_le_to_cpu16 (params->version) >= 0x0204)
{
params->v0204.efi_mem_desc_size = efi_desc_size;
params->v0204.efi_mem_desc_version = efi_desc_version;
params->v0204.efi_mmap = (grub_uint32_t) (unsigned long) efi_mmap_buf;
params->v0204.efi_mmap_size = efi_mmap_size;
}
}
#endif
/* FIXME. */ /* FIXME. */
/* asm volatile ("lidt %0" : : "m" (idt_desc)); */ /* asm volatile ("lidt %0" : : "m" (idt_desc)); */
state.ebx = 0; state.ebx = 0;
@ -675,7 +791,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
goto fail; goto fail;
} }
#ifdef GRUB_MACHINE_EFI
/* XXX Linux assumes that only elilo can boot Linux on EFI!!! */
params->type_of_loader = (LINUX_LOADER_ID_ELILO << 4);
#else
params->type_of_loader = (LINUX_LOADER_ID_GRUB << 4); params->type_of_loader = (LINUX_LOADER_ID_GRUB << 4);
#endif
/* These two are used (instead of cmd_line_ptr) by older versions of Linux, /* These two are used (instead of cmd_line_ptr) by older versions of Linux,
and otherwise ignored. */ and otherwise ignored. */
@ -698,14 +819,27 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
/* Ignored by Linux. */ /* Ignored by Linux. */
params->video_page = 0; params->video_page = 0;
/* Must be non-zero even in text mode, or Linux will think there's no VGA. */
params->video_mode = 0x3;
/* Only used when `video_mode == 0x7', otherwise ignored. */ /* Only used when `video_mode == 0x7', otherwise ignored. */
params->video_ega_bx = 0; params->video_ega_bx = 0;
params->font_size = 16; /* XXX */ params->font_size = 16; /* XXX */
#ifdef GRUB_MACHINE_EFI
if (grub_le_to_cpu16 (params->version) >= 0x0206)
{
params->v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
params->v0206.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
#ifdef __x86_64__
params->v0206.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
#endif
}
else if (grub_le_to_cpu16 (params->version) >= 0x0204)
{
params->v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
params->v0204.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
}
#endif
/* The other parameters are filled when booting. */ /* The other parameters are filled when booting. */
grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE); grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
@ -859,7 +993,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
} }
len = prot_size; len = prot_size;
if (grub_file_read (file, (void *) GRUB_LINUX_BZIMAGE_ADDR, len) != len) if (grub_file_read (file, prot_mode_mem, len) != len)
grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file"); grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
if (grub_errno == GRUB_ERR_NONE) if (grub_errno == GRUB_ERR_NONE)