12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
- *
- * 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>
- #include <grub/memory.h>
- #include <grub/normal.h>
- #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>
- #include <grub/video.h>
- #include <grub/video_fb.h>
- #include <grub/command.h>
- #include <grub/i386/relocator.h>
- #include <grub/i18n.h>
- #include <grub/lib/cmdline.h>
- #include <grub/linux.h>
- GRUB_MOD_LICENSE ("GPLv3+");
- #ifdef GRUB_MACHINE_PCBIOS
- #include <grub/i386/pc/vesa_modes_table.h>
- #endif
- #ifdef GRUB_MACHINE_EFI
- #include <grub/efi/efi.h>
- #define HAS_VGA_TEXT 0
- #define DEFAULT_VIDEO_MODE "auto"
- #define ACCEPTS_PURE_TEXT 0
- #elif defined (GRUB_MACHINE_IEEE1275)
- #include <grub/ieee1275/ieee1275.h>
- #define HAS_VGA_TEXT 0
- #define DEFAULT_VIDEO_MODE "text"
- #define ACCEPTS_PURE_TEXT 1
- #else
- #include <grub/i386/pc/vbe.h>
- #include <grub/i386/pc/console.h>
- #define HAS_VGA_TEXT 1
- #define DEFAULT_VIDEO_MODE "text"
- #define ACCEPTS_PURE_TEXT 1
- #endif
- static grub_dl_t my_mod;
- static grub_size_t linux_mem_size;
- static int loaded;
- static void *prot_mode_mem;
- static grub_addr_t prot_mode_target;
- static void *initrd_mem;
- static grub_addr_t initrd_mem_target;
- static grub_size_t prot_init_space;
- static struct grub_relocator *relocator = NULL;
- static void *efi_mmap_buf;
- static grub_size_t maximal_cmdline_size;
- static struct linux_kernel_params linux_params;
- static char *linux_cmdline;
- #ifdef GRUB_MACHINE_EFI
- static grub_efi_uintn_t efi_mmap_size;
- #else
- static const grub_size_t efi_mmap_size = 0;
- #endif
- /* FIXME */
- #if 0
- struct idt_descriptor
- {
- grub_uint16_t limit;
- void *base;
- } GRUB_PACKED;
- static struct idt_descriptor idt_desc =
- {
- 0,
- 0
- };
- #endif
- static inline grub_size_t
- page_align (grub_size_t size)
- {
- 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;
- grub_efi_uintn_t cur_mmap_size = mmap_size;
- mmap = grub_malloc (cur_mmap_size);
- if (! mmap)
- return 0;
- ret = grub_efi_get_memory_map (&cur_mmap_size, mmap, 0, &desc_size, 0);
- grub_free (mmap);
- if (ret < 0)
- {
- grub_error (GRUB_ERR_IO, "cannot get memory map");
- return 0;
- }
- else if (ret > 0)
- break;
- if (mmap_size < cur_mmap_size)
- mmap_size = cur_mmap_size;
- 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 += (3 << 12);
- mmap_size = page_align (mmap_size);
- return mmap_size;
- }
- #endif
- /* 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;
- }
- /* 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;
- grub_mmap_iterate (count_hook, &count);
- 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);
- return page_align (mmap_size);
- }
- static void
- free_pages (void)
- {
- grub_relocator_unload (relocator);
- relocator = NULL;
- prot_mode_mem = initrd_mem = 0;
- prot_mode_target = initrd_mem_target = 0;
- }
- /* Allocate pages for the real mode code and the protected mode code
- for linux as well as a memory map buffer. */
- static grub_err_t
- allocate_pages (grub_size_t prot_size, grub_size_t *align,
- grub_size_t min_align, int relocatable,
- grub_uint64_t preferred_address)
- {
- grub_err_t err;
- if (prot_size == 0)
- prot_size = 1;
- prot_size = page_align (prot_size);
- /* Initialize the memory pointers with NULL for convenience. */
- free_pages ();
- relocator = grub_relocator_new ();
- if (!relocator)
- {
- err = grub_errno;
- goto fail;
- }
- /* FIXME: Should request low memory from the heap when this feature is
- implemented. */
- {
- grub_relocator_chunk_t ch;
- if (relocatable)
- {
- err = grub_relocator_alloc_chunk_align (relocator, &ch,
- preferred_address,
- preferred_address,
- prot_size, 1,
- GRUB_RELOCATOR_PREFERENCE_LOW,
- 1);
- for (; err && *align + 1 > min_align; (*align)--)
- {
- grub_errno = GRUB_ERR_NONE;
- err = grub_relocator_alloc_chunk_align (relocator, &ch,
- 0x1000000,
- 0xffffffff & ~prot_size,
- prot_size, 1 << *align,
- GRUB_RELOCATOR_PREFERENCE_LOW,
- 1);
- }
- if (err)
- goto fail;
- }
- else
- err = grub_relocator_alloc_chunk_addr (relocator, &ch,
- preferred_address,
- prot_size);
- if (err)
- goto fail;
- prot_mode_mem = get_virtual_current_address (ch);
- prot_mode_target = get_physical_target_address (ch);
- }
- grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n",
- prot_mode_mem, (unsigned long) prot_mode_target,
- (unsigned) prot_size);
- return GRUB_ERR_NONE;
- fail:
- free_pages ();
- return err;
- }
- static grub_err_t
- 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)++;
- }
- return GRUB_ERR_NONE;
- }
- static grub_err_t
- grub_linux_setup_video (struct linux_kernel_params *params)
- {
- struct grub_video_mode_info mode_info;
- void *framebuffer;
- grub_err_t err;
- grub_video_driver_id_t driver_id;
- const char *gfxlfbvar = grub_env_get ("gfxpayloadforcelfb");
- driver_id = grub_video_get_driver_id ();
- if (driver_id == GRUB_VIDEO_DRIVER_NONE)
- return 1;
- err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
- if (err)
- {
- grub_errno = GRUB_ERR_NONE;
- return 1;
- }
- 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;
- params->lfb_base = (grub_size_t) framebuffer;
- params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height, 65536);
- 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;
- 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:
- case GRUB_VIDEO_DRIVER_SIS315PRO:
- case GRUB_VIDEO_DRIVER_VGA:
- case GRUB_VIDEO_DRIVER_CIRRUS:
- case GRUB_VIDEO_DRIVER_BOCHS:
- case GRUB_VIDEO_DRIVER_RADEON_FULOONG2E:
- case GRUB_VIDEO_DRIVER_RADEON_YEELOONG3A:
- case GRUB_VIDEO_DRIVER_IEEE1275:
- case GRUB_VIDEO_DRIVER_COREBOOT:
- /* Make gcc happy. */
- case GRUB_VIDEO_DRIVER_XEN:
- case GRUB_VIDEO_DRIVER_SDL:
- case GRUB_VIDEO_DRIVER_NONE:
- case GRUB_VIDEO_ADAPTER_CAPTURE:
- params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
- break;
- }
- }
- #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. */
- if (driver_id == GRUB_VIDEO_DRIVER_VBE && mode_info.bpp <= 8)
- {
- 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
- return GRUB_ERR_NONE;
- }
- /* 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;
- }
- /* GRUB types conveniently match E820 types. */
- 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,
- addr, size, type))
- return 1;
- return 0;
- }
- static grub_err_t
- grub_linux_boot (void)
- {
- grub_err_t err = 0;
- const char *modevar;
- char *tmp;
- struct grub_relocator32_state state;
- void *real_mode_mem;
- struct grub_linux_boot_ctx ctx = {
- .real_mode_target = 0
- };
- grub_size_t mmap_size;
- grub_size_t cl_offset;
- #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);
- }
- 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
- {
- #if ACCEPTS_PURE_TEXT
- err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
- #else
- err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
- GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
- #endif
- }
- 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)
- {
- struct grub_term_coordinate pos = grub_term_getxy (term);
- linux_params.video_cursor_x = pos.x;
- linux_params.video_cursor_y = pos.y;
- 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;
- }
- }
- mmap_size = find_mmap_size ();
- /* Make sure that each size is aligned to a page boundary. */
- 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
- << GRUB_DISK_SECTOR_BITS), 4096);
- ctx.real_size = ALIGN_UP (cl_offset + maximal_cmdline_size, 4096);
- #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",
- (unsigned) ctx.real_size, (unsigned) mmap_size);
- #ifdef GRUB_MACHINE_EFI
- 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);
- #else
- grub_mmap_iterate (grub_linux_boot_mmap_find, &ctx);
- #endif
- grub_dprintf ("linux", "real_mode_target = %lx, real_size = %x, efi_mmap_size = %x\n",
- (unsigned long) ctx.real_mode_target,
- (unsigned) ctx.real_size,
- (unsigned) efi_mmap_size);
- if (! ctx.real_mode_target)
- 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,
- ctx.real_mode_target,
- (ctx.real_size + efi_mmap_size));
- if (err)
- return err;
- real_mode_mem = get_virtual_current_address (ch);
- }
- efi_mmap_buf = (grub_uint8_t *) real_mode_mem + ctx.real_size;
- grub_dprintf ("linux", "real_mode_mem = %p\n",
- real_mode_mem);
- ctx.params = real_mode_mem;
- *ctx.params = linux_params;
- ctx.params->cmd_line_ptr = ctx.real_mode_target + cl_offset;
- grub_memcpy ((char *) ctx.params + cl_offset, linux_cmdline,
- maximal_cmdline_size);
- grub_dprintf ("linux", "code32_start = %x\n",
- (unsigned) ctx.params->code32_start);
- ctx.e820_num = 0;
- if (grub_mmap_iterate (grub_linux_boot_mmap_fill, &ctx))
- return grub_errno;
- ctx.params->mmap_size = ctx.e820_num;
- #ifdef GRUB_MACHINE_EFI
- {
- grub_efi_uintn_t efi_desc_size;
- grub_size_t efi_mmap_target;
- grub_efi_uint32_t efi_desc_version;
- err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL,
- &efi_desc_size, &efi_desc_version);
- if (err)
- return err;
-
- /* Note that no boot services are available from here. */
- efi_mmap_target = ctx.real_mode_target
- + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem);
- /* Pass EFI parameters. */
- if (grub_le_to_cpu16 (ctx.params->version) >= 0x0208)
- {
- 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;
- #ifdef __x86_64__
- ctx.params->v0208.efi_mmap_hi = (efi_mmap_target >> 32);
- #endif
- }
- else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0206)
- {
- 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;
- }
- else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0204)
- {
- 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;
- }
- }
- #endif
- /* FIXME. */
- /* asm volatile ("lidt %0" : : "m" (idt_desc)); */
- state.ebp = state.edi = state.ebx = 0;
- state.esi = ctx.real_mode_target;
- state.esp = ctx.real_mode_target;
- state.eip = ctx.params->code32_start;
- return grub_relocator32_boot (relocator, state, 0);
- }
- static grub_err_t
- grub_linux_unload (void)
- {
- grub_dl_unref (my_mod);
- loaded = 0;
- grub_free (linux_cmdline);
- linux_cmdline = 0;
- return GRUB_ERR_NONE;
- }
- static grub_err_t
- grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
- int argc, char *argv[])
- {
- grub_file_t file = 0;
- struct linux_kernel_header lh;
- grub_uint8_t setup_sects;
- grub_size_t real_size, prot_size, prot_file_size;
- grub_ssize_t len;
- int i;
- grub_size_t align, min_align;
- int relocatable;
- grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
- grub_dl_ref (my_mod);
- if (argc == 0)
- {
- grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
- goto fail;
- }
- file = grub_file_open (argv[0]);
- if (! file)
- goto fail;
- if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
- {
- if (!grub_errno)
- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
- argv[0]);
- goto fail;
- }
- if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55))
- {
- 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;
- }
- /* FIXME: 2.03 is not always good enough (Linux 2.4 can be 2.03 and
- still not support 32-bit boot. */
- if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_MAGIC_SIGNATURE)
- || grub_le_to_cpu16 (lh.version) < 0x0203)
- {
- grub_error (GRUB_ERR_BAD_OS, "version too old for 32-bit boot"
- #ifdef GRUB_MACHINE_PCBIOS
- " (try with `linux16')"
- #endif
- );
- goto fail;
- }
- if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
- {
- grub_error (GRUB_ERR_BAD_OS, "zImage doesn't support 32-bit boot"
- #ifdef GRUB_MACHINE_PCBIOS
- " (try with `linux16')"
- #endif
- );
- goto fail;
- }
- 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;
- setup_sects = lh.setup_sects;
- /* 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;
- prot_file_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
- 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);
- prot_init_space = page_align (prot_size);
- if (relocatable)
- preferred_address = grub_le_to_cpu64 (lh.pref_address);
- else
- preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
- }
- else
- {
- min_align = align;
- prot_size = prot_file_size;
- preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
- /* Usually, the compression ratio is about 50%. */
- prot_init_space = page_align (prot_size) * 3;
- }
- if (allocate_pages (prot_size, &align,
- min_align, relocatable,
- preferred_address))
- goto fail;
- grub_memset (&linux_params, 0, sizeof (linux_params));
- grub_memcpy (&linux_params.setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
- 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;
- len = sizeof (linux_params) - sizeof (lh);
- if (grub_file_read (file, (char *) &linux_params + sizeof (lh), len) != len)
- {
- if (!grub_errno)
- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
- argv[0]);
- goto fail;
- }
- linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
- /* These two are used (instead of cmd_line_ptr) by older versions of Linux,
- and otherwise ignored. */
- linux_params.cl_magic = GRUB_LINUX_CL_MAGIC;
- linux_params.cl_offset = 0x1000;
- linux_params.ramdisk_image = 0;
- linux_params.ramdisk_size = 0;
- linux_params.heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
- linux_params.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
- /* 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. */
- linux_params.ext_mem = ((32 * 0x100000) >> 10);
- linux_params.alt_mem = ((32 * 0x100000) >> 10);
- /* Ignored by Linux. */
- linux_params.video_page = 0;
- /* Only used when `video_mode == 0x7', otherwise ignored. */
- linux_params.video_ega_bx = 0;
- linux_params.font_size = 16; /* XXX */
- #ifdef GRUB_MACHINE_EFI
- #ifdef __x86_64__
- if (grub_le_to_cpu16 (linux_params.version) < 0x0208 &&
- ((grub_addr_t) grub_efi_system_table >> 32) != 0)
- return grub_error(GRUB_ERR_BAD_OS,
- "kernel does not support 64-bit addressing");
- #endif
- if (grub_le_to_cpu16 (linux_params.version) >= 0x0208)
- {
- linux_params.v0208.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
- linux_params.v0208.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
- #ifdef __x86_64__
- linux_params.v0208.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
- #endif
- }
- else if (grub_le_to_cpu16 (linux_params.version) >= 0x0206)
- {
- linux_params.v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
- linux_params.v0206.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
- }
- else if (grub_le_to_cpu16 (linux_params.version) >= 0x0204)
- {
- linux_params.v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
- linux_params.v0204.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
- }
- #endif
- /* The other parameters are filled when booting. */
- grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
- grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
- (unsigned) real_size, (unsigned) prot_size);
- /* Look for memory size and video mode specified on the command line. */
- linux_mem_size = 0;
- for (i = 1; i < argc; i++)
- #ifdef GRUB_MACHINE_PCBIOS
- if (grub_memcmp (argv[i], "vga=", 4) == 0)
- {
- /* Video mode selection support. */
- char *val = argv[i] + 4;
- unsigned vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
- struct grub_vesa_mode_table_entry *linux_mode;
- grub_err_t err;
- char *buf;
- grub_dl_load ("vbe");
- 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;
- else if (grub_strcmp (val, "ask") == 0)
- {
- grub_puts_ (N_("Legacy `ask' parameter no longer supported."));
- /* 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 ();
- goto fail;
- }
- else
- vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
- switch (vid_mode)
- {
- case 0:
- case GRUB_LINUX_VID_MODE_NORMAL:
- grub_env_set ("gfxpayload", "text");
- grub_printf_ (N_("%s is deprecated. "
- "Use set gfxpayload=%s before "
- "linux command instead.\n"),
- argv[i], "text");
- break;
- case 1:
- case GRUB_LINUX_VID_MODE_EXTENDED:
- /* FIXME: support 80x50 text. */
- grub_env_set ("gfxpayload", "text");
- grub_printf_ (N_("%s is deprecated. "
- "Use set gfxpayload=%s before "
- "linux command instead.\n"),
- argv[i], "text");
- break;
- default:
- /* Ignore invalid values. */
- if (vid_mode < GRUB_VESA_MODE_TABLE_START ||
- vid_mode > GRUB_VESA_MODE_TABLE_END)
- {
- grub_env_set ("gfxpayload", "text");
- /* TRANSLATORS: "x" has to be entered in, like an identifier,
- so please don't use better Unicode codepoints. */
- grub_printf_ (N_("%s is deprecated. VGA mode %d isn't recognized. "
- "Use set gfxpayload=WIDTHxHEIGHT[xDEPTH] "
- "before linux command instead.\n"),
- argv[i], vid_mode);
- break;
- }
- linux_mode = &grub_vesa_mode_table[vid_mode
- - GRUB_VESA_MODE_TABLE_START];
- buf = grub_xasprintf ("%ux%ux%u,%ux%u",
- linux_mode->width, linux_mode->height,
- linux_mode->depth,
- linux_mode->width, linux_mode->height);
- if (! buf)
- goto fail;
- grub_printf_ (N_("%s is deprecated. "
- "Use set gfxpayload=%s before "
- "linux command instead.\n"),
- argv[i], buf);
- err = grub_env_set ("gfxpayload", buf);
- grub_free (buf);
- if (err)
- goto fail;
- }
- }
- else
- #endif /* GRUB_MACHINE_PCBIOS */
- if (grub_memcmp (argv[i], "mem=", 4) == 0)
- {
- char *val = argv[i] + 4;
- linux_mem_size = grub_strtoul (val, &val, 0);
- if (grub_errno)
- {
- grub_errno = GRUB_ERR_NONE;
- linux_mem_size = 0;
- }
- else
- {
- int shift = 0;
- switch (grub_tolower (val[0]))
- {
- case 'g':
- shift += 10;
- /* FALLTHROUGH */
- case 'm':
- shift += 10;
- /* FALLTHROUGH */
- case 'k':
- shift += 10;
- /* FALLTHROUGH */
- default:
- break;
- }
- /* Check an overflow. */
- if (linux_mem_size > (~0UL >> shift))
- linux_mem_size = 0;
- else
- linux_mem_size <<= shift;
- }
- }
- else if (grub_memcmp (argv[i], "quiet", sizeof ("quiet") - 1) == 0)
- {
- linux_params.loadflags |= GRUB_LINUX_FLAG_QUIET;
- }
- /* Create kernel command line. */
- linux_cmdline = grub_zalloc (maximal_cmdline_size + 1);
- if (!linux_cmdline)
- goto fail;
- grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
- grub_create_loader_cmdline (argc, argv,
- linux_cmdline
- + sizeof (LINUX_IMAGE) - 1,
- maximal_cmdline_size
- - (sizeof (LINUX_IMAGE) - 1));
- len = prot_file_size;
- if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno)
- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
- argv[0]);
- if (grub_errno == GRUB_ERR_NONE)
- {
- grub_loader_set (grub_linux_boot, grub_linux_unload,
- 0 /* set noreturn=0 in order to avoid grub_console_fini() */);
- loaded = 1;
- }
- fail:
- if (file)
- grub_file_close (file);
- if (grub_errno != GRUB_ERR_NONE)
- {
- grub_dl_unref (my_mod);
- loaded = 0;
- }
- return grub_errno;
- }
- static grub_err_t
- grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
- int argc, char *argv[])
- {
- grub_size_t size = 0, aligned_size = 0;
- grub_addr_t addr_min, addr_max;
- grub_addr_t addr;
- grub_err_t err;
- struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
- if (argc == 0)
- {
- grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
- goto fail;
- }
- if (! loaded)
- {
- grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
- goto fail;
- }
- if (grub_initrd_init (argc, argv, &initrd_ctx))
- goto fail;
- size = grub_get_initrd_size (&initrd_ctx);
- aligned_size = ALIGN_UP (size, 4096);
- /* Get the highest address available for the initrd. */
- if (grub_le_to_cpu16 (linux_params.version) >= 0x0203)
- {
- addr_max = grub_cpu_to_le32 (linux_params.initrd_addr_max);
- /* XXX in reality, Linux specifies a bogus value, so
- it is necessary to make sure that ADDR_MAX does not exceed
- 0x3fffffff. */
- if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
- addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
- }
- else
- addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
- if (linux_mem_size != 0 && linux_mem_size < addr_max)
- addr_max = linux_mem_size;
- /* Linux 2.3.xx has a bug in the memory range check, so avoid
- the last page.
- Linux 2.2.xx has a bug in the memory range check, which is
- worse than that of Linux 2.3.xx, so avoid the last 64kb. */
- addr_max -= 0x10000;
- addr_min = (grub_addr_t) prot_mode_target + prot_init_space;
- /* Put the initrd as high as possible, 4KiB aligned. */
- addr = (addr_max - aligned_size) & ~0xFFF;
- if (addr < addr_min)
- {
- grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big");
- goto fail;
- }
- {
- grub_relocator_chunk_t ch;
- err = grub_relocator_alloc_chunk_align (relocator, &ch,
- addr_min, addr, aligned_size,
- 0x1000,
- GRUB_RELOCATOR_PREFERENCE_HIGH,
- 1);
- if (err)
- return err;
- initrd_mem = get_virtual_current_address (ch);
- initrd_mem_target = get_physical_target_address (ch);
- }
- if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
- goto fail;
- grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
- (unsigned) addr, (unsigned) size);
- linux_params.ramdisk_image = initrd_mem_target;
- linux_params.ramdisk_size = size;
- linux_params.root_dev = 0x0100; /* XXX */
- fail:
- grub_initrd_close (&initrd_ctx);
- return grub_errno;
- }
- static grub_command_t cmd_linux, cmd_initrd;
- GRUB_MOD_INIT(linux)
- {
- cmd_linux = grub_register_command ("linux", grub_cmd_linux,
- 0, N_("Load Linux."));
- cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
- 0, N_("Load initrd."));
- my_mod = mod;
- }
- GRUB_MOD_FINI(linux)
- {
- grub_unregister_command (cmd_linux);
- grub_unregister_command (cmd_initrd);
- }
|