From 774683685f23e2cc40f26088e258e3238e494e36 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 7 Mar 2013 08:17:24 +0100 Subject: [PATCH] Lift up core size limits on some platforms. Fix potential memory corruption with big core on small memory systems. Document remaining limits. --- ChangeLog | 6 ++++ docs/grub.texi | 38 ++++++++++++++++++++ grub-core/kern/i386/coreboot/init.c | 36 +++++++++++-------- grub-core/kern/i386/pc/init.c | 12 ++++++- grub-core/kern/main.c | 56 +++++++++++++++++++++++++---- grub-core/kern/mm.c | 21 +++++++++++ include/grub/kernel.h | 25 +++++++++++++ include/grub/offsets.h | 1 + util/grub-mkimage.c | 47 +++++++++++++++++++----- 9 files changed, 210 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index f1ab52aa7..96527dd10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2013-03-07 Vladimir Serbinenko + + Lift up core size limits on some platforms. Fix potential memory + corruption with big core on small memory systems. Document remaining + limits. + 2013-03-05 Vladimir Serbinenko * grub-core/term/terminfo.c (grub_terminfo_cls): Issue an explicit diff --git a/docs/grub.texi b/docs/grub.texi index 9941b4742..0b668272c 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -89,6 +89,7 @@ This edition documents version @value{VERSION}. * Serial terminal:: Using GRUB via a serial line * Vendor power-on keys:: Changing GRUB behaviour on vendor power-on keys * Images:: GRUB image files +* Core image size limitation:: GRUB image files size limitations * Filesystem:: Filesystem syntax and semantics * Interface:: The menu and the command-line * Environment:: GRUB environment variables @@ -2390,6 +2391,43 @@ In GRUB 2, images for PXE network booting are now constructed using contains the @samp{pxe} and @samp{pxecmd} modules. @xref{Network}. @end table +@node Core image size limitation +@chapter Core image size limitation + +Heavily limited platforms: +@itemize +@item i386-pc (normal and PXE): the core image size (compressed) is limited by 458240 bytes. + kernel.img (.text + .data + .bss, uncompressed) is limited by 392704 bytes. + module size (uncompressed) + kernel.img (.text + .data, uncompressed) is limited by the size of contiguous chunk at 1M address. +@item sparc64-ieee1275: kernel.img (.text + .data + .bss) + modules + 256K (stack) + 2M (heap) is limited by space available at 0x4400. On most platforms it's just 3 or 4M since ieee1275 maps only so much. +@item i386-ieee1275: kernel.img (.text + .data + .bss) + modules is limited by memory available at 0x10000, at most 596K +@end itemize + +Lightly limited platforms: + +@itemize +@item i386-qemu: kernel.img (.text + .data + .bss) is limited by 392704 bytes. + (core.img would be limited by ROM size but it's unlimited on qemu +@item All EFI platforms: limited by contiguous RAM size and possibly firmware bugs +@item Coreboot and multiboot. kernel.img (.text + .data + .bss) is limited by 392704 bytes. + module size is limited by the size of contiguous chunk at 1M address. +@item mipsel-loongson (ELF), mips(el)-qemu_mips (ELF): if uncompressed: + kernel.img (.text + .data) + modules is limited by the space from 80200000 forward + if compressed: + kernel.img (.text + .data, uncompressed) + modules (uncompressed) + + (modules + kernel.img (.text + .data)) (compressed) + + decompressor is limited by the space from 80200000 forward +@item mipsel-loongson (Flash), mips(el)-qemu_mips (Flash): kernel.img (.text + .data) + modules is limited by the space from 80200000 forward + core.img (final) is limited by flash size (512K on yeeloong and fulooong) +@item mips-arc: if uncompressed: + kernel.img (.text + .data) is limited by the space from 8bd00000 forward + modules + dummy decompressor is limited by the space from 8bd00000 backward + if compressed: + kernel.img (.text + .data, uncompressed) is limited by the space from 8bd00000 forward + modules (uncompressed) + (modules + kernel.img (.text + .data)) (compressed, aligned to 1M) + + 1M (decompressor + scratch space) is limited by the space from 8bd00000 backward +@item powerpc-ieee1275: kernel.img (.text + .data + .bss) + modules is limited by space available at 0x200000 +@end itemize @node Filesystem @chapter Filesystem syntax and semantics diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c index 48fd1a677..bfc8f3f12 100644 --- a/grub-core/kern/i386/coreboot/init.c +++ b/grub-core/kern/i386/coreboot/init.c @@ -54,7 +54,8 @@ grub_exit (void) #ifdef GRUB_MACHINE_QEMU grub_addr_t grub_modbase; #else -grub_addr_t grub_modbase = ALIGN_UP((grub_addr_t) _end, GRUB_KERNEL_MACHINE_MOD_ALIGN); +grub_addr_t grub_modbase = GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR; +static grub_uint64_t modend; #endif /* Helper for grub_machine_init. */ @@ -62,30 +63,32 @@ static int heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, void *data __attribute__ ((unused))) { + grub_uint64_t begin = addr, end = addr + size; + #if GRUB_CPU_SIZEOF_VOID_P == 4 /* Restrict ourselves to 32-bit memory space. */ - if (addr > GRUB_ULONG_MAX) + if (begin > GRUB_ULONG_MAX) return 0; - if (addr + size > GRUB_ULONG_MAX) - size = GRUB_ULONG_MAX - addr; + if (end > GRUB_ULONG_MAX) + end = GRUB_ULONG_MAX; #endif if (type != GRUB_MEMORY_AVAILABLE) return 0; /* Avoid the lower memory. */ - if (addr < GRUB_MEMORY_MACHINE_LOWER_SIZE) - { - if (addr + size <= GRUB_MEMORY_MACHINE_LOWER_SIZE) - return 0; - else - { - size -= GRUB_MEMORY_MACHINE_LOWER_SIZE - addr; - addr = GRUB_MEMORY_MACHINE_LOWER_SIZE; - } - } + if (begin < GRUB_MEMORY_MACHINE_LOWER_SIZE) + begin = GRUB_MEMORY_MACHINE_LOWER_SIZE; - grub_mm_init_region ((void *) (grub_addr_t) addr, (grub_size_t) size); +#ifndef GRUB_MACHINE_QEMU + if (modend && begin < modend) + begin = modend; +#endif + + if (end <= begin) + return 0; + + grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) (end - begin)); return 0; } @@ -97,6 +100,9 @@ grub_machine_init (void) grub_modbase = grub_core_entry_addr + (_edata - _start); grub_qemu_init_cirrus (); +#endif +#ifndef GRUB_MACHINE_QEMU + modend = grub_modules_get_end (); #endif /* Initialize the console as early as possible. */ grub_vga_text_init (); diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c index 7d8b12cf9..730e04ac4 100644 --- a/grub-core/kern/i386/pc/init.c +++ b/grub-core/kern/i386/pc/init.c @@ -191,6 +191,7 @@ grub_machine_init (void) #if 0 int grub_lower_mem; #endif + grub_addr_t modend; grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - _start); @@ -222,8 +223,17 @@ grub_machine_init (void) compact_mem_regions (); + modend = grub_modules_get_end (); for (i = 0; i < num_regions; i++) - grub_mm_init_region ((void *) mem_regions[i].addr, mem_regions[i].size); + { + grub_addr_t beg = mem_regions[i].addr; + grub_addr_t fin = mem_regions[i].addr + mem_regions[i].size; + if (modend && beg < modend) + beg = modend; + if (beg >= fin) + continue; + grub_mm_init_region ((void *) beg, fin - beg); + } grub_tsc_init (); } diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index c43ac6b7f..e1a200166 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -30,8 +30,10 @@ #include #include -/* This is actualy platform-independant but used only on loongson and sparc. */ -#if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) || defined (GRUB_MACHINE_SPARC64) +#ifdef GRUB_MACHINE_PCBIOS +#include +#endif + grub_addr_t grub_modules_get_end (void) { @@ -45,7 +47,6 @@ grub_modules_get_end (void) return grub_modbase + modinfo->size; } -#endif /* Load all modules in core. */ static void @@ -67,6 +68,8 @@ grub_load_modules (void) } } +static char *load_config; + static void grub_load_config (void) { @@ -76,9 +79,17 @@ grub_load_config (void) /* Not an embedded config, skip. */ if (header->type != OBJ_TYPE_CONFIG) continue; - - grub_parser_execute ((char *) header + - sizeof (struct grub_module_header)); + + load_config = grub_malloc (header->size - sizeof (struct grub_module_header) + 1); + if (!load_config) + { + grub_print_error (); + break; + } + grub_memcpy (load_config, (char *) header + + sizeof (struct grub_module_header), + header->size - sizeof (struct grub_module_header)); + load_config[header->size - sizeof (struct grub_module_header)] = 0; break; } } @@ -212,6 +223,30 @@ grub_load_normal_mode (void) grub_command_execute ("normal", 0, 0); } +static void +reclaim_module_space (void) +{ + grub_addr_t modstart, modend; + + if (!grub_modbase) + return; + +#ifdef GRUB_MACHINE_PCBIOS + modstart = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR; +#else + modstart = grub_modbase; +#endif + modend = grub_modules_get_end (); + grub_modbase = 0; + +#if GRUB_KERNEL_PRELOAD_SPACE_REUSABLE + grub_mm_init_region ((void *) modstart, modend - modstart); +#else + (void) modstart; + (void) modend; +#endif +} + /* The main routine. */ void __attribute__ ((noreturn)) grub_main (void) @@ -224,6 +259,8 @@ grub_main (void) grub_printf ("Welcome to GRUB!\n\n"); grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + grub_load_config (); + /* Load pre-loaded modules and free the space. */ grub_register_exported_symbols (); #ifdef GRUB_LINKER_HAVE_INIT @@ -237,9 +274,14 @@ grub_main (void) grub_env_export ("root"); grub_env_export ("prefix"); + /* Reclaim space used for modules. */ + reclaim_module_space (); + grub_register_core_commands (); - grub_load_config (); + if (load_config) + grub_parser_execute (load_config); + grub_load_normal_mode (); grub_rescue_run (); } diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 9f08e05bf..d8690916c 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -117,6 +117,27 @@ grub_mm_init_region (void *addr, grub_size_t size) grub_printf ("Using memory for heap: start=%p, end=%p\n", addr, addr + (unsigned int) size); #endif + for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p) + if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q) + { + r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); + *r = *q; + r->pre_size += size; + + if (r->pre_size >> GRUB_MM_ALIGN_LOG2) + { + h = (grub_mm_header_t) (r + 1); + h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2); + h->magic = GRUB_MM_ALLOC_MAGIC; + r->size += h->size << GRUB_MM_ALIGN_LOG2; + r->pre_size &= (GRUB_MM_ALIGN - 1); + *p = r; + grub_free (h + 1); + } + *p = r; + return; + } + /* Allocate a region from the head. */ r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); size -= (char *) r - (char *) addr + sizeof (*r); diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 23e4f02c0..73ea41656 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -64,6 +64,29 @@ struct grub_module_info64 grub_uint64_t size; }; +#ifndef GRUB_UTIL +/* Space isn't reusable on some platforms. */ +/* On Qemu the preload space is readonly. */ +/* On emu there is no preload space. */ +/* On ieee1275 our code assumes that heap is p=v which isn't guaranteed for module space. */ +#if defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_EMU) \ + || defined (GRUB_MACHINE_EFI) \ + || (defined (GRUB_MACHINE_IEEE1275) && !defined (__sparc__)) +#define GRUB_KERNEL_PRELOAD_SPACE_REUSABLE 0 +#endif + +#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) \ + || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) \ + || defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_ARC) \ + || defined (__sparc__) +/* FIXME: stack is between 2 heap regions. Move it. */ +#define GRUB_KERNEL_PRELOAD_SPACE_REUSABLE 1 +#endif + +#ifndef GRUB_KERNEL_PRELOAD_SPACE_REUSABLE +#error "Please check if preload space is reusable on this platform!" +#endif + #if GRUB_TARGET_SIZEOF_VOID_P == 8 #define grub_module_info grub_module_info64 #else @@ -82,6 +105,8 @@ extern grub_addr_t EXPORT_VAR (grub_modbase); grub_addr_t grub_modules_get_end (void); +#endif + /* The start point of the C code. */ void grub_main (void) __attribute__ ((noreturn)); diff --git a/include/grub/offsets.h b/include/grub/offsets.h index d55e308c7..bce755d98 100644 --- a/include/grub/offsets.h +++ b/include/grub/offsets.h @@ -91,6 +91,7 @@ #define GRUB_KERNEL_MIPS_ARC_TOTAL_MODULE_SIZE 0x08 #define GRUB_KERNEL_I386_COREBOOT_LINK_ADDR 0x8200 +#define GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR 0x100000 #define GRUB_KERNEL_I386_IEEE1275_LINK_ADDR 0x10000 diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index 29bda1715..845abeda0 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -991,6 +991,38 @@ generate_image (const char *dir, const char *prefix, core_size = full_size; } + switch (image_target->id) + { + case IMAGE_I386_PC: + case IMAGE_I386_PC_PXE: + if (GRUB_KERNEL_I386_PC_LINK_ADDR + core_size > 0x78000 + || (core_size > (0xffff << GRUB_DISK_SECTOR_BITS)) + || (kernel_size + bss_size + GRUB_KERNEL_I386_PC_LINK_ADDR > 0x68000)) + grub_util_error (_("core image is too big (0x%x > 0x%x)"), + GRUB_KERNEL_I386_PC_LINK_ADDR + core_size, + 0x78000); + /* fallthrough */ + case IMAGE_COREBOOT: + case IMAGE_QEMU: + if (kernel_size + bss_size + GRUB_KERNEL_I386_PC_LINK_ADDR > 0x68000) + grub_util_error (_("kernel image is too big (0x%x > 0x%x)"), + kernel_size + bss_size + GRUB_KERNEL_I386_PC_LINK_ADDR, + 0x68000); + break; + case IMAGE_LOONGSON_ELF: + case IMAGE_YEELOONG_FLASH: + case IMAGE_FULOONG2F_FLASH: + case IMAGE_EFI: + case IMAGE_MIPS_ARC: + case IMAGE_QEMU_MIPS_FLASH: + break; + case IMAGE_SPARC64_AOUT: + case IMAGE_SPARC64_RAW: + case IMAGE_I386_IEEE1275: + case IMAGE_PPC: + break; + } + switch (image_target->id) { case IMAGE_I386_PC: @@ -1000,12 +1032,6 @@ generate_image (const char *dir, const char *prefix, char *boot_path, *boot_img; size_t boot_size; - if (GRUB_KERNEL_I386_PC_LINK_ADDR + core_size > 0x78000 - || (core_size > (0xffff << GRUB_DISK_SECTOR_BITS))) - grub_util_error (_("core image is too big (0x%x > 0x%x)"), - GRUB_KERNEL_I386_PC_LINK_ADDR + core_size, - 0x78000); - num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); if (image_target->id == IMAGE_I386_PC_PXE) { @@ -1615,9 +1641,12 @@ generate_image (const char *dir, const char *prefix, phdr->p_filesz = phdr->p_memsz = grub_host_to_target32 (core_size - kernel_size); - target_addr_mods = ALIGN_UP (target_addr + kernel_size + bss_size - + image_target->mod_gap, - image_target->mod_align); + if (image_target->id == IMAGE_COREBOOT) + target_addr_mods = GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR; + else + target_addr_mods = ALIGN_UP (target_addr + kernel_size + bss_size + + image_target->mod_gap, + image_target->mod_align); phdr->p_vaddr = grub_host_to_target32 (target_addr_mods); phdr->p_paddr = grub_host_to_target32 (target_addr_mods); phdr->p_align = grub_host_to_target32 (image_target->link_align);