From: Vladimir Serbinenko Date: Tue, 19 Nov 2013 23:51:03 +0000 (+0100) Subject: Some scribbles on EFI compression X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d69064ad50049dbe7f40564a0937ccfe401b028b;p=thirdparty%2Fgrub.git Some scribbles on EFI compression --- diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 5cd84b183..d6de47944 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -163,6 +163,7 @@ kernel = { efi = disk/efi/efidisk.c; efi = kern/efi/efi.c; + efi = kern/efi/efi_core.c; efi = kern/efi/init.c; efi = kern/efi/mm.c; efi = term/efi/console.c; @@ -416,12 +417,22 @@ image = { image = { name = xz_decompress; + ia64_efi = kern/ia64/efi/startup.S; + i386_efi = kern/i386/efi/startup.S; + x86_64_efi = kern/x86_64/efi/startup.S; + arm_efi = kern/arm/efi/startup.S; + mips = boot/mips/startup_raw.S; + mips = boot/mips/scratch.c; common = boot/decompressor/minilib.c; common = boot/decompressor/xz.c; common = lib/xzembed/xz_dec_bcj.c; common = lib/xzembed/xz_dec_lzma2.c; common = lib/xzembed/xz_dec_stream.c; + efi = boot/efi/decompressor.c; + efi = kern/efi/efi_core.c; + + x86_64_efi = kern/x86_64/efi/callwrap.S; cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed -DGRUB_EMBED_DECOMPRESSOR=1'; @@ -430,6 +441,7 @@ image = { ldadd = '$(TARGET_LIBGCC)'; cflags = '-Wno-unreachable-code $(TARGET_LDFLAGS_STATIC_LIBGCC)'; enable = mips; + enable = efi; }; image = { diff --git a/grub-core/boot/decompressor/minilib.c b/grub-core/boot/decompressor/minilib.c index 94edfd561..5d380f239 100644 --- a/grub-core/boot/decompressor/minilib.c +++ b/grub-core/boot/decompressor/minilib.c @@ -79,24 +79,3 @@ void *memcpy (void *dest, const void *src, grub_size_t n) void *grub_decompressor_scratch; -void -find_scratch (void *src, void *dst, unsigned long srcsize, - unsigned long dstsize) -{ -#ifdef _mips - /* Decoding from ROM. */ - if (((grub_addr_t) src & 0x10000000)) - { - grub_decompressor_scratch = (void *) ALIGN_UP((grub_addr_t) dst + dstsize, - 256); - return; - } -#endif - if ((char *) src + srcsize > (char *) dst + dstsize) - grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) src + srcsize, - 256); - else - grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) dst + dstsize, - 256); - return; -} diff --git a/grub-core/boot/decompressor/scratch.c b/grub-core/boot/decompressor/scratch.c new file mode 100644 index 000000000..90d41bcff --- /dev/null +++ b/grub-core/boot/decompressor/scratch.c @@ -0,0 +1,34 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2013 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 . + */ + +#include +#include +#include + +void +find_scratch (void *src, void *dst, unsigned long srcsize, + unsigned long dstsize) +{ + if ((char *) src + srcsize > (char *) dst + dstsize) + grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) src + srcsize, + 256); + else + grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) dst + dstsize, + 256); + return; +} diff --git a/grub-core/boot/efi/decompressor.c b/grub-core/boot/efi/decompressor.c new file mode 100644 index 000000000..30f8fcc99 --- /dev/null +++ b/grub-core/boot/efi/decompressor.c @@ -0,0 +1,116 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include + +/* The handle of GRUB itself. Filled in by the startup code. */ +grub_efi_handle_t grub_efi_image_handle; + +/* The pointer to a system table. Filled in by the startup code. */ +grub_efi_system_table_t *grub_efi_system_table; + +int +grub_strcmp (const char *s1, const char *s2) +{ + while (*s1 && *s2) + { + if (*s1 != *s2) + break; + + s1++; + s2++; + } + + return (int) (grub_uint8_t) *s1 - (int) (grub_uint8_t) *s2; +} + +static grub_size_t unc_pages, scratch_pages; +static void *unc; + +void +grub_efi_fini (void) +{ + if (unc) + grub_efi_free_pages ((grub_addr_t) unc, unc_pages); + if (grub_decompressor_scratch) + grub_efi_free_pages ((grub_addr_t) grub_decompressor_scratch, + scratch_pages); +} + +void +find_scratch (void *src __attribute__ ((unused)), + void *dst __attribute__ ((unused)), + unsigned long srcsize __attribute__ ((unused)), + unsigned long dstsize __attribute__ ((unused))) +{ +} + +void __attribute__ ((noreturn)) +grub_main (void) +{ + grub_addr_t start; + grub_size_t sz; + grub_addr_t unc_size; + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_uintn_t exit_data_size; + grub_efi_char16_t *exit_data = 0; + grub_efi_handle_t image_handle; + grub_efi_loaded_image_t *image; + + b = grub_efi_system_table->boot_services; + + if (!grub_efi_get_section ("payload", &start, &sz) + || sz < 12) + grub_exit (); + if (grub_memcmp ((void *) start, "COMPRESS", 8) != 0) + grub_exit (); + unc_size = grub_get_unaligned32 ((char *) start + 8); + + unc_pages = (unc_size + 0xfff) >> 12; + + unc = grub_efi_allocate_pages (0, unc_pages); + if (!unc) + grub_exit (); + + scratch_pages = 0x200000 >> 12; + grub_decompressor_scratch = grub_efi_allocate_pages (0, scratch_pages); + if (!grub_decompressor_scratch) + grub_exit (); + + grub_decompress_core ((char *) start + 12, unc, sz - 12, + unc_size); + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + + status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, + image->file_path, unc, unc_size, &image_handle); + if (status != GRUB_EFI_SUCCESS) + efi_call_4 (grub_efi_system_table->boot_services->exit, + grub_efi_image_handle, status, 0, 0); + + status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data); + + efi_call_4 (grub_efi_system_table->boot_services->exit, + grub_efi_image_handle, status, exit_data_size, exit_data); + + grub_exit (); +} + diff --git a/grub-core/boot/mips/scratch.c b/grub-core/boot/mips/scratch.c new file mode 100644 index 000000000..e78147273 --- /dev/null +++ b/grub-core/boot/mips/scratch.c @@ -0,0 +1,42 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2013 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 . + */ + +#include +#include +#include + +void +find_scratch (void *src, void *dst, unsigned long srcsize, + unsigned long dstsize) +{ + /* Decoding from ROM. */ + if (((grub_addr_t) src & 0x10000000)) + { + grub_decompressor_scratch = (void *) ALIGN_UP((grub_addr_t) dst + dstsize, + 256); + return; + } + + if ((char *) src + srcsize > (char *) dst + dstsize) + grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) src + srcsize, + 256); + else + grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) dst + dstsize, + 256); + return; +} diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 2a910bbdf..255baba2d 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -35,7 +35,6 @@ grub_efi_handle_t grub_efi_image_handle; grub_efi_system_table_t *grub_efi_system_table; static grub_efi_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID; -static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID; static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; void * @@ -94,28 +93,6 @@ grub_efi_locate_handle (grub_efi_locate_search_type_t search_type, return buffer; } -void * -grub_efi_open_protocol (grub_efi_handle_t handle, - grub_efi_guid_t *protocol, - grub_efi_uint32_t attributes) -{ - grub_efi_boot_services_t *b; - grub_efi_status_t status; - void *interface; - - b = grub_efi_system_table->boot_services; - status = efi_call_6 (b->open_protocol, handle, - protocol, - &interface, - grub_efi_image_handle, - 0, - attributes); - if (status != GRUB_EFI_SUCCESS) - return 0; - - return interface; -} - int grub_efi_set_text_mode (int on) { @@ -145,23 +122,6 @@ grub_efi_stall (grub_efi_uintn_t microseconds) efi_call_1 (grub_efi_system_table->boot_services->stall, microseconds); } -grub_efi_loaded_image_t * -grub_efi_get_loaded_image (grub_efi_handle_t image_handle) -{ - return grub_efi_open_protocol (image_handle, - &loaded_image_guid, - GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); -} - -void -grub_exit (void) -{ - grub_efi_fini (); - efi_call_4 (grub_efi_system_table->boot_services->exit, - grub_efi_image_handle, GRUB_EFI_SUCCESS, 0, 0); - for (;;) ; -} - grub_err_t grub_efi_set_virtual_address_map (grub_efi_uintn_t memory_map_size, grub_efi_uintn_t descriptor_size, @@ -259,53 +219,6 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, return NULL; } -#pragma GCC diagnostic ignored "-Wcast-align" - -/* Search the mods section from the PE32/PE32+ image. This code uses - a PE32 header, but should work with PE32+ as well. */ -grub_addr_t -grub_efi_modules_addr (void) -{ - grub_efi_loaded_image_t *image; - struct grub_pe32_header *header; - struct grub_pe32_coff_header *coff_header; - struct grub_pe32_section_table *sections; - struct grub_pe32_section_table *section; - struct grub_module_info *info; - grub_uint16_t i; - - image = grub_efi_get_loaded_image (grub_efi_image_handle); - if (! image) - return 0; - - header = image->image_base; - coff_header = &(header->coff_header); - sections - = (struct grub_pe32_section_table *) ((char *) coff_header - + sizeof (*coff_header) - + coff_header->optional_header_size); - - for (i = 0, section = sections; - i < coff_header->num_sections; - i++, section++) - { - if (grub_strcmp (section->name, "mods") == 0) - break; - } - - if (i == coff_header->num_sections) - return 0; - - info = (struct grub_module_info *) ((char *) image->image_base - + section->virtual_address); - if (info->magic != GRUB_MODULE_MAGIC) - return 0; - - return (grub_addr_t) info; -} - -#pragma GCC diagnostic error "-Wcast-align" - char * grub_efi_get_filename (grub_efi_device_path_t *dp0) { diff --git a/grub-core/kern/efi/efi_core.c b/grub-core/kern/efi/efi_core.c new file mode 100644 index 000000000..ccaacea30 --- /dev/null +++ b/grub-core/kern/efi/efi_core.c @@ -0,0 +1,172 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009,2010,2013 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 . + */ + +#include +#include + +static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID; + +void * +grub_efi_open_protocol (grub_efi_handle_t handle, + grub_efi_guid_t *protocol, + grub_efi_uint32_t attributes) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + void *interface; + + b = grub_efi_system_table->boot_services; + status = efi_call_6 (b->open_protocol, handle, + protocol, + &interface, + grub_efi_image_handle, + 0, + attributes); + if (status != GRUB_EFI_SUCCESS) + return 0; + + return interface; +} + +void +grub_exit (void) +{ + grub_efi_fini (); + efi_call_4 (grub_efi_system_table->boot_services->exit, + grub_efi_image_handle, GRUB_EFI_SUCCESS, 0, 0); + for (;;) ; +} + +grub_efi_loaded_image_t * +grub_efi_get_loaded_image (grub_efi_handle_t image_handle) +{ + return grub_efi_open_protocol (image_handle, + &loaded_image_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); +} + +/* Allocate pages. Return the pointer to the first of allocated pages. */ +void * +grub_efi_allocate_pages (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + grub_efi_allocate_type_t type; + grub_efi_status_t status; + grub_efi_boot_services_t *b; + +#if 1 + /* Limit the memory access to less than 4GB for 32-bit platforms. */ + if (address > 0xffffffff) + return 0; +#endif + +#if 1 + if (address == 0) + { + type = GRUB_EFI_ALLOCATE_MAX_ADDRESS; + address = 0xffffffff; + } + else + type = GRUB_EFI_ALLOCATE_ADDRESS; +#else + if (address == 0) + type = GRUB_EFI_ALLOCATE_ANY_PAGES; + else + type = GRUB_EFI_ALLOCATE_ADDRESS; +#endif + + b = grub_efi_system_table->boot_services; + status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address); + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (address == 0) + { + /* Uggh, the address 0 was allocated... This is too annoying, + so reallocate another one. */ + address = 0xffffffff; + status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address); + grub_efi_free_pages (0, pages); + if (status != GRUB_EFI_SUCCESS) + return 0; + } + + return (void *) ((grub_addr_t) address); +} + +/* Free pages starting from ADDRESS. */ +void +grub_efi_free_pages (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + efi_call_2 (b->free_pages, address, pages); +} + + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* Search the mods section from the PE32/PE32+ image. This code uses + a PE32 header, but should work with PE32+ as well. */ +int +grub_efi_get_section (const char *name, + grub_addr_t *start, grub_size_t *sz) +{ + grub_efi_loaded_image_t *image; + struct grub_pe32_header *header; + struct grub_pe32_coff_header *coff_header; + struct grub_pe32_section_table *sections; + struct grub_pe32_section_table *section; + grub_uint16_t i; + + *start = 0; + if (sz) + *sz = 0; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (! image) + return 0; + + header = image->image_base; + coff_header = &(header->coff_header); + sections + = (struct grub_pe32_section_table *) ((char *) coff_header + + sizeof (*coff_header) + + coff_header->optional_header_size); + + for (i = 0, section = sections; + i < coff_header->num_sections; + i++, section++) + { + if (grub_strcmp (section->name, name) == 0) + break; + } + + if (i == coff_header->num_sections) + return 0; + + *start = ((grub_addr_t) image->image_base + section->virtual_address); + if (sz) + *sz = section->virtual_size; + + return 1; +} + +#pragma GCC diagnostic error "-Wcast-align" diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 942ab0256..cbb8d2572 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -31,7 +31,10 @@ grub_addr_t grub_modbase; void grub_efi_init (void) { - grub_modbase = grub_efi_modules_addr (); + if (!grub_efi_get_section ("mods", &grub_modbase, NULL) + || ((struct grub_module_info *) grub_modbase)->magic != GRUB_MODULE_MAGIC) + grub_modbase = 0; + /* First of all, initialize the console so that GRUB can display messages. */ grub_console_init (); diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 1409b5da9..58ac30afc 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -47,66 +47,6 @@ static grub_efi_uintn_t finish_desc_size; static grub_efi_uint32_t finish_desc_version; int grub_efi_is_finished = 0; -/* Allocate pages. Return the pointer to the first of allocated pages. */ -void * -grub_efi_allocate_pages (grub_efi_physical_address_t address, - grub_efi_uintn_t pages) -{ - grub_efi_allocate_type_t type; - grub_efi_status_t status; - grub_efi_boot_services_t *b; - -#if 1 - /* Limit the memory access to less than 4GB for 32-bit platforms. */ - if (address > 0xffffffff) - return 0; -#endif - -#if 1 - if (address == 0) - { - type = GRUB_EFI_ALLOCATE_MAX_ADDRESS; - address = 0xffffffff; - } - else - type = GRUB_EFI_ALLOCATE_ADDRESS; -#else - if (address == 0) - type = GRUB_EFI_ALLOCATE_ANY_PAGES; - else - type = GRUB_EFI_ALLOCATE_ADDRESS; -#endif - - b = grub_efi_system_table->boot_services; - status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address); - if (status != GRUB_EFI_SUCCESS) - return 0; - - if (address == 0) - { - /* Uggh, the address 0 was allocated... This is too annoying, - so reallocate another one. */ - address = 0xffffffff; - status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address); - grub_efi_free_pages (0, pages); - if (status != GRUB_EFI_SUCCESS) - return 0; - } - - return (void *) ((grub_addr_t) address); -} - -/* Free pages starting from ADDRESS. */ -void -grub_efi_free_pages (grub_efi_physical_address_t address, - grub_efi_uintn_t pages) -{ - grub_efi_boot_services_t *b; - - b = grub_efi_system_table->boot_services; - efi_call_2 (b->free_pages, address, pages); -} - #if defined (__i386__) || defined (__x86_64__) /* Helper for stop_broadcom. */ diff --git a/grub-core/lib/posix_wrap/stdlib.h b/grub-core/lib/posix_wrap/stdlib.h index 3b46f47ff..92b0afeb2 100644 --- a/grub-core/lib/posix_wrap/stdlib.h +++ b/grub-core/lib/posix_wrap/stdlib.h @@ -22,6 +22,8 @@ #include #include +#ifndef GRUB_EMBED_DECOMPRESSOR + static inline void free (void *ptr) { @@ -46,6 +48,8 @@ realloc (void *ptr, grub_size_t size) return grub_realloc (ptr, size); } +#endif + static inline int abs (int c) { diff --git a/grub-core/lib/xzembed/xz_private.h b/grub-core/lib/xzembed/xz_private.h index fc845c92d..11f96f7a2 100644 --- a/grub-core/lib/xzembed/xz_private.h +++ b/grub-core/lib/xzembed/xz_private.h @@ -91,6 +91,10 @@ enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, struct xz_dec_lzma2 *lzma2, struct xz_buf *b); /* Free the memory allocated for the BCJ filters. */ +#ifndef GRUB_EMBED_DECOMPRESSOR #define xz_dec_bcj_end(s) kfree(s) +#else +#define xz_dec_bcj_end(s) +#endif #endif diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 489cf9e6d..19c824ca7 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -77,7 +77,9 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd, char **device, char **path); -grub_addr_t grub_efi_modules_addr (void); +int +grub_efi_get_section (const char *name, + grub_addr_t *start, grub_size_t *sz); void grub_efi_mm_init (void); void grub_efi_mm_fini (void);