From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756531Ab1JQQju (ORCPT ); Mon, 17 Oct 2011 12:39:50 -0400 Received: from mail-ey0-f174.google.com ([209.85.215.174]:44297 "EHLO mail-ey0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756477Ab1JQQjt (ORCPT ); Mon, 17 Oct 2011 12:39:49 -0400 Message-ID: <4E9C5A4D.7090802@gmail.com> Date: Mon, 17 Oct 2011 18:39:41 +0200 From: Maarten Lankhorst User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:7.0) Gecko/20110927 Thunderbird/7.0 MIME-Version: 1.0 To: Matt Fleming CC: "H. Peter Anvin" , Matthew Garrett , linux-kernel@vger.kernel.org, Ingo Molnar , Thomas Gleixner , x86@kernel.org, Matt Fleming , Mike Waychison , Andi Kleen Subject: Re: [PATCH v5 10/10] x86, efi: EFI boot stub support References: <1318848017-12301-1-git-send-email-matt@console-pimps.org> <1318848017-12301-11-git-send-email-matt@console-pimps.org> In-Reply-To: <1318848017-12301-11-git-send-email-matt@console-pimps.org> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hey Matt, On 10/17/2011 12:40 PM, Matt Fleming wrote: > From: Matt Fleming > > There is currently a large divide between kernel development and the > development of EFI boot loaders. The idea behind this patch is to give > the kernel developers full control over the EFI boot process. As > H. Peter Anvin put it, > > "The 'kernel carries its own stub' approach been very successful in > dealing with BIOS, and would make a lot of sense to me for EFI as > well." > > This patch introduces an EFI boot stub that allows an x86 bzImage to > be loaded and executed by EFI firmware. The bzImage appears to the > firmware as an EFI application. Luckily there are enough free bits > within the bzImage header so that it can masquerade as an EFI > application, thereby coercing the EFI firmware into loading it and > jumping to its entry point. The beauty of this masquerading approach > is that both BIOS and EFI boot loaders can still load and run the same > bzImage, thereby allowing a single kernel image to work in any boot > environment. > > The EFI boot stub supports multiple initrds, but they must exist on > the same partition as the bzImage. Command-line arguments for the > kernel can be appended after the bzImage name when run from the EFI > shell, e.g. > > Shell> bzImage console=ttyS0 root=/dev/sdb initrd=initrd.img > > Cc: H. Peter Anvin > Cc: Thomas Gleixner > Cc: Ingo Molnar > Cc: Mike Waychison > Cc: Matthew Garrett > Cc: Andi Kleen > Cc: Maarten Lankhorst > Tested-by: Henrik Rydberg > Signed-off-by: Matt Fleming > --- > > v5: > > - load_options_size is UTF-16, which needs dividing by 2 to convert > to the corresponding ASCII size (spotted by Maarten). > > v4: > > - Don't read more than image->load_options_size > > v3: > > - Fix following warnings when compiling CONFIG_EFI_STUB=n > > arch/x86/boot/tools/build.c: In function ‘main’: > arch/x86/boot/tools/build.c:138:24: warning: unused variable ‘pe_header’ > arch/x86/boot/tools/build.c:138:15: warning: unused variable ‘file_sz’ > > - As reported by Matthew Garrett, some Apple machines have GOPs that > don't have hardware attached. We need to weed these out by > searching for ones that handle the PCIIO protocol. > > - Don't allocate memory if no initrds are on cmdline > - Don't trust image->load_options_size > > Maarten Lankhorst noted: > - Don't strip first argument when booted from efibootmgr > - Don't allocate too much memory for cmdline > - Don't update cmdline_size, the kernel considers it read-only > - Don't accept '\n' for initrd names > > v2: > > - File alignment was too large, was 8192 should be 512. Reported by > Maarten Lankhorst on LKML. > - Added UGA support for graphics > - Use VIDEO_TYPE_EFI instead of hard-coded number. > - Move linelength assignment until after we've assigned depth > - Dynamically fill out AddressOfEntryPoint in tools/build.c > - Don't use magic number for GDT/TSS stuff. Requested by Andi Kleen > - The bzImage may need to be relocated as it may have been loaded at > a high address address by the firmware. This was required to get my > macbook booting because the firmware loaded it at 0x7cxxxxxx, which > triggers this error in decompress_kernel(), > > if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff)) > error("Destination address too large"); > > arch/x86/Kconfig | 7 + > arch/x86/boot/compressed/Makefile | 10 +- > arch/x86/boot/compressed/eboot.c | 975 ++++++++++++++++++++++++++++++++ > arch/x86/boot/compressed/efi_stub_32.S | 87 +++ > arch/x86/boot/compressed/efi_stub_64.S | 1 + > arch/x86/boot/compressed/head_32.S | 22 + > arch/x86/boot/compressed/head_64.S | 20 + > arch/x86/boot/compressed/string.c | 9 + > arch/x86/boot/header.S | 158 +++++ > arch/x86/boot/string.c | 35 ++ > arch/x86/boot/tools/build.c | 39 ++ > arch/x86/kernel/asm-offsets.c | 2 + > 12 files changed, 1364 insertions(+), 1 deletions(-) > create mode 100644 arch/x86/boot/compressed/eboot.c > create mode 100644 arch/x86/boot/compressed/efi_stub_32.S > create mode 100644 arch/x86/boot/compressed/efi_stub_64.S > > diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig > index 6a47bb2..d40c876 100644 > --- a/arch/x86/Kconfig > +++ b/arch/x86/Kconfig > @@ -1466,6 +1466,13 @@ config EFI > resultant kernel should continue to boot on existing non-EFI > platforms. > > +config EFI_STUB > + bool "EFI stub support" > + depends on EFI > + ---help--- > + This kernel feature allows a bzImage to be loaded directly > + by EFI firmware without the use of a bootloader. > + > config SECCOMP > def_bool y > prompt "Enable seccomp to safely compute untrusted bytecode" > diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile > index 09664ef..b123b9a 100644 > --- a/arch/x86/boot/compressed/Makefile > +++ b/arch/x86/boot/compressed/Makefile > @@ -23,7 +23,15 @@ LDFLAGS_vmlinux := -T > > hostprogs-y := mkpiggy > > -$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o $(obj)/piggy.o FORCE > +VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \ > + $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \ > + $(obj)/piggy.o > + > +ifeq ($(CONFIG_EFI_STUB), y) > + VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o > +endif > + > +$(obj)/vmlinux: $(VMLINUX_OBJS) FORCE > $(call if_changed,ld) > @: > > diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c > new file mode 100644 > index 0000000..f84e389 > --- /dev/null > +++ b/arch/x86/boot/compressed/eboot.c > @@ -0,0 +1,975 @@ > +/* ----------------------------------------------------------------------- > + * > + * Copyright 2011 Intel Corporation; author Matt Fleming > + * > + * This file is part of the Linux kernel, and is made available under > + * the terms of the GNU General Public License version 2. > + * > + * ----------------------------------------------------------------------- */ > + > +#include > +#include > +#include > +#include > + > +#define SEG_TYPE_DATA (0 << 3) > +#define SEG_TYPE_READ_WRITE (1 << 1) > +#define SEG_TYPE_CODE (1 << 3) > +#define SEG_TYPE_EXEC_READ (1 << 1) > +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) > +#define SEG_OP_SIZE_32BIT (1 << 0) > +#define SEG_GRANULARITY_4KB (1 << 0) > + > +#define DESC_TYPE_CODE_DATA (1 << 0) > + > +#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) > + > +#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 > +#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 > +#define PIXEL_BIT_MASK 2 > +#define PIXEL_BLT_ONLY 3 > +#define PIXEL_FORMAT_MAX 4 > + > +typedef struct { > + u32 red_mask; > + u32 green_mask; > + u32 blue_mask; > + u32 reserved_mask; > +} efi_pixel_bitmask_t; > + > +typedef struct { > + u32 version; > + u32 horizontal_resolution; > + u32 vertical_resolution; > + int pixel_format; > + efi_pixel_bitmask_t pixel_information; > + u32 pixels_per_scan_line; > +} __attribute__((packed)) efi_graphics_output_mode_information_t; > + > +typedef struct { > + u32 max_mode; > + u32 mode; > + unsigned long info; > + unsigned long size_of_info; > + u64 frame_buffer_base; > + unsigned long frame_buffer_size; > +} __attribute__((packed)) efi_graphics_output_protocol_mode_t; > + > +typedef struct { > + void *query_mode; > + unsigned long set_mode; > + unsigned long blt; > + efi_graphics_output_protocol_mode_t *mode; > +} efi_graphics_output_protocol_t; > + > +typedef struct { > + void *get_mode; > + void *set_mode; > + void *blt; > +} efi_uga_draw_protocol_t; > + > +static efi_system_table_t *sys_table; > + > +static efi_status_t low_alloc(unsigned long size, unsigned long align, > + unsigned long *addr) > +{ > + unsigned long map_size, key, desc_size; > + efi_memory_desc_t *map; > + efi_status_t status; > + unsigned long nr_pages; > + u32 desc_version; > + int i; > + > + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; > + > + map_size = sizeof(*map) * 32; > + > +again: > + /* > + * Add an additional efi_memory_desc_t because we're doing an > + * allocation which may be in a new descriptor region. > + */ > + map_size += sizeof(*map); > + status = efi_call_phys3(sys_table->boottime->allocate_pool, > + EFI_LOADER_DATA, map_size, (void **)&map); > + if (status != EFI_SUCCESS) > + goto fail; > + > + status = efi_call_phys5(sys_table->boottime->get_memory_map, &map_size, > + map, &key, &desc_size, &desc_version); > + if (status == EFI_BUFFER_TOO_SMALL) { > + efi_call_phys1(sys_table->boottime->free_pool, map); > + goto again; > + } > + > + if (status != EFI_SUCCESS) > + goto free_pool; > + > + for (i = 0; i < map_size / desc_size; i++) { > + efi_memory_desc_t *desc; > + u64 start, end; > + > + desc = (efi_memory_desc_t *)((unsigned long)map + (i * desc_size)); > + > + if (desc->type != EFI_CONVENTIONAL_MEMORY) > + continue; > + > + if (desc->num_pages < nr_pages) > + continue; > + > + start = desc->phys_addr; > + end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); > + > + /* > + * Don't allocate at 0x0. It will confuse code that > + * checks pointers against NULL. Skip the first 8 > + * bytes so we start at a nice even number. > + */ > + if (start == 0x0) { > + start += 8; > + > + /* Check for tiny memory regions */ > + if (start >= end) > + continue; > + } > + > + start = round_up(start, align); > + if ((start + size) > end) > + continue; > + > + status = efi_call_phys4(sys_table->boottime->allocate_pages, > + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, > + nr_pages, &start); > + if (status == EFI_SUCCESS) { > + *addr = start; > + break; > + } > + } > + > + if (i == map_size / desc_size) > + status = EFI_NOT_FOUND; > + > +free_pool: > + efi_call_phys1(sys_table->boottime->free_pool, map); > +fail: > + return status; > +} > + > +static void low_free(unsigned long size, unsigned long addr) > +{ > + unsigned long nr_pages; > + > + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; > + efi_call_phys2(sys_table->boottime->free_pages, addr, size); > +} > + > +static void find_bits(unsigned long mask, u8 *pos, u8 *size) > +{ > + u8 first, len; > + > + first = 0; > + len = 0; > + > + if (mask) { > + while (!(mask & 0x1)) { > + mask = mask >> 1; > + first++; > + } > + > + while (mask & 0x1) { > + mask = mask >> 1; > + len++; > + } > + } > + > + *pos = first; > + *size = len; > +} > + > +/* > + * See if we have Graphics Output Protocol > + */ > +static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, > + unsigned long size) > +{ > + efi_graphics_output_protocol_t *gop, *first_gop; > + efi_pixel_bitmask_t pixel_info; > + unsigned long nr_gops; > + efi_status_t status; > + void **gop_handle; > + u16 width, height; > + u32 fb_base, fb_size; > + u32 pixels_per_scan_line; > + int pixel_format; > + int i; > + > + status = efi_call_phys3(sys_table->boottime->allocate_pool, > + EFI_LOADER_DATA, size, &gop_handle); > + if (status != EFI_SUCCESS) > + return status; > + > + status = efi_call_phys5(sys_table->boottime->locate_handle, > + EFI_LOCATE_BY_PROTOCOL, proto, > + NULL, &size, gop_handle); > + if (status != EFI_SUCCESS) > + goto free_handle; > + > + first_gop = NULL; > + > + nr_gops = size / sizeof(void *); > + for (i = 0; i < nr_gops; i++) { > + efi_graphics_output_mode_information_t *info; > + efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID; > + void *pciio; > + void *h = gop_handle[i]; > + > + status = efi_call_phys3(sys_table->boottime->handle_protocol, > + h, proto, &gop); > + if (status != EFI_SUCCESS) > + continue; > + > + efi_call_phys3(sys_table->boottime->handle_protocol, > + h, &pciio_proto, &pciio); > + > + status = efi_call_phys4(gop->query_mode, gop, > + gop->mode->mode, &size, &info); > + if (status == EFI_SUCCESS && (!first_gop || pciio)) { > + /* > + * Apple provide GOPs that are not backed by > + * real hardware (they're used to handle > + * multiple displays). The workaround is to > + * search for a GOP implementing the PCIIO > + * protocol, and if one isn't found, to just > + * fallback to the first GOP. > + */ > + width = info->horizontal_resolution; > + height = info->vertical_resolution; > + fb_base = gop->mode->frame_buffer_base; > + fb_size = gop->mode->frame_buffer_size; > + pixel_format = info->pixel_format; > + pixel_info = info->pixel_information; > + pixels_per_scan_line = info->pixels_per_scan_line; > + > + /* > + * Once we've found a GOP supporting PCIIO, > + * don't bother looking any further. > + */ > + if (pciio) > + break; > + > + first_gop = gop; > + } > + } > + > + /* Did we find any GOPs? */ > + if (!first_gop) > + goto free_handle; > + > + /* EFI framebuffer */ > + si->orig_video_isVGA = VIDEO_TYPE_EFI; > + > + si->lfb_width = width; > + si->lfb_height = height; > + si->lfb_base = fb_base; > + si->lfb_size = fb_size; > + si->pages = 1; > + > + if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { > + si->lfb_depth = 32; > + si->lfb_linelength = pixels_per_scan_line * 4; > + si->red_size = 8; > + si->red_pos = 0; > + si->green_size = 8; > + si->green_pos = 8; > + si->blue_size = 8; > + si->blue_pos = 16; > + si->rsvd_size = 8; > + si->rsvd_pos = 24; > + } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) { > + si->lfb_depth = 32; > + si->lfb_linelength = pixels_per_scan_line * 4; > + si->red_size = 8; > + si->red_pos = 16; > + si->green_size = 8; > + si->green_pos = 8; > + si->blue_size = 8; > + si->blue_pos = 0; > + si->rsvd_size = 8; > + si->rsvd_pos = 24; > + } else if (pixel_format == PIXEL_BIT_MASK) { > + find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size); > + find_bits(pixel_info.green_mask, &si->green_pos, > + &si->green_size); > + find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size); > + find_bits(pixel_info.reserved_mask, &si->rsvd_pos, > + &si->rsvd_size); > + si->lfb_depth = si->red_size + si->green_size + > + si->blue_size + si->rsvd_size; > + si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8; > + } else { > + si->lfb_depth = 4; > + si->lfb_linelength = si->lfb_width / 2; > + si->red_size = 0; > + si->red_pos = 0; > + si->green_size = 0; > + si->green_pos = 0; > + si->blue_size = 0; > + si->blue_pos = 0; > + si->rsvd_size = 0; > + si->rsvd_pos = 0; > + } > + > +free_handle: > + efi_call_phys1(sys_table->boottime->free_pool, gop_handle); > + return status; > +} > + > +/* > + * See if we have Universal Graphics Adapter (UGA) protocol > + */ > +static efi_status_t setup_uga(struct screen_info *si, efi_guid_t *uga_proto, > + unsigned long size) > +{ > + efi_uga_draw_protocol_t *uga, *first_uga; > + unsigned long nr_ugas; > + efi_status_t status; > + u32 width, height; > + void **uga_handle = NULL; > + int i; > + > + status = efi_call_phys3(sys_table->boottime->allocate_pool, > + EFI_LOADER_DATA, size, &uga_handle); > + if (status != EFI_SUCCESS) > + return status; > + > + status = efi_call_phys5(sys_table->boottime->locate_handle, > + EFI_LOCATE_BY_PROTOCOL, uga_proto, > + NULL, &size, uga_handle); > + if (status != EFI_SUCCESS) > + goto free_handle; > + > + first_uga = NULL; > + > + nr_ugas = size / sizeof(void *); > + for (i = 0; i < nr_ugas; i++) { > + efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID; > + void *handle = uga_handle[i]; > + u32 w, h, depth, refresh; > + void *pciio; > + > + status = efi_call_phys3(sys_table->boottime->handle_protocol, > + handle, uga_proto, &uga); > + if (status != EFI_SUCCESS) > + continue; > + > + efi_call_phys3(sys_table->boottime->handle_protocol, > + handle, &pciio_proto, &pciio); > + > + status = efi_call_phys5(uga->get_mode, uga, &w, &h, > + &depth, &refresh); > + if (status == EFI_SUCCESS && (!first_uga || pciio)) { > + width = w; > + height = h; > + > + /* > + * Once we've found a UGA supporting PCIIO, > + * don't bother looking any further. > + */ > + if (pciio) > + break; > + > + first_uga = uga; > + } > + } > + > + if (!first_uga) > + goto free_handle; > + > + /* EFI framebuffer */ > + si->orig_video_isVGA = VIDEO_TYPE_EFI; > + > + si->lfb_depth = 32; > + si->lfb_width = width; > + si->lfb_height = height; > + > + si->red_size = 8; > + si->red_pos = 16; > + si->green_size = 8; > + si->green_pos = 8; > + si->blue_size = 8; > + si->blue_pos = 0; > + si->rsvd_size = 8; > + si->rsvd_pos = 24; > + > + > +free_handle: > + efi_call_phys1(sys_table->boottime->free_pool, uga_handle); > + return status; > +} > + > +void setup_graphics(struct boot_params *boot_params) > +{ > + efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; > + struct screen_info *si; > + efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID; > + efi_status_t status; > + unsigned long size; > + void **gop_handle = NULL; > + void **uga_handle = NULL; > + > + si = &boot_params->screen_info; > + memset(si, 0, sizeof(*si)); > + > + size = 0; > + status = efi_call_phys5(sys_table->boottime->locate_handle, > + EFI_LOCATE_BY_PROTOCOL, &graphics_proto, > + NULL, &size, gop_handle); > + if (status == EFI_BUFFER_TOO_SMALL) > + status = setup_gop(si, &graphics_proto, size); > + > + if (status != EFI_SUCCESS) { > + size = 0; > + status = efi_call_phys5(sys_table->boottime->locate_handle, > + EFI_LOCATE_BY_PROTOCOL, &uga_proto, > + NULL, &size, uga_handle); > + if (status == EFI_BUFFER_TOO_SMALL) > + setup_uga(si, &uga_proto, size); > + } > +} > + > +struct initrd { > + efi_file_handle_t *handle; > + u64 size; > +}; > + > +/* > + * Check the cmdline for a LILO-style initrd= arguments. > + * > + * We only support loading an initrd from the same filesystem as the > + * kernel image. > + */ > +static efi_status_t handle_ramdisks(efi_loaded_image_t *image, > + struct setup_header *hdr) > +{ > + struct initrd *initrds; > + unsigned long initrd_addr; > + efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; > + u64 initrd_total; > + efi_file_io_interface_t *io; > + efi_file_handle_t *fh; > + efi_status_t status; > + int nr_initrds; > + char *str; > + int i, j, k; > + > + initrd_addr = 0; > + initrd_total = 0; > + > + str = (char *)(unsigned long)hdr->cmd_line_ptr; > + > + j = 0; /* See close_handles */ > + > + if (!str || !*str) > + return EFI_SUCCESS; > + > + for (nr_initrds = 0; *str; nr_initrds++) { > + str = strstr(str, "initrd="); > + if (!str) > + break; > + > + str += 7; > + > + /* Skip any leading slashes */ > + while (*str == '/' || *str == '\\') > + str++; > + > + while (*str && *str != ' ' && *str != '\n') > + str++; > + } > + > + if (!nr_initrds) > + return EFI_SUCCESS; > + > + status = efi_call_phys3(sys_table->boottime->allocate_pool, > + EFI_LOADER_DATA, > + nr_initrds * sizeof(*initrds), > + &initrds); > + if (status != EFI_SUCCESS) > + goto fail; > + > + str = (char *)(unsigned long)hdr->cmd_line_ptr; > + for (i = 0; i < nr_initrds; i++) { > + struct initrd *initrd; > + efi_file_handle_t *h; > + efi_file_info_t *info; > + efi_char16_t filename[256]; > + unsigned long info_sz; > + efi_guid_t info_guid = EFI_FILE_INFO_ID; > + efi_char16_t *p; > + u64 file_sz; > + > + str = strstr(str, "initrd="); > + if (!str) > + break; > + > + str += 7; > + > + initrd = &initrds[i]; > + p = filename; > + > + /* Skip any leading slashes */ > + while (*str == '/' || *str == '\\') > + str++; > + > + while (*str && *str != ' ' && *str != '\n') { > + if (p >= filename + sizeof(filename)) > + break; > + > + *p++ = *str++; > + } > + > + *p = '\0'; > + > + /* Only open the volume once. */ > + if (!i) { > + efi_boot_services_t *boottime; > + > + boottime = sys_table->boottime; > + > + status = efi_call_phys3(boottime->handle_protocol, > + image->device_handle, &fs_proto, &io); > + if (status != EFI_SUCCESS) > + goto free_initrds; > + > + status = efi_call_phys2(io->open_volume, io, &fh); > + if (status != EFI_SUCCESS) > + goto free_initrds; > + } > + > + status = efi_call_phys5(fh->open, fh, &h, filename, > + EFI_FILE_MODE_READ, (u64)0); > + if (status != EFI_SUCCESS) > + goto close_handles; > + > + initrd->handle = h; > + > + info_sz = 0; > + status = efi_call_phys4(h->get_info, h, &info_guid, > + &info_sz, NULL); > + if (status != EFI_BUFFER_TOO_SMALL) > + goto close_handles; > + > +grow: > + status = efi_call_phys3(sys_table->boottime->allocate_pool, > + EFI_LOADER_DATA, info_sz, &info); > + if (status != EFI_SUCCESS) > + goto close_handles; > + > + status = efi_call_phys4(h->get_info, h, &info_guid, > + &info_sz, info); > + if (status == EFI_BUFFER_TOO_SMALL) { > + efi_call_phys1(sys_table->boottime->free_pool, info); > + goto grow; > + } > + > + file_sz = info->file_size; > + efi_call_phys1(sys_table->boottime->free_pool, info); > + > + if (status != EFI_SUCCESS) > + goto close_handles; > + > + initrd->size = file_sz; > + initrd_total += file_sz; > + } > + > + if (initrd_total) { > + unsigned long addr; > + > + /* > + * Multiple initrd's need to be at consecutive > + * addresses in memory, so allocate enough memory for > + * all the initrd's. > + */ > + status = low_alloc(initrd_total, 0x1000, &initrd_addr); > + if (status != EFI_SUCCESS) > + goto close_handles; > + > + /* We've run out of free low memory. */ > + if (initrd_addr > hdr->initrd_addr_max) { > + status = EFI_INVALID_PARAMETER; > + goto free_initrd_total; > + } > + > + addr = initrd_addr; > + for (j = 0; j < nr_initrds; j++) { > + u64 size; > + > + size = initrds[j].size; > + status = efi_call_phys3(fh->read, initrds[j].handle, > + &size, addr); > + if (status != EFI_SUCCESS) > + goto free_initrd_total; > + > + efi_call_phys1(fh->close, initrds[j].handle); > + > + addr += size; > + } > + > + } > + > + efi_call_phys1(sys_table->boottime->free_pool, initrds); > + > + hdr->ramdisk_image = initrd_addr; > + hdr->ramdisk_size = initrd_total; > + > + return status; > + > +free_initrd_total: > + low_free(initrd_total, initrd_addr); > + > +close_handles: > + for (k = j; k < nr_initrds; k++) > + efi_call_phys1(fh->close, initrds[k].handle); > +free_initrds: > + efi_call_phys1(sys_table->boottime->free_pool, initrds); > +fail: > + hdr->ramdisk_image = 0; > + hdr->ramdisk_size = 0; > + > + return status; > +} > + > +/* > + * Because the x86 boot code expects to be passed a boot_params we > + * need to create one ourselves (usually the bootloader would create > + * one for us). > + */ > +static efi_status_t make_boot_params(struct boot_params *boot_params, > + efi_loaded_image_t *image, > + void *handle) > +{ > + struct efi_info *efi = &boot_params->efi_info; > + struct apm_bios_info *bi = &boot_params->apm_bios_info; > + struct sys_desc_table *sdt = &boot_params->sys_desc_table; > + struct e820entry *e820_map = &boot_params->e820_map[0]; > + struct e820entry *prev = NULL; > + struct setup_header *hdr = &boot_params->hdr; > + unsigned long size, key, desc_size, _size; > + efi_memory_desc_t *mem_map; > + void *options = image->load_options; > + u32 load_options_size = image->load_options_size / 2; /* ASCII */ > + int options_size = 0; > + efi_status_t status; > + __u32 desc_version; > + unsigned long cmdline; > + u8 nr_entries; > + u16 *s2; > + u8 *s1; > + int i; > + > + hdr->type_of_loader = 0x21; > + > + /* Convert unicode cmdline to ascii */ > + cmdline = 0; > + s2 = (u16 *)options; > + > + if (s2) { > + while (*s2 && *s2 != '\n' && options_size < load_options_size) { > + s2++; > + options_size++; > + } > + > + if (options_size) { > + if (options_size > hdr->cmdline_size) > + options_size = hdr->cmdline_size; > + > + options_size++; /* NUL termination */ > + > + status = low_alloc(options_size, 1, &cmdline); > + if (status != EFI_SUCCESS) > + goto fail; Shouldn't this be options_size + 1? > + > + s1 = (u8 *)(unsigned long)cmdline; > + s2 = (u16 *)options; > + > + for (i = 0; i < options_size - 1; i++) > + *s1++ = *s2++; > + > + *s1 = '\0'; For this null character.. > + } > + } > + > + hdr->cmd_line_ptr = cmdline; > + > + hdr->ramdisk_image = 0; > + hdr->ramdisk_size = 0; > + > + status = handle_ramdisks(image, hdr); > + if (status != EFI_SUCCESS) > + goto free_cmdline; > + > + setup_graphics(boot_params); > + > + /* Clear APM BIOS info */ > + memset(bi, 0, sizeof(*bi)); > + > + memset(sdt, 0, sizeof(*sdt)); > + > + memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); > + > + size = sizeof(*mem_map) * 32; > + > +again: > + size += sizeof(*mem_map); > + _size = size; > + status = low_alloc(size, 1, (unsigned long *)&mem_map); > + if (status != EFI_SUCCESS) > + goto free_cmdline; > + > + status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, > + mem_map, &key, &desc_size, &desc_version); > + if (status == EFI_BUFFER_TOO_SMALL) { > + low_free(_size, (unsigned long)mem_map); > + goto again; > + } > + > + if (status != EFI_SUCCESS) > + goto free_mem_map; > + > + efi->efi_systab = (unsigned long)sys_table; > + efi->efi_memdesc_size = desc_size; > + efi->efi_memdesc_version = desc_version; > + efi->efi_memmap = (unsigned long)mem_map; > + efi->efi_memmap_size = size; > + > +#ifdef CONFIG_X86_64 > + efi->efi_systab_hi = (unsigned long)sys_table >> 32; > + efi->efi_memmap_hi = (unsigned long)mem_map >> 32; > +#endif > + > + /* Might as well exit boot services now */ > + status = efi_call_phys2(sys_table->boottime->exit_boot_services, > + handle, key); > + if (status != EFI_SUCCESS) > + goto free_mem_map; > + > + /* Historic? */ > + boot_params->alt_mem_k = 32 * 1024; > + > + /* > + * Convert the EFI memory map to E820. > + */ > + nr_entries = 0; > + for (i = 0; i < size / desc_size; i++) { > + efi_memory_desc_t *d; > + unsigned int e820_type = 0; > + > + d = (efi_memory_desc_t *)((unsigned long)mem_map + (i * desc_size)); > + switch(d->type) { > + case EFI_RESERVED_TYPE: > + case EFI_RUNTIME_SERVICES_CODE: > + case EFI_RUNTIME_SERVICES_DATA: > + case EFI_MEMORY_MAPPED_IO: > + case EFI_MEMORY_MAPPED_IO_PORT_SPACE: > + case EFI_PAL_CODE: > + e820_type = E820_RESERVED; > + break; > + > + case EFI_UNUSABLE_MEMORY: > + e820_type = E820_UNUSABLE; > + break; > + > + case EFI_ACPI_RECLAIM_MEMORY: > + e820_type = E820_ACPI; > + break; > + > + case EFI_LOADER_CODE: > + case EFI_LOADER_DATA: > + case EFI_BOOT_SERVICES_CODE: > + case EFI_BOOT_SERVICES_DATA: > + case EFI_CONVENTIONAL_MEMORY: > + e820_type = E820_RAM; > + break; > + > + case EFI_ACPI_MEMORY_NVS: > + e820_type = E820_NVS; > + break; > + > + default: > + continue; > + } > + > + /* Merge adjacent mappings */ > + if (prev && prev->type == e820_type && > + (prev->addr + prev->size) == d->phys_addr) > + prev->size += d->num_pages << 12; > + else { > + e820_map->addr = d->phys_addr; > + e820_map->size = d->num_pages << 12; > + e820_map->type = e820_type; > + prev = e820_map++; > + nr_entries++; > + } > + } > + > + boot_params->e820_entries = nr_entries; > + > + return EFI_SUCCESS; > + > +free_mem_map: > + low_free(_size, (unsigned long)mem_map); > +free_cmdline: > + if (options_size) > + low_free(options_size, hdr->cmd_line_ptr); > +fail: > + return status; > +} > + > +/* > + * On success we return a pointer to a boot_params structure, and NULL > + * on failure. > + */ > +struct boot_params *efi_main(void *handle, efi_system_table_t *_table) > +{ > + struct boot_params *boot_params; > + unsigned long start, nr_pages; > + struct desc_ptr *gdt, *idt; > + efi_loaded_image_t *image; > + struct setup_header *hdr; > + efi_status_t status; > + efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; > + struct desc_struct *desc; > + > + sys_table = _table; > + > + /* Check if we were booted by the EFI firmware */ > + if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) > + goto fail; > + > + status = efi_call_phys3(sys_table->boottime->handle_protocol, > + handle, &proto, (void *)&image); > + if (status != EFI_SUCCESS) > + goto fail; > + > + status = low_alloc(0x4000, 1, (unsigned long *)&boot_params); > + if (status != EFI_SUCCESS) > + goto fail; > + > + memset(boot_params, 0x0, 0x4000); > + > + /* Copy first two sectors to boot_params */ > + memcpy(boot_params, image->image_base, 1024); > + > + hdr = &boot_params->hdr; > + > + /* > + * The EFI firmware loader could have placed the kernel image > + * anywhere in memory, but the kernel has various restrictions > + * on the max physical address it can run at. Attempt to move > + * the kernel to boot_params.pref_address, or as low as > + * possible. > + */ > + start = hdr->pref_address; > + nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; > + > + status = efi_call_phys4(sys_table->boottime->allocate_pages, > + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, > + nr_pages, &start); > + if (status != EFI_SUCCESS) { > + status = low_alloc(hdr->init_size, hdr->kernel_alignment, > + &start); > + if (status != EFI_SUCCESS) > + goto fail; > + } > + > + hdr->code32_start = (__u32)start; > + hdr->pref_address = (__u64)(unsigned long)image->image_base; > + > + memcpy((void *)start, image->image_base, image->image_size); > + > + status = efi_call_phys3(sys_table->boottime->allocate_pool, > + EFI_LOADER_DATA, sizeof(*gdt), > + (void **)&gdt); > + if (status != EFI_SUCCESS) > + goto fail; > + > + gdt->size = 0x800; > + status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address); > + if (status != EFI_SUCCESS) > + goto fail; > + > + status = efi_call_phys3(sys_table->boottime->allocate_pool, > + EFI_LOADER_DATA, sizeof(*idt), > + (void **)&idt); > + if (status != EFI_SUCCESS) > + goto fail; > + > + idt->size = 0; > + idt->address = 0; > + > + status = make_boot_params(boot_params, image, handle); > + if (status != EFI_SUCCESS) > + goto fail; > + > + memset((char *)gdt->address, 0x0, gdt->size); > + desc = (struct desc_struct *)gdt->address; > + > + /* The first GDT is a dummy and the second is unused. */ > + desc += 2; > + > + desc->limit0 = 0xffff; > + desc->base0 = 0x0000; > + desc->base1 = 0x0000; > + desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; > + desc->s = DESC_TYPE_CODE_DATA; > + desc->dpl = 0; > + desc->p = 1; > + desc->limit = 0xf; > + desc->avl = 0; > + desc->l = 0; > + desc->d = SEG_OP_SIZE_32BIT; > + desc->g = SEG_GRANULARITY_4KB; > + desc->base2 = 0x00; > + > + desc++; > + desc->limit0 = 0xffff; > + desc->base0 = 0x0000; > + desc->base1 = 0x0000; > + desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; > + desc->s = DESC_TYPE_CODE_DATA; > + desc->dpl = 0; > + desc->p = 1; > + desc->limit = 0xf; > + desc->avl = 0; > + desc->l = 0; > + desc->d = SEG_OP_SIZE_32BIT; > + desc->g = SEG_GRANULARITY_4KB; > + desc->base2 = 0x00; > + > +#ifdef CONFIG_X86_64 > + /* Task segment value */ > + desc++; > + desc->limit0 = 0x0000; > + desc->base0 = 0x0000; > + desc->base1 = 0x0000; > + desc->type = SEG_TYPE_TSS; > + desc->s = 0; > + desc->dpl = 0; > + desc->p = 1; > + desc->limit = 0x0; > + desc->avl = 0; > + desc->l = 0; > + desc->d = 0; > + desc->g = SEG_GRANULARITY_4KB; > + desc->base2 = 0x00; > +#endif /* CONFIG_X86_64 */ > + > + asm volatile ("lidt %0" :: "m" (*idt)); > + asm volatile ("lgdt %0" :: "m" (*gdt)); > + > + asm volatile("cli"); > + > + return boot_params; > +fail: > + return NULL; > +} > diff --git a/arch/x86/boot/compressed/efi_stub_32.S b/arch/x86/boot/compressed/efi_stub_32.S > new file mode 100644 > index 0000000..5047cd9 > --- /dev/null > +++ b/arch/x86/boot/compressed/efi_stub_32.S > @@ -0,0 +1,87 @@ > +/* > + * EFI call stub for IA32. > + * > + * This stub allows us to make EFI calls in physical mode with interrupts > + * turned off. Note that this implementation is different from the one in > + * arch/x86/platform/efi/efi_stub_32.S because we're _already_ in physical > + * mode at this point. > + */ > + > +#include > +#include > + > +/* > + * efi_call_phys(void *, ...) is a function with variable parameters. > + * All the callers of this function assure that all the parameters are 4-bytes. > + */ > + > +/* > + * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save. > + * So we'd better save all of them at the beginning of this function and restore > + * at the end no matter how many we use, because we can not assure EFI runtime > + * service functions will comply with gcc calling convention, too. > + */ > + > +.text > +ENTRY(efi_call_phys) > + /* > + * 0. The function can only be called in Linux kernel. So CS has been > + * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found > + * the values of these registers are the same. And, the corresponding > + * GDT entries are identical. So I will do nothing about segment reg > + * and GDT, but change GDT base register in prelog and epilog. > + */ > + > + /* > + * 1. Because we haven't been relocated by this point we need to > + * use relative addressing. > + */ > + call 1f > +1: popl %edx > + subl $1b, %edx > + > + /* > + * 2. Now on the top of stack is the return > + * address in the caller of efi_call_phys(), then parameter 1, > + * parameter 2, ..., param n. To make things easy, we save the return > + * address of efi_call_phys in a global variable. > + */ > + popl %ecx > + movl %ecx, saved_return_addr(%edx) > + /* get the function pointer into ECX*/ > + popl %ecx > + movl %ecx, efi_rt_function_ptr(%edx) > + > + /* > + * 3. Call the physical function. > + */ > + call *%ecx > + > + /* > + * 4. Balance the stack. And because EAX contain the return value, > + * we'd better not clobber it. We need to calculate our address > + * again because %ecx and %edx are not preserved across EFI function > + * calls. > + */ > + call 1f > +1: popl %edx > + subl $1b, %edx > + > + movl efi_rt_function_ptr(%edx), %ecx > + pushl %ecx > + > + /* > + * 10. Push the saved return address onto the stack and return. > + */ > + movl saved_return_addr(%edx), %ecx > + pushl %ecx > + ret > +ENDPROC(efi_call_phys) > +.previous > + > +.data > +saved_return_addr: > + .long 0 > +efi_rt_function_ptr: > + .long 0 > + > diff --git a/arch/x86/boot/compressed/efi_stub_64.S b/arch/x86/boot/compressed/efi_stub_64.S > new file mode 100644 > index 0000000..cedc60d > --- /dev/null > +++ b/arch/x86/boot/compressed/efi_stub_64.S > @@ -0,0 +1 @@ > +#include "../../platform/efi/efi_stub_64.S" > diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S > index 67a655a..a055993 100644 > --- a/arch/x86/boot/compressed/head_32.S > +++ b/arch/x86/boot/compressed/head_32.S > @@ -32,6 +32,28 @@ > > __HEAD > ENTRY(startup_32) > +#ifdef CONFIG_EFI_STUB > + /* > + * We don't need the return address, so set up the stack so > + * efi_main() can find its arugments. > + */ > + add $0x4, %esp > + > + call efi_main > + cmpl $0, %eax > + je preferred_addr > + movl %eax, %esi > + call 1f > +1: > + popl %eax > + subl $1b, %eax > + subl BP_pref_address(%esi), %eax > + add BP_code32_start(%esi), %eax > + leal preferred_addr(%eax), %eax > + jmp *%eax > + > +preferred_addr: > +#endif > cld > /* > * Test KEEP_SEGMENTS flag to see if the bootloader is asking > diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S > index 35af09d..558d76c 100644 > --- a/arch/x86/boot/compressed/head_64.S > +++ b/arch/x86/boot/compressed/head_64.S > @@ -199,6 +199,26 @@ ENTRY(startup_64) > * an identity mapped page table being provied that maps our > * entire text+data+bss and hopefully all of memory. > */ > +#ifdef CONFIG_EFI_STUB > + pushq %rsi > + mov %rcx, %rdi > + mov %rdx, %rsi > + call efi_main > + popq %rsi > + cmpq $0,%rax > + je preferred_addr > + movq %rax,%rsi > + call 1f > +1: > + popq %rax > + subq $1b, %rax > + subq BP_pref_address(%rsi), %rax > + add BP_code32_start(%esi), %eax > + leaq preferred_addr(%rax), %rax > + jmp *%rax > + > +preferred_addr: > +#endif > > /* Setup data segments. */ > xorl %eax, %eax > diff --git a/arch/x86/boot/compressed/string.c b/arch/x86/boot/compressed/string.c > index 19b3e69..ffb9c5c 100644 > --- a/arch/x86/boot/compressed/string.c > +++ b/arch/x86/boot/compressed/string.c > @@ -1,2 +1,11 @@ > #include "misc.h" > + > +int memcmp(const void *s1, const void *s2, size_t len) > +{ > + u8 diff; > + asm("repe; cmpsb; setnz %0" > + : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len)); > + return diff; > +} > + > #include "../string.c" > diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S > index 93e689f..c4756f6 100644 > --- a/arch/x86/boot/header.S > +++ b/arch/x86/boot/header.S > @@ -45,6 +45,11 @@ SYSSEG = 0x1000 /* historical load address >> 4 */ > > .global bootsect_start > bootsect_start: > +#ifdef CONFIG_EFI_STUB > + # "MZ", MS-DOS header > + .byte 0x4d > + .byte 0x5a > +#endif > > # Normalize the start address > ljmp $BOOTSEG, $start2 > @@ -79,6 +84,14 @@ bs_die: > # invoke the BIOS reset code... > ljmp $0xf000,$0xfff0 > > +#ifdef CONFIG_EFI_STUB > + .org 0x3c > + # > + # Offset to the PE header. > + # > + .long pe_header > +#endif /* CONFIG_EFI_STUB */ > + > .section ".bsdata", "a" > bugger_off_msg: > .ascii "Direct booting from floppy is no longer supported.\r\n" > @@ -87,6 +100,141 @@ bugger_off_msg: > .ascii "Remove disk and press any key to reboot . . .\r\n" > .byte 0 > > +#ifdef CONFIG_EFI_STUB > +pe_header: > + .ascii "PE" > + .word 0 > + > +coff_header: > +#ifdef CONFIG_X86_32 > + .word 0x14c # i386 > +#else > + .word 0x8664 # x86-64 > +#endif > + .word 2 # nr_sections > + .long 0 # TimeDateStamp > + .long 0 # PointerToSymbolTable > + .long 1 # NumberOfSymbols > + .word section_table - optional_header # SizeOfOptionalHeader > +#ifdef CONFIG_X86_32 > + .word 0x306 # Characteristics. > + # IMAGE_FILE_32BIT_MACHINE | > + # IMAGE_FILE_DEBUG_STRIPPED | > + # IMAGE_FILE_EXECUTABLE_IMAGE | > + # IMAGE_FILE_LINE_NUMS_STRIPPED > +#else > + .word 0x206 # Characteristics > + # IMAGE_FILE_DEBUG_STRIPPED | > + # IMAGE_FILE_EXECUTABLE_IMAGE | > + # IMAGE_FILE_LINE_NUMS_STRIPPED > +#endif > + > +optional_header: > +#ifdef CONFIG_X86_32 > + .word 0x10b # PE32 format > +#else > + .word 0x20b # PE32+ format > +#endif > + .byte 0x02 # MajorLinkerVersion > + .byte 0x14 # MinorLinkerVersion > + > + # Filled in by build.c > + .long 0 # SizeOfCode > + > + .long 0 # SizeOfInitializedData > + .long 0 # SizeOfUninitializedData > + > + # Filled in by build.c > + .long 0x0000 # AddressOfEntryPoint > + > + .long 0x0000 # BaseOfCode > +#ifdef CONFIG_X86_32 > + .long 0 # data > +#endif > + > +extra_header_fields: > +#ifdef CONFIG_X86_32 > + .long 0 # ImageBase > +#else > + .quad 0 # ImageBase > +#endif > + .long 0x1000 # SectionAlignment > + .long 0x200 # FileAlignment > + .word 0 # MajorOperatingSystemVersion > + .word 0 # MinorOperatingSystemVersion > + .word 0 # MajorImageVersion > + .word 0 # MinorImageVersion > + .word 0 # MajorSubsystemVersion > + .word 0 # MinorSubsystemVersion > + .long 0 # Win32VersionValue > + > + # > + # The size of the bzImage is written in tools/build.c > + # > + .long 0 # SizeOfImage > + > + .long 0x200 # SizeOfHeaders > + .long 0 # CheckSum > + .word 0xa # Subsystem (EFI application) > + .word 0 # DllCharacteristics > +#ifdef CONFIG_X86_32 > + .long 0 # SizeOfStackReserve > + .long 0 # SizeOfStackCommit > + .long 0 # SizeOfHeapReserve > + .long 0 # SizeOfHeapCommit > +#else > + .quad 0 # SizeOfStackReserve > + .quad 0 # SizeOfStackCommit > + .quad 0 # SizeOfHeapReserve > + .quad 0 # SizeOfHeapCommit > +#endif > + .long 0 # LoaderFlags > + .long 0x1 # NumberOfRvaAndSizes > + > + .quad 0 # ExportTable > + .quad 0 # ImportTable > + .quad 0 # ResourceTable > + .quad 0 # ExceptionTable > + .quad 0 # CertificationTable > + .quad 0 # BaseRelocationTable > + > + # Section table > +section_table: > + .ascii ".text" > + .byte 0 > + .byte 0 > + .byte 0 > + .long 0 > + .long 0x0 # startup_{32,64} > + .long 0 # Size of initialized data > + # on disk > + .long 0x0 # startup_{32,64} > + .long 0 # PointerToRelocations > + .long 0 # PointerToLineNumbers > + .word 0 # NumberOfRelocations > + .word 0 # NumberOfLineNumbers > + .long 0x60500020 # Characteristics (section flags) > + > + # > + # The EFI application loader requires a relocation section > + # because EFI applications are relocatable and not having > + # this section seems to confuse it. But since we don't need > + # the loader to fixup any relocs for us just fill it with a > + # single dummy reloc. > + # > + .ascii ".reloc" > + .byte 0 > + .byte 0 > + .long reloc_end - reloc_start > + .long reloc_start > + .long reloc_end - reloc_start # SizeOfRawData > + .long reloc_start # PointerToRawData > + .long 0 # PointerToRelocations > + .long 0 # PointerToLineNumbers > + .word 0 # NumberOfRelocations > + .word 0 # NumberOfLineNumbers > + .long 0x42100040 # Characteristics (section flags) > +#endif /* CONFIG_EFI_STUB */ > > # Kernel attributes; used by setup. This is part 1 of the > # header, from the old boot sector. > @@ -318,3 +466,13 @@ die: > setup_corrupt: > .byte 7 > .string "No setup signature found...\n" > + > + .data > +dummy: .long 0 > + > + .section .reloc > +reloc_start: > + .long dummy - reloc_start > + .long 10 > + .word 0 > +reloc_end: > diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c > index 3cbc405..574dedf 100644 > --- a/arch/x86/boot/string.c > +++ b/arch/x86/boot/string.c > @@ -111,3 +111,38 @@ unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int bas > > return result; > } > + > +/** > + * strlen - Find the length of a string > + * @s: The string to be sized > + */ > +size_t strlen(const char *s) > +{ > + const char *sc; > + > + for (sc = s; *sc != '\0'; ++sc) > + /* nothing */; > + return sc - s; > +} > + > +/** > + * strstr - Find the first substring in a %NUL terminated string > + * @s1: The string to be searched > + * @s2: The string to search for > + */ > +char *strstr(const char *s1, const char *s2) > +{ > + size_t l1, l2; > + > + l2 = strlen(s2); > + if (!l2) > + return (char *)s1; > + l1 = strlen(s1); > + while (l1 >= l2) { > + l1--; > + if (!memcmp(s1, s2, l2)) > + return (char *)s1; > + s1++; > + } > + return NULL; > +} > diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c > index fdc60a0..4e9bd6b 100644 > --- a/arch/x86/boot/tools/build.c > +++ b/arch/x86/boot/tools/build.c > @@ -135,6 +135,9 @@ static void usage(void) > > int main(int argc, char ** argv) > { > +#ifdef CONFIG_EFI_STUB > + unsigned int file_sz, pe_header; > +#endif > unsigned int i, sz, setup_sectors; > int c; > u32 sys_size; > @@ -194,6 +197,42 @@ int main(int argc, char ** argv) > buf[0x1f6] = sys_size >> 16; > buf[0x1f7] = sys_size >> 24; > > +#ifdef CONFIG_EFI_STUB > + file_sz = sz + i + ((sys_size * 16) - sz); > + > + pe_header = *(unsigned int *)&buf[0x3c]; > + > + /* Size of code */ > + *(unsigned int *)&buf[pe_header + 0x1c] = file_sz; > + > + /* Size of image */ > + *(unsigned int *)&buf[pe_header + 0x50] = file_sz; > + > +#ifdef CONFIG_X86_32 > + /* Address of entry point */ > + *(unsigned int *)&buf[pe_header + 0x28] = i; > + > + /* .text size */ > + *(unsigned int *)&buf[pe_header + 0xb0] = file_sz; > + > + /* .text size of initialised data */ > + *(unsigned int *)&buf[pe_header + 0xb8] = file_sz; > +#else > + /* > + * Address of entry point. startup_32 is at the beginning and > + * the 64-bit entry point (startup_64) is always 512 bytes > + * after. > + */ > + *(unsigned int *)&buf[pe_header + 0x28] = i + 512; > + > + /* .text size */ > + *(unsigned int *)&buf[pe_header + 0xc0] = file_sz; > + > + /* .text size of initialised data */ > + *(unsigned int *)&buf[pe_header + 0xc8] = file_sz; > +#endif /* CONFIG_X86_32 */ > +#endif /* CONFIG_EFI_STUB */ > + > crc = partial_crc32(buf, i, crc); > if (fwrite(buf, 1, i, stdout) != i) > die("Writing setup failed"); > diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c > index 4f13faf..68de2dc 100644 > --- a/arch/x86/kernel/asm-offsets.c > +++ b/arch/x86/kernel/asm-offsets.c > @@ -67,4 +67,6 @@ void common(void) { > OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch); > OFFSET(BP_version, boot_params, hdr.version); > OFFSET(BP_kernel_alignment, boot_params, hdr.kernel_alignment); > + OFFSET(BP_pref_address, boot_params, hdr.pref_address); > + OFFSET(BP_code32_start, boot_params, hdr.code32_start); > }