diff options
| -rw-r--r-- | arch/x86/Kconfig | 7 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/Makefile | 10 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/eboot.c | 1022 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/eboot.h | 61 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/efi_stub_32.S | 86 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/efi_stub_64.S | 1 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/head_32.S | 22 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/head_64.S | 20 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/string.c | 9 | ||||
| -rw-r--r-- | arch/x86/boot/header.S | 158 | ||||
| -rw-r--r-- | arch/x86/boot/string.c | 35 | ||||
| -rw-r--r-- | arch/x86/boot/tools/build.c | 39 | ||||
| -rw-r--r-- | arch/x86/include/asm/bootparam.h | 2 | ||||
| -rw-r--r-- | arch/x86/include/asm/efi.h | 4 | ||||
| -rw-r--r-- | arch/x86/kernel/asm-offsets.c | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/setup.c | 7 | ||||
| -rw-r--r-- | include/linux/efi.h | 136 | 
17 files changed, 1613 insertions, 8 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1a31254ceb83..2513da0e6b67 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1489,6 +1489,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 09664efb9cee..b123b9a8f5b3 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 000000000000..fec216f4fbc3 --- /dev/null +++ b/arch/x86/boot/compressed/eboot.c @@ -0,0 +1,1022 @@ +/* ----------------------------------------------------------------------- + * + *   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 <linux/efi.h> +#include <asm/efi.h> +#include <asm/setup.h> +#include <asm/desc.h> + +#include "eboot.h" + +static efi_system_table_t *sys_table; + +static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, +			      unsigned long *desc_size) +{ +	efi_memory_desc_t *m = NULL; +	efi_status_t status; +	unsigned long key; +	u32 desc_version; + +	*map_size = sizeof(*m) * 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(*m); +	status = efi_call_phys3(sys_table->boottime->allocate_pool, +				EFI_LOADER_DATA, *map_size, (void **)&m); +	if (status != EFI_SUCCESS) +		goto fail; + +	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, +				m, &key, desc_size, &desc_version); +	if (status == EFI_BUFFER_TOO_SMALL) { +		efi_call_phys1(sys_table->boottime->free_pool, m); +		goto again; +	} + +	if (status != EFI_SUCCESS) +		efi_call_phys1(sys_table->boottime->free_pool, m); + +fail: +	*map = m; +	return status; +} + +/* + * Allocate at the highest possible address that is not above 'max'. + */ +static efi_status_t high_alloc(unsigned long size, unsigned long align, +			      unsigned long *addr, unsigned long max) +{ +	unsigned long map_size, desc_size; +	efi_memory_desc_t *map; +	efi_status_t status; +	unsigned long nr_pages; +	u64 max_addr = 0; +	int i; + +	status = __get_map(&map, &map_size, &desc_size); +	if (status != EFI_SUCCESS) +		goto fail; + +	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +again: +	for (i = 0; i < map_size / desc_size; i++) { +		efi_memory_desc_t *desc; +		unsigned long m = (unsigned long)map; +		u64 start, end; + +		desc = (efi_memory_desc_t *)(m + (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); + +		if ((start + size) > end || (start + size) > max) +			continue; + +		if (end - size > max) +			end = max; + +		if (round_down(end - size, align) < start) +			continue; + +		start = round_down(end - size, align); + +		/* +		 * Don't allocate at 0x0. It will confuse code that +		 * checks pointers against NULL. +		 */ +		if (start == 0x0) +			continue; + +		if (start > max_addr) +			max_addr = start; +	} + +	if (!max_addr) +		status = EFI_NOT_FOUND; +	else { +		status = efi_call_phys4(sys_table->boottime->allocate_pages, +					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, +					nr_pages, &max_addr); +		if (status != EFI_SUCCESS) { +			max = max_addr; +			max_addr = 0; +			goto again; +		} + +		*addr = max_addr; +	} + +free_pool: +	efi_call_phys1(sys_table->boottime->free_pool, map); + +fail: +	return status; +} + +/* + * Allocate at the lowest possible address. + */ +static efi_status_t low_alloc(unsigned long size, unsigned long align, +			      unsigned long *addr) +{ +	unsigned long map_size, desc_size; +	efi_memory_desc_t *map; +	efi_status_t status; +	unsigned long nr_pages; +	int i; + +	status = __get_map(&map, &map_size, &desc_size); +	if (status != EFI_SUCCESS) +		goto fail; + +	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +	for (i = 0; i < map_size / desc_size; i++) { +		efi_memory_desc_t *desc; +		unsigned long m = (unsigned long)map; +		u64 start, end; + +		desc = (efi_memory_desc_t *)(m + (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; + +		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) +{ +	struct efi_graphics_output_protocol *gop, *first_gop; +	struct efi_pixel_bitmask 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++) { +		struct efi_graphics_output_mode_info *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) +{ +	struct efi_uga_draw_protocol *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 = high_alloc(initrd_total, 0x1000, +				   &initrd_addr, hdr->initrd_addr_max); +		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; +			while (size) { +				u64 chunksize; +				if (size > EFI_READ_CHUNK_SIZE) +					chunksize = EFI_READ_CHUNK_SIZE; +				else +					chunksize = size; +				status = efi_call_phys3(fh->read, +							initrds[j].handle, +							&chunksize, addr); +				if (status != EFI_SUCCESS) +					goto free_initrd_total; +				addr += chunksize; +				size -= chunksize; +			} + +			efi_call_phys1(fh->close, initrds[j].handle); +		} + +	} + +	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; + +			s1 = (u8 *)(unsigned long)cmdline; +			s2 = (u16 *)options; + +			for (i = 0; i < options_size - 1; i++) +				*s1++ = *s2++; + +			*s1 = '\0'; +		} +	} + +	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; +		unsigned long m = (unsigned long)mem_map; + +		d = (efi_memory_desc_t *)(m + (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/eboot.h b/arch/x86/boot/compressed/eboot.h new file mode 100644 index 000000000000..39251663e65b --- /dev/null +++ b/arch/x86/boot/compressed/eboot.h @@ -0,0 +1,61 @@ +#ifndef BOOT_COMPRESSED_EBOOT_H +#define BOOT_COMPRESSED_EBOOT_H + +#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 EFI_READ_CHUNK_SIZE	(1024 * 1024) + +#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 + +struct efi_pixel_bitmask { +	u32 red_mask; +	u32 green_mask; +	u32 blue_mask; +	u32 reserved_mask; +}; + +struct efi_graphics_output_mode_info { +	u32 version; +	u32 horizontal_resolution; +	u32 vertical_resolution; +	int pixel_format; +	struct efi_pixel_bitmask pixel_information; +	u32 pixels_per_scan_line; +} __packed; + +struct efi_graphics_output_protocol_mode { +	u32 max_mode; +	u32 mode; +	unsigned long info; +	unsigned long size_of_info; +	u64 frame_buffer_base; +	unsigned long frame_buffer_size; +} __packed; + +struct efi_graphics_output_protocol { +	void *query_mode; +	unsigned long set_mode; +	unsigned long blt; +	struct efi_graphics_output_protocol_mode *mode; +}; + +struct efi_uga_draw_protocol { +	void *get_mode; +	void *set_mode; +	void *blt; +}; + +#endif /* BOOT_COMPRESSED_EBOOT_H */ 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 000000000000..a53440e81d52 --- /dev/null +++ b/arch/x86/boot/compressed/efi_stub_32.S @@ -0,0 +1,86 @@ +/* + * 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 <linux/linkage.h> +#include <asm/page_types.h> + +/* + * 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 000000000000..cedc60de86eb --- /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 67a655a39ce4..a0559930a180 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 35af09d13dc1..558d76ce23bc 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 19b3e693cd72..ffb9c5c9d748 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 bdb4d458ec8c..f1bbeeb09148 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 3cbc4058dd26..574dedfe2890 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 fdc60a0b3c20..4e9bd6bcafa6 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/include/asm/bootparam.h b/arch/x86/include/asm/bootparam.h index e020d88ec02d..2f90c51cc49d 100644 --- a/arch/x86/include/asm/bootparam.h +++ b/arch/x86/include/asm/bootparam.h @@ -64,6 +64,8 @@ struct setup_header {  	__u32	payload_offset;  	__u32	payload_length;  	__u64	setup_data; +	__u64	pref_address; +	__u32	init_size;  } __attribute__((packed));  struct sys_desc_table { diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 7093e4a6a0bc..844f735fd63a 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -3,6 +3,8 @@  #ifdef CONFIG_X86_32 +#define EFI_LOADER_SIGNATURE	"EL32" +  extern unsigned long asmlinkage efi_call_phys(void *, ...);  #define efi_call_phys0(f)		efi_call_phys(f) @@ -37,6 +39,8 @@ extern unsigned long asmlinkage efi_call_phys(void *, ...);  #else /* !CONFIG_X86_32 */ +#define EFI_LOADER_SIGNATURE	"EL64" +  extern u64 efi_call0(void *fp);  extern u64 efi_call1(void *fp, u64 arg1);  extern u64 efi_call2(void *fp, u64 arg1, u64 arg2); diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c index 4f13fafc5264..68de2dc962ec 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);  } diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index d05444ac2aea..d7d5099fe874 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -749,12 +749,7 @@ void __init setup_arch(char **cmdline_p)  #endif  #ifdef CONFIG_EFI  	if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature, -#ifdef CONFIG_X86_32 -		     "EL32", -#else -		     "EL64", -#endif -	 4)) { +		     EFI_LOADER_SIGNATURE, 4)) {  		efi_enabled = 1;  		efi_memblock_x86_reserve_range();  	} diff --git a/include/linux/efi.h b/include/linux/efi.h index 2362a0bc7f0d..37c300712e02 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -109,6 +109,14 @@ typedef struct {  	u32 imagesize;  } efi_capsule_header_t; +/* + * Allocation types for calls to boottime->allocate_pages. + */ +#define EFI_ALLOCATE_ANY_PAGES		0 +#define EFI_ALLOCATE_MAX_ADDRESS	1 +#define EFI_ALLOCATE_ADDRESS		2 +#define EFI_MAX_ALLOCATE_TYPE		3 +  typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg);  /* @@ -139,6 +147,57 @@ typedef struct {  } efi_time_cap_t;  /* + * EFI Boot Services table + */ +typedef struct { +	efi_table_hdr_t hdr; +	void *raise_tpl; +	void *restore_tpl; +	void *allocate_pages; +	void *free_pages; +	void *get_memory_map; +	void *allocate_pool; +	void *free_pool; +	void *create_event; +	void *set_timer; +	void *wait_for_event; +	void *signal_event; +	void *close_event; +	void *check_event; +	void *install_protocol_interface; +	void *reinstall_protocol_interface; +	void *uninstall_protocol_interface; +	void *handle_protocol; +	void *__reserved; +	void *register_protocol_notify; +	void *locate_handle; +	void *locate_device_path; +	void *install_configuration_table; +	void *load_image; +	void *start_image; +	void *exit; +	void *unload_image; +	void *exit_boot_services; +	void *get_next_monotonic_count; +	void *stall; +	void *set_watchdog_timer; +	void *connect_controller; +	void *disconnect_controller; +	void *open_protocol; +	void *close_protocol; +	void *open_protocol_information; +	void *protocols_per_handle; +	void *locate_handle_buffer; +	void *locate_protocol; +	void *install_multiple_protocol_interfaces; +	void *uninstall_multiple_protocol_interfaces; +	void *calculate_crc32; +	void *copy_mem; +	void *set_mem; +	void *create_event_ex; +} efi_boot_services_t; + +/*   * Types and defines for EFI ResetSystem   */  #define EFI_RESET_COLD 0 @@ -236,6 +295,24 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,  #define LINUX_EFI_CRASH_GUID \      EFI_GUID(  0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 ) +#define LOADED_IMAGE_PROTOCOL_GUID \ +    EFI_GUID(  0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) + +#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \ +    EFI_GUID(  0x9042a9de, 0x23dc, 0x4a38, 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a ) + +#define EFI_UGA_PROTOCOL_GUID \ +    EFI_GUID(  0x982c298b, 0xf4fa, 0x41cb, 0xb8, 0x38, 0x77, 0xaa, 0x68, 0x8f, 0xb8, 0x39 ) + +#define EFI_PCI_IO_PROTOCOL_GUID \ +    EFI_GUID(  0x4cf5b200, 0x68b8, 0x4ca5, 0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a ) + +#define EFI_FILE_INFO_ID \ +    EFI_GUID(  0x9576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) + +#define EFI_FILE_SYSTEM_GUID \ +    EFI_GUID(  0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) +  typedef struct {  	efi_guid_t guid;  	unsigned long table; @@ -261,7 +338,7 @@ typedef struct {  	unsigned long stderr_handle;  	unsigned long stderr;  	efi_runtime_services_t *runtime; -	unsigned long boottime; +	efi_boot_services_t *boottime;  	unsigned long nr_tables;  	unsigned long tables;  } efi_system_table_t; @@ -275,6 +352,56 @@ struct efi_memory_map {  	unsigned long desc_size;  }; +typedef struct { +	u32 revision; +	void *parent_handle; +	efi_system_table_t *system_table; +	void *device_handle; +	void *file_path; +	void *reserved; +	u32 load_options_size; +	void *load_options; +	void *image_base; +	__aligned_u64 image_size; +	unsigned int image_code_type; +	unsigned int image_data_type; +	unsigned long unload; +} efi_loaded_image_t; + +typedef struct { +	u64 revision; +	void *open_volume; +} efi_file_io_interface_t; + +typedef struct { +	u64 size; +	u64 file_size; +	u64 phys_size; +	efi_time_t create_time; +	efi_time_t last_access_time; +	efi_time_t modification_time; +	__aligned_u64 attribute; +	efi_char16_t filename[1]; +} efi_file_info_t; + +typedef struct { +	u64 revision; +	void *open; +	void *close; +	void *delete; +	void *read; +	void *write; +	void *get_position; +	void *set_position; +	void *get_info; +	void *set_info; +	void *flush; +} efi_file_handle_t; + +#define EFI_FILE_MODE_READ	0x0000000000000001 +#define EFI_FILE_MODE_WRITE	0x0000000000000002 +#define EFI_FILE_MODE_CREATE	0x8000000000000000 +  #define EFI_INVALID_TABLE_ADDR		(~0UL)  /* @@ -385,6 +512,13 @@ extern int __init efi_setup_pcdp_console(char *);  #define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004  /* + * The type of search to perform when calling boottime->locate_handle + */ +#define EFI_LOCATE_ALL_HANDLES			0 +#define EFI_LOCATE_BY_REGISTER_NOTIFY		1 +#define EFI_LOCATE_BY_PROTOCOL			2 + +/*   * EFI Device Path information   */  #define EFI_DEV_HW			0x01  | 
