diff options
Diffstat (limited to 'arch/x86/boot/compressed')
| -rw-r--r-- | arch/x86/boot/compressed/Makefile | 32 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/aslr.c | 324 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/cmdline.c | 16 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/cpuflags.c | 12 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/early_serial_console.c | 4 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/eboot.c | 1474 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/eboot.h | 122 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/efi_stub_32.S | 86 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/efi_stub_64.S | 30 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/head_32.S | 115 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/head_64.S | 191 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/misc.c | 187 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/misc.h | 53 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/mkpiggy.c | 34 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/relocs.c | 682 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/string.c | 41 | 
16 files changed, 2556 insertions, 847 deletions
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 0c229551eea..0fcd9133790 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -4,7 +4,8 @@  # create a compressed vmlinux image from the original vmlinux  # -targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma vmlinux.bin.lzo head_$(BITS).o misc.o string.o cmdline.o early_serial_console.o piggy.o +targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \ +	vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4  KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2  KBUILD_CFLAGS += -fno-strict-aliasing -fPIC @@ -12,6 +13,7 @@ KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING  cflags-$(CONFIG_X86_32) := -march=i386  cflags-$(CONFIG_X86_64) := -mcmodel=small  KBUILD_CFLAGS += $(cflags-y) +KBUILD_CFLAGS += -mno-mmx -mno-sse  KBUILD_CFLAGS += $(call cc-option,-ffreestanding)  KBUILD_CFLAGS += $(call cc-option,-fno-stack-protector) @@ -22,8 +24,19 @@ LDFLAGS := -m elf_$(UTS_MACHINE)  LDFLAGS_vmlinux := -T  hostprogs-y	:= mkpiggy +HOST_EXTRACFLAGS += -I$(srctree)/tools/include -$(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 $(obj)/cpuflags.o $(obj)/aslr.o + +$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone + +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)  	@: @@ -31,13 +44,12 @@ OBJCOPYFLAGS_vmlinux.bin :=  -R .comment -S  $(obj)/vmlinux.bin: vmlinux FORCE  	$(call if_changed,objcopy) +targets += $(patsubst $(obj)/%,%,$(VMLINUX_OBJS)) vmlinux.bin.all vmlinux.relocs -targets += vmlinux.bin.all vmlinux.relocs relocs -hostprogs-$(CONFIG_X86_NEED_RELOCS) += relocs - +CMD_RELOCS = arch/x86/tools/relocs  quiet_cmd_relocs = RELOCS  $@ -      cmd_relocs = $(obj)/relocs $< > $@;$(obj)/relocs --abs-relocs $< -$(obj)/vmlinux.relocs: vmlinux $(obj)/relocs FORCE +      cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $< +$(obj)/vmlinux.relocs: vmlinux FORCE  	$(call if_changed,relocs)  vmlinux.bin.all-y := $(obj)/vmlinux.bin @@ -49,13 +61,19 @@ $(obj)/vmlinux.bin.bz2: $(vmlinux.bin.all-y) FORCE  	$(call if_changed,bzip2)  $(obj)/vmlinux.bin.lzma: $(vmlinux.bin.all-y) FORCE  	$(call if_changed,lzma) +$(obj)/vmlinux.bin.xz: $(vmlinux.bin.all-y) FORCE +	$(call if_changed,xzkern)  $(obj)/vmlinux.bin.lzo: $(vmlinux.bin.all-y) FORCE  	$(call if_changed,lzo) +$(obj)/vmlinux.bin.lz4: $(vmlinux.bin.all-y) FORCE +	$(call if_changed,lz4)  suffix-$(CONFIG_KERNEL_GZIP)	:= gz  suffix-$(CONFIG_KERNEL_BZIP2)	:= bz2  suffix-$(CONFIG_KERNEL_LZMA)	:= lzma +suffix-$(CONFIG_KERNEL_XZ)	:= xz  suffix-$(CONFIG_KERNEL_LZO) 	:= lzo +suffix-$(CONFIG_KERNEL_LZ4) 	:= lz4  quiet_cmd_mkpiggy = MKPIGGY $@        cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false ) diff --git a/arch/x86/boot/compressed/aslr.c b/arch/x86/boot/compressed/aslr.c new file mode 100644 index 00000000000..fc6091abedb --- /dev/null +++ b/arch/x86/boot/compressed/aslr.c @@ -0,0 +1,324 @@ +#include "misc.h" + +#ifdef CONFIG_RANDOMIZE_BASE +#include <asm/msr.h> +#include <asm/archrandom.h> +#include <asm/e820.h> + +#include <generated/compile.h> +#include <linux/module.h> +#include <linux/uts.h> +#include <linux/utsname.h> +#include <generated/utsrelease.h> + +/* Simplified build-specific string for starting entropy. */ +static const char build_str[] = UTS_RELEASE " (" LINUX_COMPILE_BY "@" +		LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION; + +#define I8254_PORT_CONTROL	0x43 +#define I8254_PORT_COUNTER0	0x40 +#define I8254_CMD_READBACK	0xC0 +#define I8254_SELECT_COUNTER0	0x02 +#define I8254_STATUS_NOTREADY	0x40 +static inline u16 i8254(void) +{ +	u16 status, timer; + +	do { +		outb(I8254_PORT_CONTROL, +		     I8254_CMD_READBACK | I8254_SELECT_COUNTER0); +		status = inb(I8254_PORT_COUNTER0); +		timer  = inb(I8254_PORT_COUNTER0); +		timer |= inb(I8254_PORT_COUNTER0) << 8; +	} while (status & I8254_STATUS_NOTREADY); + +	return timer; +} + +static unsigned long rotate_xor(unsigned long hash, const void *area, +				size_t size) +{ +	size_t i; +	unsigned long *ptr = (unsigned long *)area; + +	for (i = 0; i < size / sizeof(hash); i++) { +		/* Rotate by odd number of bits and XOR. */ +		hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7); +		hash ^= ptr[i]; +	} + +	return hash; +} + +/* Attempt to create a simple but unpredictable starting entropy. */ +static unsigned long get_random_boot(void) +{ +	unsigned long hash = 0; + +	hash = rotate_xor(hash, build_str, sizeof(build_str)); +	hash = rotate_xor(hash, real_mode, sizeof(*real_mode)); + +	return hash; +} + +static unsigned long get_random_long(void) +{ +#ifdef CONFIG_X86_64 +	const unsigned long mix_const = 0x5d6008cbf3848dd3UL; +#else +	const unsigned long mix_const = 0x3f39e593UL; +#endif +	unsigned long raw, random = get_random_boot(); +	bool use_i8254 = true; + +	debug_putstr("KASLR using"); + +	if (has_cpuflag(X86_FEATURE_RDRAND)) { +		debug_putstr(" RDRAND"); +		if (rdrand_long(&raw)) { +			random ^= raw; +			use_i8254 = false; +		} +	} + +	if (has_cpuflag(X86_FEATURE_TSC)) { +		debug_putstr(" RDTSC"); +		rdtscll(raw); + +		random ^= raw; +		use_i8254 = false; +	} + +	if (use_i8254) { +		debug_putstr(" i8254"); +		random ^= i8254(); +	} + +	/* Circular multiply for better bit diffusion */ +	asm("mul %3" +	    : "=a" (random), "=d" (raw) +	    : "a" (random), "rm" (mix_const)); +	random += raw; + +	debug_putstr("...\n"); + +	return random; +} + +struct mem_vector { +	unsigned long start; +	unsigned long size; +}; + +#define MEM_AVOID_MAX 5 +static struct mem_vector mem_avoid[MEM_AVOID_MAX]; + +static bool mem_contains(struct mem_vector *region, struct mem_vector *item) +{ +	/* Item at least partially before region. */ +	if (item->start < region->start) +		return false; +	/* Item at least partially after region. */ +	if (item->start + item->size > region->start + region->size) +		return false; +	return true; +} + +static bool mem_overlaps(struct mem_vector *one, struct mem_vector *two) +{ +	/* Item one is entirely before item two. */ +	if (one->start + one->size <= two->start) +		return false; +	/* Item one is entirely after item two. */ +	if (one->start >= two->start + two->size) +		return false; +	return true; +} + +static void mem_avoid_init(unsigned long input, unsigned long input_size, +			   unsigned long output, unsigned long output_size) +{ +	u64 initrd_start, initrd_size; +	u64 cmd_line, cmd_line_size; +	unsigned long unsafe, unsafe_len; +	char *ptr; + +	/* +	 * Avoid the region that is unsafe to overlap during +	 * decompression (see calculations at top of misc.c). +	 */ +	unsafe_len = (output_size >> 12) + 32768 + 18; +	unsafe = (unsigned long)input + input_size - unsafe_len; +	mem_avoid[0].start = unsafe; +	mem_avoid[0].size = unsafe_len; + +	/* Avoid initrd. */ +	initrd_start  = (u64)real_mode->ext_ramdisk_image << 32; +	initrd_start |= real_mode->hdr.ramdisk_image; +	initrd_size  = (u64)real_mode->ext_ramdisk_size << 32; +	initrd_size |= real_mode->hdr.ramdisk_size; +	mem_avoid[1].start = initrd_start; +	mem_avoid[1].size = initrd_size; + +	/* Avoid kernel command line. */ +	cmd_line  = (u64)real_mode->ext_cmd_line_ptr << 32; +	cmd_line |= real_mode->hdr.cmd_line_ptr; +	/* Calculate size of cmd_line. */ +	ptr = (char *)(unsigned long)cmd_line; +	for (cmd_line_size = 0; ptr[cmd_line_size++]; ) +		; +	mem_avoid[2].start = cmd_line; +	mem_avoid[2].size = cmd_line_size; + +	/* Avoid heap memory. */ +	mem_avoid[3].start = (unsigned long)free_mem_ptr; +	mem_avoid[3].size = BOOT_HEAP_SIZE; + +	/* Avoid stack memory. */ +	mem_avoid[4].start = (unsigned long)free_mem_end_ptr; +	mem_avoid[4].size = BOOT_STACK_SIZE; +} + +/* Does this memory vector overlap a known avoided area? */ +static bool mem_avoid_overlap(struct mem_vector *img) +{ +	int i; + +	for (i = 0; i < MEM_AVOID_MAX; i++) { +		if (mem_overlaps(img, &mem_avoid[i])) +			return true; +	} + +	return false; +} + +static unsigned long slots[CONFIG_RANDOMIZE_BASE_MAX_OFFSET / +			   CONFIG_PHYSICAL_ALIGN]; +static unsigned long slot_max; + +static void slots_append(unsigned long addr) +{ +	/* Overflowing the slots list should be impossible. */ +	if (slot_max >= CONFIG_RANDOMIZE_BASE_MAX_OFFSET / +			CONFIG_PHYSICAL_ALIGN) +		return; + +	slots[slot_max++] = addr; +} + +static unsigned long slots_fetch_random(void) +{ +	/* Handle case of no slots stored. */ +	if (slot_max == 0) +		return 0; + +	return slots[get_random_long() % slot_max]; +} + +static void process_e820_entry(struct e820entry *entry, +			       unsigned long minimum, +			       unsigned long image_size) +{ +	struct mem_vector region, img; + +	/* Skip non-RAM entries. */ +	if (entry->type != E820_RAM) +		return; + +	/* Ignore entries entirely above our maximum. */ +	if (entry->addr >= CONFIG_RANDOMIZE_BASE_MAX_OFFSET) +		return; + +	/* Ignore entries entirely below our minimum. */ +	if (entry->addr + entry->size < minimum) +		return; + +	region.start = entry->addr; +	region.size = entry->size; + +	/* Potentially raise address to minimum location. */ +	if (region.start < minimum) +		region.start = minimum; + +	/* Potentially raise address to meet alignment requirements. */ +	region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN); + +	/* Did we raise the address above the bounds of this e820 region? */ +	if (region.start > entry->addr + entry->size) +		return; + +	/* Reduce size by any delta from the original address. */ +	region.size -= region.start - entry->addr; + +	/* Reduce maximum size to fit end of image within maximum limit. */ +	if (region.start + region.size > CONFIG_RANDOMIZE_BASE_MAX_OFFSET) +		region.size = CONFIG_RANDOMIZE_BASE_MAX_OFFSET - region.start; + +	/* Walk each aligned slot and check for avoided areas. */ +	for (img.start = region.start, img.size = image_size ; +	     mem_contains(®ion, &img) ; +	     img.start += CONFIG_PHYSICAL_ALIGN) { +		if (mem_avoid_overlap(&img)) +			continue; +		slots_append(img.start); +	} +} + +static unsigned long find_random_addr(unsigned long minimum, +				      unsigned long size) +{ +	int i; +	unsigned long addr; + +	/* Make sure minimum is aligned. */ +	minimum = ALIGN(minimum, CONFIG_PHYSICAL_ALIGN); + +	/* Verify potential e820 positions, appending to slots list. */ +	for (i = 0; i < real_mode->e820_entries; i++) { +		process_e820_entry(&real_mode->e820_map[i], minimum, size); +	} + +	return slots_fetch_random(); +} + +unsigned char *choose_kernel_location(unsigned char *input, +				      unsigned long input_size, +				      unsigned char *output, +				      unsigned long output_size) +{ +	unsigned long choice = (unsigned long)output; +	unsigned long random; + +#ifdef CONFIG_HIBERNATION +	if (!cmdline_find_option_bool("kaslr")) { +		debug_putstr("KASLR disabled by default...\n"); +		goto out; +	} +#else +	if (cmdline_find_option_bool("nokaslr")) { +		debug_putstr("KASLR disabled by cmdline...\n"); +		goto out; +	} +#endif + +	/* Record the various known unsafe memory ranges. */ +	mem_avoid_init((unsigned long)input, input_size, +		       (unsigned long)output, output_size); + +	/* Walk e820 and find a random address. */ +	random = find_random_addr(choice, output_size); +	if (!random) { +		debug_putstr("KASLR could not find suitable E820 region...\n"); +		goto out; +	} + +	/* Always enforce the minimum. */ +	if (random < choice) +		goto out; + +	choice = random; +out: +	return (unsigned char *)choice; +} + +#endif /* CONFIG_RANDOMIZE_BASE */ diff --git a/arch/x86/boot/compressed/cmdline.c b/arch/x86/boot/compressed/cmdline.c index cb62f786990..b68e3033e6b 100644 --- a/arch/x86/boot/compressed/cmdline.c +++ b/arch/x86/boot/compressed/cmdline.c @@ -1,5 +1,7 @@  #include "misc.h" +#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE +  static unsigned long fs;  static inline void set_fs(unsigned long seg)  { @@ -11,11 +13,21 @@ static inline char rdfs8(addr_t addr)  	return *((char *)(fs + addr));  }  #include "../cmdline.c" +static unsigned long get_cmd_line_ptr(void) +{ +	unsigned long cmd_line_ptr = real_mode->hdr.cmd_line_ptr; + +	cmd_line_ptr |= (u64)real_mode->ext_cmd_line_ptr << 32; + +	return cmd_line_ptr; +}  int cmdline_find_option(const char *option, char *buffer, int bufsize)  { -	return __cmdline_find_option(real_mode->hdr.cmd_line_ptr, option, buffer, bufsize); +	return __cmdline_find_option(get_cmd_line_ptr(), option, buffer, bufsize);  }  int cmdline_find_option_bool(const char *option)  { -	return __cmdline_find_option_bool(real_mode->hdr.cmd_line_ptr, option); +	return __cmdline_find_option_bool(get_cmd_line_ptr(), option);  } + +#endif diff --git a/arch/x86/boot/compressed/cpuflags.c b/arch/x86/boot/compressed/cpuflags.c new file mode 100644 index 00000000000..aa313466118 --- /dev/null +++ b/arch/x86/boot/compressed/cpuflags.c @@ -0,0 +1,12 @@ +#ifdef CONFIG_RANDOMIZE_BASE + +#include "../cpuflags.c" + +bool has_cpuflag(int flag) +{ +	get_cpuflags(); + +	return test_bit(flag, cpu.flags); +} + +#endif diff --git a/arch/x86/boot/compressed/early_serial_console.c b/arch/x86/boot/compressed/early_serial_console.c index 261e81fb958..d3d003cb548 100644 --- a/arch/x86/boot/compressed/early_serial_console.c +++ b/arch/x86/boot/compressed/early_serial_console.c @@ -1,5 +1,9 @@  #include "misc.h" +#ifdef CONFIG_EARLY_PRINTK +  int early_serial_base;  #include "../early_serial_console.c" + +#endif diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c new file mode 100644 index 00000000000..0331d765c2b --- /dev/null +++ b/arch/x86/boot/compressed/eboot.c @@ -0,0 +1,1474 @@ +/* ----------------------------------------------------------------------- + * + *   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 <linux/pci.h> +#include <asm/efi.h> +#include <asm/setup.h> +#include <asm/desc.h> + +#undef memcpy			/* Use memcpy from misc.c */ + +#include "eboot.h" + +static efi_system_table_t *sys_table; + +static struct efi_config *efi_early; + +#define efi_call_early(f, ...)						\ +	efi_early->call(efi_early->f, __VA_ARGS__); + +#define BOOT_SERVICES(bits)						\ +static void setup_boot_services##bits(struct efi_config *c)		\ +{									\ +	efi_system_table_##bits##_t *table;				\ +	efi_boot_services_##bits##_t *bt;				\ +									\ +	table = (typeof(table))sys_table;				\ +									\ +	c->text_output = table->con_out;				\ +									\ +	bt = (typeof(bt))(unsigned long)(table->boottime);		\ +									\ +	c->allocate_pool = bt->allocate_pool;				\ +	c->allocate_pages = bt->allocate_pages;				\ +	c->get_memory_map = bt->get_memory_map;				\ +	c->free_pool = bt->free_pool;					\ +	c->free_pages = bt->free_pages;					\ +	c->locate_handle = bt->locate_handle;				\ +	c->handle_protocol = bt->handle_protocol;			\ +	c->exit_boot_services = bt->exit_boot_services;			\ +} +BOOT_SERVICES(32); +BOOT_SERVICES(64); + +static void efi_printk(efi_system_table_t *, char *); +static void efi_char16_printk(efi_system_table_t *, efi_char16_t *); + +static efi_status_t +__file_size32(void *__fh, efi_char16_t *filename_16, +	      void **handle, u64 *file_sz) +{ +	efi_file_handle_32_t *h, *fh = __fh; +	efi_file_info_t *info; +	efi_status_t status; +	efi_guid_t info_guid = EFI_FILE_INFO_ID; +	u32 info_sz; + +	status = efi_early->call((unsigned long)fh->open, fh, &h, filename_16, +				 EFI_FILE_MODE_READ, (u64)0); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to open file: "); +		efi_char16_printk(sys_table, filename_16); +		efi_printk(sys_table, "\n"); +		return status; +	} + +	*handle = h; + +	info_sz = 0; +	status = efi_early->call((unsigned long)h->get_info, h, &info_guid, +				 &info_sz, NULL); +	if (status != EFI_BUFFER_TOO_SMALL) { +		efi_printk(sys_table, "Failed to get file info size\n"); +		return status; +	} + +grow: +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, +				info_sz, (void **)&info); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to alloc mem for file info\n"); +		return status; +	} + +	status = efi_early->call((unsigned long)h->get_info, h, &info_guid, +				 &info_sz, info); +	if (status == EFI_BUFFER_TOO_SMALL) { +		efi_call_early(free_pool, info); +		goto grow; +	} + +	*file_sz = info->file_size; +	efi_call_early(free_pool, info); + +	if (status != EFI_SUCCESS) +		efi_printk(sys_table, "Failed to get initrd info\n"); + +	return status; +} + +static efi_status_t +__file_size64(void *__fh, efi_char16_t *filename_16, +	      void **handle, u64 *file_sz) +{ +	efi_file_handle_64_t *h, *fh = __fh; +	efi_file_info_t *info; +	efi_status_t status; +	efi_guid_t info_guid = EFI_FILE_INFO_ID; +	u64 info_sz; + +	status = efi_early->call((unsigned long)fh->open, fh, &h, filename_16, +				 EFI_FILE_MODE_READ, (u64)0); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to open file: "); +		efi_char16_printk(sys_table, filename_16); +		efi_printk(sys_table, "\n"); +		return status; +	} + +	*handle = h; + +	info_sz = 0; +	status = efi_early->call((unsigned long)h->get_info, h, &info_guid, +				 &info_sz, NULL); +	if (status != EFI_BUFFER_TOO_SMALL) { +		efi_printk(sys_table, "Failed to get file info size\n"); +		return status; +	} + +grow: +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, +				info_sz, (void **)&info); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to alloc mem for file info\n"); +		return status; +	} + +	status = efi_early->call((unsigned long)h->get_info, h, &info_guid, +				 &info_sz, info); +	if (status == EFI_BUFFER_TOO_SMALL) { +		efi_call_early(free_pool, info); +		goto grow; +	} + +	*file_sz = info->file_size; +	efi_call_early(free_pool, info); + +	if (status != EFI_SUCCESS) +		efi_printk(sys_table, "Failed to get initrd info\n"); + +	return status; +} +static efi_status_t +efi_file_size(efi_system_table_t *sys_table, void *__fh, +	      efi_char16_t *filename_16, void **handle, u64 *file_sz) +{ +	if (efi_early->is64) +		return __file_size64(__fh, filename_16, handle, file_sz); + +	return __file_size32(__fh, filename_16, handle, file_sz); +} + +static inline efi_status_t +efi_file_read(void *handle, unsigned long *size, void *addr) +{ +	unsigned long func; + +	if (efi_early->is64) { +		efi_file_handle_64_t *fh = handle; + +		func = (unsigned long)fh->read; +		return efi_early->call(func, handle, size, addr); +	} else { +		efi_file_handle_32_t *fh = handle; + +		func = (unsigned long)fh->read; +		return efi_early->call(func, handle, size, addr); +	} +} + +static inline efi_status_t efi_file_close(void *handle) +{ +	if (efi_early->is64) { +		efi_file_handle_64_t *fh = handle; + +		return efi_early->call((unsigned long)fh->close, handle); +	} else { +		efi_file_handle_32_t *fh = handle; + +		return efi_early->call((unsigned long)fh->close, handle); +	} +} + +static inline efi_status_t __open_volume32(void *__image, void **__fh) +{ +	efi_file_io_interface_t *io; +	efi_loaded_image_32_t *image = __image; +	efi_file_handle_32_t *fh; +	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; +	efi_status_t status; +	void *handle = (void *)(unsigned long)image->device_handle; +	unsigned long func; + +	status = efi_call_early(handle_protocol, handle, +				&fs_proto, (void **)&io); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to handle fs_proto\n"); +		return status; +	} + +	func = (unsigned long)io->open_volume; +	status = efi_early->call(func, io, &fh); +	if (status != EFI_SUCCESS) +		efi_printk(sys_table, "Failed to open volume\n"); + +	*__fh = fh; +	return status; +} + +static inline efi_status_t __open_volume64(void *__image, void **__fh) +{ +	efi_file_io_interface_t *io; +	efi_loaded_image_64_t *image = __image; +	efi_file_handle_64_t *fh; +	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; +	efi_status_t status; +	void *handle = (void *)(unsigned long)image->device_handle; +	unsigned long func; + +	status = efi_call_early(handle_protocol, handle, +				&fs_proto, (void **)&io); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to handle fs_proto\n"); +		return status; +	} + +	func = (unsigned long)io->open_volume; +	status = efi_early->call(func, io, &fh); +	if (status != EFI_SUCCESS) +		efi_printk(sys_table, "Failed to open volume\n"); + +	*__fh = fh; +	return status; +} + +static inline efi_status_t +efi_open_volume(efi_system_table_t *sys_table, void *__image, void **__fh) +{ +	if (efi_early->is64) +		return __open_volume64(__image, __fh); + +	return __open_volume32(__image, __fh); +} + +static void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str) +{ +	unsigned long output_string; +	size_t offset; + +	if (efi_early->is64) { +		struct efi_simple_text_output_protocol_64 *out; +		u64 *func; + +		offset = offsetof(typeof(*out), output_string); +		output_string = efi_early->text_output + offset; +		func = (u64 *)output_string; + +		efi_early->call(*func, efi_early->text_output, str); +	} else { +		struct efi_simple_text_output_protocol_32 *out; +		u32 *func; + +		offset = offsetof(typeof(*out), output_string); +		output_string = efi_early->text_output + offset; +		func = (u32 *)output_string; + +		efi_early->call(*func, efi_early->text_output, str); +	} +} + +#include "../../../../drivers/firmware/efi/efi-stub-helper.c" + +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; +} + +static efi_status_t +__setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom) +{ +	struct pci_setup_rom *rom = NULL; +	efi_status_t status; +	unsigned long size; +	uint64_t attributes; + +	status = efi_early->call(pci->attributes, pci, +				 EfiPciIoAttributeOperationGet, 0, 0, +				 &attributes); +	if (status != EFI_SUCCESS) +		return status; + +	if (!pci->romimage || !pci->romsize) +		return EFI_INVALID_PARAMETER; + +	size = pci->romsize + sizeof(*rom); + +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom); +	if (status != EFI_SUCCESS) +		return status; + +	memset(rom, 0, sizeof(*rom)); + +	rom->data.type = SETUP_PCI; +	rom->data.len = size - sizeof(struct setup_data); +	rom->data.next = 0; +	rom->pcilen = pci->romsize; +	*__rom = rom; + +	status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, +				 PCI_VENDOR_ID, 1, &(rom->vendor)); + +	if (status != EFI_SUCCESS) +		goto free_struct; + +	status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, +				 PCI_DEVICE_ID, 1, &(rom->devid)); + +	if (status != EFI_SUCCESS) +		goto free_struct; + +	status = efi_early->call(pci->get_location, pci, &(rom->segment), +				 &(rom->bus), &(rom->device), &(rom->function)); + +	if (status != EFI_SUCCESS) +		goto free_struct; + +	memcpy(rom->romdata, pci->romimage, pci->romsize); +	return status; + +free_struct: +	efi_call_early(free_pool, rom); +	return status; +} + +static efi_status_t +setup_efi_pci32(struct boot_params *params, void **pci_handle, +		unsigned long size) +{ +	efi_pci_io_protocol_32 *pci = NULL; +	efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; +	u32 *handles = (u32 *)(unsigned long)pci_handle; +	efi_status_t status; +	unsigned long nr_pci; +	struct setup_data *data; +	int i; + +	data = (struct setup_data *)(unsigned long)params->hdr.setup_data; + +	while (data && data->next) +		data = (struct setup_data *)(unsigned long)data->next; + +	nr_pci = size / sizeof(u32); +	for (i = 0; i < nr_pci; i++) { +		struct pci_setup_rom *rom = NULL; +		u32 h = handles[i]; + +		status = efi_call_early(handle_protocol, h, +					&pci_proto, (void **)&pci); + +		if (status != EFI_SUCCESS) +			continue; + +		if (!pci) +			continue; + +		status = __setup_efi_pci32(pci, &rom); +		if (status != EFI_SUCCESS) +			continue; + +		if (data) +			data->next = (unsigned long)rom; +		else +			params->hdr.setup_data = (unsigned long)rom; + +		data = (struct setup_data *)rom; + +	} + +	return status; +} + +static efi_status_t +__setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom) +{ +	struct pci_setup_rom *rom; +	efi_status_t status; +	unsigned long size; +	uint64_t attributes; + +	status = efi_early->call(pci->attributes, pci, +				 EfiPciIoAttributeOperationGet, 0, +				 &attributes); +	if (status != EFI_SUCCESS) +		return status; + +	if (!pci->romimage || !pci->romsize) +		return EFI_INVALID_PARAMETER; + +	size = pci->romsize + sizeof(*rom); + +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom); +	if (status != EFI_SUCCESS) +		return status; + +	rom->data.type = SETUP_PCI; +	rom->data.len = size - sizeof(struct setup_data); +	rom->data.next = 0; +	rom->pcilen = pci->romsize; +	*__rom = rom; + +	status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, +				 PCI_VENDOR_ID, 1, &(rom->vendor)); + +	if (status != EFI_SUCCESS) +		goto free_struct; + +	status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, +				 PCI_DEVICE_ID, 1, &(rom->devid)); + +	if (status != EFI_SUCCESS) +		goto free_struct; + +	status = efi_early->call(pci->get_location, pci, &(rom->segment), +				 &(rom->bus), &(rom->device), &(rom->function)); + +	if (status != EFI_SUCCESS) +		goto free_struct; + +	memcpy(rom->romdata, pci->romimage, pci->romsize); +	return status; + +free_struct: +	efi_call_early(free_pool, rom); +	return status; + +} + +static efi_status_t +setup_efi_pci64(struct boot_params *params, void **pci_handle, +		unsigned long size) +{ +	efi_pci_io_protocol_64 *pci = NULL; +	efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; +	u64 *handles = (u64 *)(unsigned long)pci_handle; +	efi_status_t status; +	unsigned long nr_pci; +	struct setup_data *data; +	int i; + +	data = (struct setup_data *)(unsigned long)params->hdr.setup_data; + +	while (data && data->next) +		data = (struct setup_data *)(unsigned long)data->next; + +	nr_pci = size / sizeof(u64); +	for (i = 0; i < nr_pci; i++) { +		struct pci_setup_rom *rom = NULL; +		u64 h = handles[i]; + +		status = efi_call_early(handle_protocol, h, +					&pci_proto, (void **)&pci); + +		if (status != EFI_SUCCESS) +			continue; + +		if (!pci) +			continue; + +		status = __setup_efi_pci64(pci, &rom); +		if (status != EFI_SUCCESS) +			continue; + +		if (data) +			data->next = (unsigned long)rom; +		else +			params->hdr.setup_data = (unsigned long)rom; + +		data = (struct setup_data *)rom; + +	} + +	return status; +} + +static efi_status_t setup_efi_pci(struct boot_params *params) +{ +	efi_status_t status; +	void **pci_handle = NULL; +	efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; +	unsigned long size = 0; + +	status = efi_call_early(locate_handle, +				EFI_LOCATE_BY_PROTOCOL, +				&pci_proto, NULL, &size, pci_handle); + +	if (status == EFI_BUFFER_TOO_SMALL) { +		status = efi_call_early(allocate_pool, +					EFI_LOADER_DATA, +					size, (void **)&pci_handle); + +		if (status != EFI_SUCCESS) +			return status; + +		status = efi_call_early(locate_handle, +					EFI_LOCATE_BY_PROTOCOL, &pci_proto, +					NULL, &size, pci_handle); +	} + +	if (status != EFI_SUCCESS) +		goto free_handle; + +	if (efi_early->is64) +		status = setup_efi_pci64(params, pci_handle, size); +	else +		status = setup_efi_pci32(params, pci_handle, size); + +free_handle: +	efi_call_early(free_pool, pci_handle); +	return status; +} + +static void +setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, +		 struct efi_pixel_bitmask pixel_info, int pixel_format) +{ +	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; +	} +} + +static efi_status_t +__gop_query32(struct efi_graphics_output_protocol_32 *gop32, +	      struct efi_graphics_output_mode_info **info, +	      unsigned long *size, u32 *fb_base) +{ +	struct efi_graphics_output_protocol_mode_32 *mode; +	efi_status_t status; +	unsigned long m; + +	m = gop32->mode; +	mode = (struct efi_graphics_output_protocol_mode_32 *)m; + +	status = efi_early->call(gop32->query_mode, gop32, +				 mode->mode, size, info); +	if (status != EFI_SUCCESS) +		return status; + +	*fb_base = mode->frame_buffer_base; +	return status; +} + +static efi_status_t +setup_gop32(struct screen_info *si, efi_guid_t *proto, +	    unsigned long size, void **gop_handle) +{ +	struct efi_graphics_output_protocol_32 *gop32, *first_gop; +	unsigned long nr_gops; +	u16 width, height; +	u32 pixels_per_scan_line; +	u32 fb_base; +	struct efi_pixel_bitmask pixel_info; +	int pixel_format; +	efi_status_t status; +	u32 *handles = (u32 *)(unsigned long)gop_handle; +	int i; + +	first_gop = NULL; +	gop32 = NULL; + +	nr_gops = size / sizeof(u32); +	for (i = 0; i < nr_gops; i++) { +		struct efi_graphics_output_mode_info *info = NULL; +		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; +		bool conout_found = false; +		void *dummy = NULL; +		u32 h = handles[i]; + +		status = efi_call_early(handle_protocol, h, +					proto, (void **)&gop32); +		if (status != EFI_SUCCESS) +			continue; + +		status = efi_call_early(handle_protocol, h, +					&conout_proto, &dummy); +		if (status == EFI_SUCCESS) +			conout_found = true; + +		status = __gop_query32(gop32, &info, &size, &fb_base); +		if (status == EFI_SUCCESS && (!first_gop || conout_found)) { +			/* +			 * Systems that use the UEFI Console Splitter may +			 * provide multiple GOP devices, not all of which are +			 * backed by real hardware. The workaround is to search +			 * for a GOP implementing the ConOut protocol, and if +			 * one isn't found, to just fall back to the first GOP. +			 */ +			width = info->horizontal_resolution; +			height = info->vertical_resolution; +			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 ConOut, +			 * don't bother looking any further. +			 */ +			first_gop = gop32; +			if (conout_found) +				break; +		} +	} + +	/* Did we find any GOPs? */ +	if (!first_gop) +		goto out; + +	/* EFI framebuffer */ +	si->orig_video_isVGA = VIDEO_TYPE_EFI; + +	si->lfb_width = width; +	si->lfb_height = height; +	si->lfb_base = fb_base; +	si->pages = 1; + +	setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format); + +	si->lfb_size = si->lfb_linelength * si->lfb_height; + +	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; +out: +	return status; +} + +static efi_status_t +__gop_query64(struct efi_graphics_output_protocol_64 *gop64, +	      struct efi_graphics_output_mode_info **info, +	      unsigned long *size, u32 *fb_base) +{ +	struct efi_graphics_output_protocol_mode_64 *mode; +	efi_status_t status; +	unsigned long m; + +	m = gop64->mode; +	mode = (struct efi_graphics_output_protocol_mode_64 *)m; + +	status = efi_early->call(gop64->query_mode, gop64, +				 mode->mode, size, info); +	if (status != EFI_SUCCESS) +		return status; + +	*fb_base = mode->frame_buffer_base; +	return status; +} + +static efi_status_t +setup_gop64(struct screen_info *si, efi_guid_t *proto, +	    unsigned long size, void **gop_handle) +{ +	struct efi_graphics_output_protocol_64 *gop64, *first_gop; +	unsigned long nr_gops; +	u16 width, height; +	u32 pixels_per_scan_line; +	u32 fb_base; +	struct efi_pixel_bitmask pixel_info; +	int pixel_format; +	efi_status_t status; +	u64 *handles = (u64 *)(unsigned long)gop_handle; +	int i; + +	first_gop = NULL; +	gop64 = NULL; + +	nr_gops = size / sizeof(u64); +	for (i = 0; i < nr_gops; i++) { +		struct efi_graphics_output_mode_info *info = NULL; +		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; +		bool conout_found = false; +		void *dummy = NULL; +		u64 h = handles[i]; + +		status = efi_call_early(handle_protocol, h, +					proto, (void **)&gop64); +		if (status != EFI_SUCCESS) +			continue; + +		status = efi_call_early(handle_protocol, h, +					&conout_proto, &dummy); +		if (status == EFI_SUCCESS) +			conout_found = true; + +		status = __gop_query64(gop64, &info, &size, &fb_base); +		if (status == EFI_SUCCESS && (!first_gop || conout_found)) { +			/* +			 * Systems that use the UEFI Console Splitter may +			 * provide multiple GOP devices, not all of which are +			 * backed by real hardware. The workaround is to search +			 * for a GOP implementing the ConOut protocol, and if +			 * one isn't found, to just fall back to the first GOP. +			 */ +			width = info->horizontal_resolution; +			height = info->vertical_resolution; +			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 ConOut, +			 * don't bother looking any further. +			 */ +			first_gop = gop64; +			if (conout_found) +				break; +		} +	} + +	/* Did we find any GOPs? */ +	if (!first_gop) +		goto out; + +	/* EFI framebuffer */ +	si->orig_video_isVGA = VIDEO_TYPE_EFI; + +	si->lfb_width = width; +	si->lfb_height = height; +	si->lfb_base = fb_base; +	si->pages = 1; + +	setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format); + +	si->lfb_size = si->lfb_linelength * si->lfb_height; + +	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; +out: +	return status; +} + +/* + * 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_status_t status; +	void **gop_handle = NULL; + +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, +				size, (void **)&gop_handle); +	if (status != EFI_SUCCESS) +		return status; + +	status = efi_call_early(locate_handle, +				EFI_LOCATE_BY_PROTOCOL, +				proto, NULL, &size, gop_handle); +	if (status != EFI_SUCCESS) +		goto free_handle; + +	if (efi_early->is64) +		status = setup_gop64(si, proto, size, gop_handle); +	else +		status = setup_gop32(si, proto, size, gop_handle); + +free_handle: +	efi_call_early(free_pool, gop_handle); +	return status; +} + +static efi_status_t +setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height) +{ +	struct efi_uga_draw_protocol *uga = NULL, *first_uga; +	efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID; +	unsigned long nr_ugas; +	u32 *handles = (u32 *)uga_handle;; +	efi_status_t status; +	int i; + +	first_uga = NULL; +	nr_ugas = size / sizeof(u32); +	for (i = 0; i < nr_ugas; i++) { +		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID; +		u32 w, h, depth, refresh; +		void *pciio; +		u32 handle = handles[i]; + +		status = efi_call_early(handle_protocol, handle, +					&uga_proto, (void **)&uga); +		if (status != EFI_SUCCESS) +			continue; + +		efi_call_early(handle_protocol, handle, &pciio_proto, &pciio); + +		status = efi_early->call((unsigned long)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; +		} +	} + +	return status; +} + +static efi_status_t +setup_uga64(void **uga_handle, unsigned long size, u32 *width, u32 *height) +{ +	struct efi_uga_draw_protocol *uga = NULL, *first_uga; +	efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID; +	unsigned long nr_ugas; +	u64 *handles = (u64 *)uga_handle;; +	efi_status_t status; +	int i; + +	first_uga = NULL; +	nr_ugas = size / sizeof(u64); +	for (i = 0; i < nr_ugas; i++) { +		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID; +		u32 w, h, depth, refresh; +		void *pciio; +		u64 handle = handles[i]; + +		status = efi_call_early(handle_protocol, handle, +					&uga_proto, (void **)&uga); +		if (status != EFI_SUCCESS) +			continue; + +		efi_call_early(handle_protocol, handle, &pciio_proto, &pciio); + +		status = efi_early->call((unsigned long)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; +		} +	} + +	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_status_t status; +	u32 width, height; +	void **uga_handle = NULL; + +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, +				size, (void **)&uga_handle); +	if (status != EFI_SUCCESS) +		return status; + +	status = efi_call_early(locate_handle, +				EFI_LOCATE_BY_PROTOCOL, +				uga_proto, NULL, &size, uga_handle); +	if (status != EFI_SUCCESS) +		goto free_handle; + +	height = 0; +	width = 0; + +	if (efi_early->is64) +		status = setup_uga64(uga_handle, size, &width, &height); +	else +		status = setup_uga32(uga_handle, size, &width, &height); + +	if (!width && !height) +		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_early(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_early(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_early(locate_handle, +					EFI_LOCATE_BY_PROTOCOL, +					&uga_proto, NULL, &size, uga_handle); +		if (status == EFI_BUFFER_TOO_SMALL) +			setup_uga(si, &uga_proto, size); +	} +} + +/* + * 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). + * + * The caller is responsible for filling out ->code32_start in the + * returned boot_params. + */ +struct boot_params *make_boot_params(struct efi_config *c) +{ +	struct boot_params *boot_params; +	struct sys_desc_table *sdt; +	struct apm_bios_info *bi; +	struct setup_header *hdr; +	struct efi_info *efi; +	efi_loaded_image_t *image; +	void *options, *handle; +	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; +	int options_size = 0; +	efi_status_t status; +	char *cmdline_ptr; +	u16 *s2; +	u8 *s1; +	int i; +	unsigned long ramdisk_addr; +	unsigned long ramdisk_size; + +	efi_early = c; +	sys_table = (efi_system_table_t *)(unsigned long)efi_early->table; +	handle = (void *)(unsigned long)efi_early->image_handle; + +	/* Check if we were booted by the EFI firmware */ +	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) +		return NULL; + +	if (efi_early->is64) +		setup_boot_services64(efi_early); +	else +		setup_boot_services32(efi_early); + +	status = efi_call_early(handle_protocol, handle, +				&proto, (void *)&image); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); +		return NULL; +	} + +	status = efi_low_alloc(sys_table, 0x4000, 1, +			       (unsigned long *)&boot_params); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n"); +		return NULL; +	} + +	memset(boot_params, 0x0, 0x4000); + +	hdr = &boot_params->hdr; +	efi = &boot_params->efi_info; +	bi = &boot_params->apm_bios_info; +	sdt = &boot_params->sys_desc_table; + +	/* Copy the second sector to boot_params */ +	memcpy(&hdr->jump, image->image_base + 512, 512); + +	/* +	 * Fill out some of the header fields ourselves because the +	 * EFI firmware loader doesn't load the first sector. +	 */ +	hdr->root_flags = 1; +	hdr->vid_mode = 0xffff; +	hdr->boot_flag = 0xAA55; + +	hdr->type_of_loader = 0x21; + +	/* Convert unicode cmdline to ascii */ +	cmdline_ptr = efi_convert_cmdline(sys_table, image, &options_size); +	if (!cmdline_ptr) +		goto fail; +	hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; + +	hdr->ramdisk_image = 0; +	hdr->ramdisk_size = 0; + +	/* Clear APM BIOS info */ +	memset(bi, 0, sizeof(*bi)); + +	memset(sdt, 0, sizeof(*sdt)); + +	status = handle_cmdline_files(sys_table, image, +				      (char *)(unsigned long)hdr->cmd_line_ptr, +				      "initrd=", hdr->initrd_addr_max, +				      &ramdisk_addr, &ramdisk_size); +	if (status != EFI_SUCCESS) +		goto fail2; +	hdr->ramdisk_image = ramdisk_addr; +	hdr->ramdisk_size = ramdisk_size; + +	return boot_params; +fail2: +	efi_free(sys_table, options_size, hdr->cmd_line_ptr); +fail: +	efi_free(sys_table, 0x4000, (unsigned long)boot_params); +	return NULL; +} + +static void add_e820ext(struct boot_params *params, +			struct setup_data *e820ext, u32 nr_entries) +{ +	struct setup_data *data; +	efi_status_t status; +	unsigned long size; + +	e820ext->type = SETUP_E820_EXT; +	e820ext->len = nr_entries * sizeof(struct e820entry); +	e820ext->next = 0; + +	data = (struct setup_data *)(unsigned long)params->hdr.setup_data; + +	while (data && data->next) +		data = (struct setup_data *)(unsigned long)data->next; + +	if (data) +		data->next = (unsigned long)e820ext; +	else +		params->hdr.setup_data = (unsigned long)e820ext; +} + +static efi_status_t setup_e820(struct boot_params *params, +			       struct setup_data *e820ext, u32 e820ext_size) +{ +	struct e820entry *e820_map = ¶ms->e820_map[0]; +	struct efi_info *efi = ¶ms->efi_info; +	struct e820entry *prev = NULL; +	u32 nr_entries; +	u32 nr_desc; +	int i; + +	nr_entries = 0; +	nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size; + +	for (i = 0; i < nr_desc; i++) { +		efi_memory_desc_t *d; +		unsigned int e820_type = 0; +		unsigned long m = efi->efi_memmap; + +		d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_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; +			continue; +		} + +		if (nr_entries == ARRAY_SIZE(params->e820_map)) { +			u32 need = (nr_desc - i) * sizeof(struct e820entry) + +				   sizeof(struct setup_data); + +			if (!e820ext || e820ext_size < need) +				return EFI_BUFFER_TOO_SMALL; + +			/* boot_params map full, switch to e820 extended */ +			e820_map = (struct e820entry *)e820ext->data; +		} + +		e820_map->addr = d->phys_addr; +		e820_map->size = d->num_pages << PAGE_SHIFT; +		e820_map->type = e820_type; +		prev = e820_map++; +		nr_entries++; +	} + +	if (nr_entries > ARRAY_SIZE(params->e820_map)) { +		u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map); + +		add_e820ext(params, e820ext, nr_e820ext); +		nr_entries -= nr_e820ext; +	} + +	params->e820_entries = (u8)nr_entries; + +	return EFI_SUCCESS; +} + +static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext, +				  u32 *e820ext_size) +{ +	efi_status_t status; +	unsigned long size; + +	size = sizeof(struct setup_data) + +		sizeof(struct e820entry) * nr_desc; + +	if (*e820ext) { +		efi_call_early(free_pool, *e820ext); +		*e820ext = NULL; +		*e820ext_size = 0; +	} + +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, +				size, (void **)e820ext); +	if (status == EFI_SUCCESS) +		*e820ext_size = size; + +	return status; +} + +static efi_status_t exit_boot(struct boot_params *boot_params, +			      void *handle, bool is64) +{ +	struct efi_info *efi = &boot_params->efi_info; +	unsigned long map_sz, key, desc_size; +	efi_memory_desc_t *mem_map; +	struct setup_data *e820ext; +	const char *signature; +	__u32 e820ext_size; +	__u32 nr_desc, prev_nr_desc; +	efi_status_t status; +	__u32 desc_version; +	bool called_exit = false; +	u8 nr_entries; +	int i; + +	nr_desc = 0; +	e820ext = NULL; +	e820ext_size = 0; + +get_map: +	status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size, +				    &desc_version, &key); + +	if (status != EFI_SUCCESS) +		return status; + +	prev_nr_desc = nr_desc; +	nr_desc = map_sz / desc_size; +	if (nr_desc > prev_nr_desc && +	    nr_desc > ARRAY_SIZE(boot_params->e820_map)) { +		u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map); + +		status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size); +		if (status != EFI_SUCCESS) +			goto free_mem_map; + +		efi_call_early(free_pool, mem_map); +		goto get_map; /* Allocated memory, get map again */ +	} + +	signature = is64 ? EFI64_LOADER_SIGNATURE : EFI32_LOADER_SIGNATURE; +	memcpy(&efi->efi_loader_signature, signature, sizeof(__u32)); + +	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 = map_sz; + +#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_early(exit_boot_services, handle, key); +	if (status != EFI_SUCCESS) { +		/* +		 * ExitBootServices() will fail if any of the event +		 * handlers change the memory map. In which case, we +		 * must be prepared to retry, but only once so that +		 * we're guaranteed to exit on repeated failures instead +		 * of spinning forever. +		 */ +		if (called_exit) +			goto free_mem_map; + +		called_exit = true; +		efi_call_early(free_pool, mem_map); +		goto get_map; +	} + +	/* Historic? */ +	boot_params->alt_mem_k = 32 * 1024; + +	status = setup_e820(boot_params, e820ext, e820ext_size); +	if (status != EFI_SUCCESS) +		return status; + +	return EFI_SUCCESS; + +free_mem_map: +	efi_call_early(free_pool, mem_map); +	return status; +} + +/* + * On success we return a pointer to a boot_params structure, and NULL + * on failure. + */ +struct boot_params *efi_main(struct efi_config *c, +			     struct boot_params *boot_params) +{ +	struct desc_ptr *gdt = NULL; +	efi_loaded_image_t *image; +	struct setup_header *hdr = &boot_params->hdr; +	efi_status_t status; +	struct desc_struct *desc; +	void *handle; +	efi_system_table_t *_table; +	bool is64; + +	efi_early = c; + +	_table = (efi_system_table_t *)(unsigned long)efi_early->table; +	handle = (void *)(unsigned long)efi_early->image_handle; +	is64 = efi_early->is64; + +	sys_table = _table; + +	/* Check if we were booted by the EFI firmware */ +	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) +		goto fail; + +	if (is64) +		setup_boot_services64(efi_early); +	else +		setup_boot_services32(efi_early); + +	setup_graphics(boot_params); + +	setup_efi_pci(boot_params); + +	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, +				sizeof(*gdt), (void **)&gdt); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to alloc mem for gdt structure\n"); +		goto fail; +	} + +	gdt->size = 0x800; +	status = efi_low_alloc(sys_table, gdt->size, 8, +			   (unsigned long *)&gdt->address); +	if (status != EFI_SUCCESS) { +		efi_printk(sys_table, "Failed to alloc mem for gdt\n"); +		goto fail; +	} + +	/* +	 * If the kernel isn't already loaded at the preferred load +	 * address, relocate it. +	 */ +	if (hdr->pref_address != hdr->code32_start) { +		unsigned long bzimage_addr = hdr->code32_start; +		status = efi_relocate_kernel(sys_table, &bzimage_addr, +					     hdr->init_size, hdr->init_size, +					     hdr->pref_address, +					     hdr->kernel_alignment); +		if (status != EFI_SUCCESS) +			goto fail; + +		hdr->pref_address = hdr->code32_start; +		hdr->code32_start = bzimage_addr; +	} + +	status = exit_boot(boot_params, handle, is64); +	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("cli"); +	asm volatile ("lgdt %0" : : "m" (*gdt)); + +	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 00000000000..c88c31ecad1 --- /dev/null +++ b/arch/x86/boot/compressed/eboot.h @@ -0,0 +1,122 @@ +#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_CONSOLE_OUT_DEVICE_GUID    \ +	EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \ +		  0x3f, 0xc1, 0x4d) + +#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_32 { +	u32 max_mode; +	u32 mode; +	u32 info; +	u32 size_of_info; +	u64 frame_buffer_base; +	u32 frame_buffer_size; +} __packed; + +struct efi_graphics_output_protocol_mode_64 { +	u32 max_mode; +	u32 mode; +	u64 info; +	u64 size_of_info; +	u64 frame_buffer_base; +	u64 frame_buffer_size; +} __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_32 { +	u32 query_mode; +	u32 set_mode; +	u32 blt; +	u32 mode; +}; + +struct efi_graphics_output_protocol_64 { +	u64 query_mode; +	u64 set_mode; +	u64 blt; +	u64 mode; +}; + +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_32 { +	u32 get_mode; +	u32 set_mode; +	u32 blt; +}; + +struct efi_uga_draw_protocol_64 { +	u64 get_mode; +	u64 set_mode; +	u64 blt; +}; + +struct efi_uga_draw_protocol { +	void *get_mode; +	void *set_mode; +	void *blt; +}; + +struct efi_config { +	u64 image_handle; +	u64 table; +	u64 allocate_pool; +	u64 allocate_pages; +	u64 get_memory_map; +	u64 free_pool; +	u64 free_pages; +	u64 locate_handle; +	u64 handle_protocol; +	u64 exit_boot_services; +	u64 text_output; +	efi_status_t (*call)(unsigned long, ...); +	bool is64; +} __packed; + +#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 00000000000..a53440e81d5 --- /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 00000000000..7ff3632806b --- /dev/null +++ b/arch/x86/boot/compressed/efi_stub_64.S @@ -0,0 +1,30 @@ +#include <asm/segment.h> +#include <asm/msr.h> +#include <asm/processor-flags.h> + +#include "../../platform/efi/efi_stub_64.S" + +#ifdef CONFIG_EFI_MIXED +	.code64 +	.text +ENTRY(efi64_thunk) +	push	%rbp +	push	%rbx + +	subq	$16, %rsp +	leaq	efi_exit32(%rip), %rax +	movl	%eax, 8(%rsp) +	leaq	efi_gdt64(%rip), %rax +	movl	%eax, 4(%rsp) +	movl	%eax, 2(%rax)		/* Fixup the gdt base address */ +	leaq	efi32_boot_gdt(%rip), %rax +	movl	%eax, (%rsp) + +	call	__efi64_thunk + +	addq	$16, %rsp +	pop	%rbx +	pop	%rbp +	ret +ENDPROC(efi64_thunk) +#endif /* CONFIG_EFI_MIXED */ diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index 67a655a39ce..cbed1407a5c 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S @@ -32,6 +32,71 @@  	__HEAD  ENTRY(startup_32) +#ifdef CONFIG_EFI_STUB +	jmp	preferred_addr + +	/* +	 * We don't need the return address, so set up the stack so +	 * efi_main() can find its arguments. +	 */ +ENTRY(efi_pe_entry) +	add	$0x4, %esp + +	call	1f +1:	popl	%esi +	subl	$1b, %esi + +	popl	%ecx +	movl	%ecx, efi32_config(%esi)	/* Handle */ +	popl	%ecx +	movl	%ecx, efi32_config+8(%esi)	/* EFI System table pointer */ + +	/* Relocate efi_config->call() */ +	leal	efi32_config(%esi), %eax +	add	%esi, 88(%eax) +	pushl	%eax + +	call	make_boot_params +	cmpl	$0, %eax +	je	fail +	movl	%esi, BP_code32_start(%eax) +	popl	%ecx +	pushl	%eax +	pushl	%ecx +	jmp	2f		/* Skip efi_config initialization */ + +ENTRY(efi32_stub_entry) +	add	$0x4, %esp +	popl	%ecx +	popl	%edx + +	call	1f +1:	popl	%esi +	subl	$1b, %esi + +	movl	%ecx, efi32_config(%esi)	/* Handle */ +	movl	%edx, efi32_config+8(%esi)	/* EFI System table pointer */ + +	/* Relocate efi_config->call() */ +	leal	efi32_config(%esi), %eax +	add	%esi, 88(%eax) +	pushl	%eax +2: +	call	efi_main +	cmpl	$0, %eax +	movl	%eax, %esi +	jne	2f +fail: +	/* EFI init failed, so hang. */ +	hlt +	jmp	fail +2: +	movl	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 @@ -75,9 +140,11 @@ ENTRY(startup_32)  	addl    %eax, %ebx  	notl	%eax  	andl    %eax, %ebx -#else -	movl	$LOAD_PHYSICAL_ADDR, %ebx +	cmpl	$LOAD_PHYSICAL_ADDR, %ebx +	jge	1f  #endif +	movl	$LOAD_PHYSICAL_ADDR, %ebx +1:  	/* Target address to relocate to for decompression */  	addl	$z_extract_offset, %ebx @@ -139,8 +206,9 @@ relocated:  /*   * Do the decompression, and jump to the new kernel..   */ -	leal	z_extract_offset_negative(%ebx), %ebp  				/* push arguments for decompress_kernel: */ +	pushl	$z_output_len	/* decompressed length */ +	leal	z_extract_offset_negative(%ebx), %ebp  	pushl	%ebp		/* output address */  	pushl	$z_input_len	/* input_len */  	leal	input_data(%ebx), %eax @@ -148,40 +216,23 @@ relocated:  	leal	boot_heap(%ebx), %eax  	pushl	%eax		/* heap area */  	pushl	%esi		/* real mode pointer */ -	call	decompress_kernel -	addl	$20, %esp - -#if CONFIG_RELOCATABLE -/* - * Find the address of the relocations. - */ -	leal	z_output_len(%ebp), %edi - -/* - * Calculate the delta between where vmlinux was compiled to run - * and where it was actually loaded. - */ -	movl	%ebp, %ebx -	subl	$LOAD_PHYSICAL_ADDR, %ebx -	jz	2f	/* Nothing to be done if loaded at compiled addr. */ -/* - * Process relocations. - */ - -1:	subl	$4, %edi -	movl	(%edi), %ecx -	testl	%ecx, %ecx -	jz	2f -	addl	%ebx, -__PAGE_OFFSET(%ebx, %ecx) -	jmp	1b -2: -#endif +	call	decompress_kernel /* returns kernel location in %eax */ +	addl	$24, %esp  /*   * Jump to the decompressed kernel.   */  	xorl	%ebx, %ebx -	jmp	*%ebp +	jmp	*%eax + +#ifdef CONFIG_EFI_STUB +	.data +efi32_config: +	.fill 11,8,0 +	.long efi_call_phys +	.long 0 +	.byte 0 +#endif  /*   * Stack and heap for uncompression diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index 52f85a196fa..2884e0c3e8a 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -27,8 +27,6 @@  #include <linux/init.h>  #include <linux/linkage.h>  #include <asm/segment.h> -#include <asm/pgtable_types.h> -#include <asm/page_types.h>  #include <asm/boot.h>  #include <asm/msr.h>  #include <asm/processor-flags.h> @@ -37,6 +35,12 @@  	__HEAD  	.code32  ENTRY(startup_32) +	/* +	 * 32bit entry is 0 and it is ABI so immutable! +	 * If we come here directly from a bootloader, +	 * kernel(text+data+bss+brk) ramdisk, zero_page, command line +	 * all need to be under the 4G limit. +	 */  	cld  	/*  	 * Test KEEP_SEGMENTS flag to see if the bootloader is asking @@ -46,7 +50,7 @@ ENTRY(startup_32)  	jnz 1f  	cli -	movl	$(__KERNEL_DS), %eax +	movl	$(__BOOT_DS), %eax  	movl	%eax, %ds  	movl	%eax, %es  	movl	%eax, %ss @@ -90,9 +94,11 @@ ENTRY(startup_32)  	addl	%eax, %ebx  	notl	%eax  	andl	%eax, %ebx -#else -	movl	$LOAD_PHYSICAL_ADDR, %ebx +	cmpl	$LOAD_PHYSICAL_ADDR, %ebx +	jge	1f  #endif +	movl	$LOAD_PHYSICAL_ADDR, %ebx +1:  	/* Target address to relocate to for decompression */  	addl	$z_extract_offset, %ebx @@ -107,7 +113,8 @@ ENTRY(startup_32)  	lgdt	gdt(%ebp)  	/* Enable PAE mode */ -	movl	$(X86_CR4_PAE), %eax +	movl	%cr4, %eax +	orl	$X86_CR4_PAE, %eax  	movl	%eax, %cr4   /* @@ -154,6 +161,12 @@ ENTRY(startup_32)  	btsl	$_EFER_LME, %eax  	wrmsr +	/* After gdt is loaded */ +	xorl	%eax, %eax +	lldt	%ax +	movl    $0x20, %eax +	ltr	%ax +  	/*  	 * Setup for the jump to 64bit mode  	 * @@ -166,6 +179,13 @@ ENTRY(startup_32)  	 */  	pushl	$__KERNEL_CS  	leal	startup_64(%ebp), %eax +#ifdef CONFIG_EFI_MIXED +	movl	efi32_config(%ebp), %ebx +	cmp	$0, %ebx +	jz	1f +	leal	handover_entry(%ebp), %eax +1: +#endif  	pushl	%eax  	/* Enter paged protected Mode, activating Long Mode */ @@ -176,29 +196,102 @@ ENTRY(startup_32)  	lret  ENDPROC(startup_32) -no_longmode: -	/* This isn't an x86-64 CPU so hang */ -1: -	hlt -	jmp     1b +#ifdef CONFIG_EFI_MIXED +	.org 0x190 +ENTRY(efi32_stub_entry) +	add	$0x4, %esp		/* Discard return address */ +	popl	%ecx +	popl	%edx +	popl	%esi -#include "../../kernel/verify_cpu_64.S" +	leal	(BP_scratch+4)(%esi), %esp +	call	1f +1:	pop	%ebp +	subl	$1b, %ebp + +	movl	%ecx, efi32_config(%ebp) +	movl	%edx, efi32_config+8(%ebp) +	sgdtl	efi32_boot_gdt(%ebp) + +	leal	efi32_config(%ebp), %eax +	movl	%eax, efi_config(%ebp) + +	jmp	startup_32 +ENDPROC(efi32_stub_entry) +#endif -	/* -	 * Be careful here startup_64 needs to be at a predictable -	 * address so I can export it in an ELF header.  Bootloaders -	 * should look at the ELF header to find this address, as -	 * it may change in the future. -	 */  	.code64  	.org 0x200  ENTRY(startup_64)  	/* +	 * 64bit entry is 0x200 and it is ABI so immutable!  	 * We come here either from startup_32 or directly from a -	 * 64bit bootloader.  If we come here from a bootloader we depend on -	 * an identity mapped page table being provied that maps our -	 * entire text+data+bss and hopefully all of memory. +	 * 64bit bootloader. +	 * If we come here from a bootloader, kernel(text+data+bss+brk), +	 * ramdisk, zero_page, command line could be above 4G. +	 * We depend on an identity mapped page table being provided +	 * that maps our entire kernel(text+data+bss+brk), zero page +	 * and command line. +	 */ +#ifdef CONFIG_EFI_STUB +	/* +	 * The entry point for the PE/COFF executable is efi_pe_entry, so +	 * only legacy boot loaders will execute this jmp. +	 */ +	jmp	preferred_addr + +ENTRY(efi_pe_entry) +	movq	%rcx, efi64_config(%rip)	/* Handle */ +	movq	%rdx, efi64_config+8(%rip) /* EFI System table pointer */ + +	leaq	efi64_config(%rip), %rax +	movq	%rax, efi_config(%rip) + +	call	1f +1:	popq	%rbp +	subq	$1b, %rbp + +	/* +	 * Relocate efi_config->call(). +	 */ +	addq	%rbp, efi64_config+88(%rip) + +	movq	%rax, %rdi +	call	make_boot_params +	cmpq	$0,%rax +	je	fail +	mov	%rax, %rsi +	leaq	startup_32(%rip), %rax +	movl	%eax, BP_code32_start(%rsi) +	jmp	2f		/* Skip the relocation */ + +handover_entry: +	call	1f +1:	popq	%rbp +	subq	$1b, %rbp + +	/* +	 * Relocate efi_config->call().  	 */ +	movq	efi_config(%rip), %rax +	addq	%rbp, 88(%rax) +2: +	movq	efi_config(%rip), %rdi +	call	efi_main +	movq	%rax,%rsi +	cmpq	$0,%rax +	jne	2f +fail: +	/* EFI init failed, so hang. */ +	hlt +	jmp	fail +2: +	movl	BP_code32_start(%esi), %eax +	leaq	preferred_addr(%rax), %rax +	jmp	*%rax + +preferred_addr: +#endif  	/* Setup data segments. */  	xorl	%eax, %eax @@ -207,9 +300,6 @@ ENTRY(startup_64)  	movl	%eax, %ss  	movl	%eax, %fs  	movl	%eax, %gs -	lldt	%ax -	movl    $0x20, %eax -	ltr	%ax  	/*  	 * Compute the decompressed kernel start address.  It is where @@ -232,9 +322,11 @@ ENTRY(startup_64)  	addq	%rax, %rbp  	notq	%rax  	andq	%rax, %rbp -#else -	movq	$LOAD_PHYSICAL_ADDR, %rbp +	cmpq	$LOAD_PHYSICAL_ADDR, %rbp +	jge	1f  #endif +	movq	$LOAD_PHYSICAL_ADDR, %rbp +1:  	/* Target address to relocate to for decompression */  	leaq	z_extract_offset(%rbp), %rbx @@ -266,6 +358,20 @@ ENTRY(startup_64)  	leaq	relocated(%rbx), %rax  	jmp	*%rax +#ifdef CONFIG_EFI_STUB +	.org 0x390 +ENTRY(efi64_stub_entry) +	movq	%rdi, efi64_config(%rip)	/* Handle */ +	movq	%rsi, efi64_config+8(%rip) /* EFI System table pointer */ + +	leaq	efi64_config(%rip), %rax +	movq	%rax, efi_config(%rip) + +	movq	%rdx, %rsi +	jmp	handover_entry +ENDPROC(efi64_stub_entry) +#endif +  	.text  relocated: @@ -301,13 +407,23 @@ relocated:  	leaq	input_data(%rip), %rdx  /* input_data */  	movl	$z_input_len, %ecx	/* input_len */  	movq	%rbp, %r8		/* output target address */ -	call	decompress_kernel +	movq	$z_output_len, %r9	/* decompressed length */ +	call	decompress_kernel	/* returns kernel location in %rax */  	popq	%rsi  /*   * Jump to the decompressed kernel.   */ -	jmp	*%rbp +	jmp	*%rax + +	.code32 +no_longmode: +	/* This isn't an x86-64 CPU so hang */ +1: +	hlt +	jmp     1b + +#include "../../kernel/verify_cpu.S"  	.data  gdt: @@ -321,6 +437,25 @@ gdt:  	.quad   0x0000000000000000	/* TS continued */  gdt_end: +#ifdef CONFIG_EFI_STUB +efi_config: +	.quad	0 + +#ifdef CONFIG_EFI_MIXED +	.global efi32_config +efi32_config: +	.fill	11,8,0 +	.quad	efi64_thunk +	.byte	0 +#endif + +	.global efi64_config +efi64_config: +	.fill	11,8,0 +	.quad	efi_call +	.byte	1 +#endif /* CONFIG_EFI_STUB */ +  /*   * Stack and heap for uncompression   */ diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index 23f315c9f21..57ab74df7ee 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -10,6 +10,7 @@   */  #include "misc.h" +#include "../string.h"  /* WARNING!!   * This code is compiled with -fPIC and it is relocated dynamically @@ -97,8 +98,14 @@   */  #define STATIC		static -#undef memset  #undef memcpy + +/* + * Use a normal definition of memset() from string.c. There are already + * included header files which expect a definition of memset() and by + * the time we define memset macro, it is too late. + */ +#undef memset  #define memzero(s, n)	memset((s), 0, (n)) @@ -108,20 +115,9 @@ static void error(char *m);   * This is set up by the setup-routine at boot-time   */  struct boot_params *real_mode;		/* Pointer to real-mode data */ -static int quiet; -static int debug; - -void *memset(void *s, int c, size_t n); -void *memcpy(void *dest, const void *src, size_t n); - -#ifdef CONFIG_X86_64 -#define memptr long -#else -#define memptr unsigned -#endif -static memptr free_mem_ptr; -static memptr free_mem_end_ptr; +memptr free_mem_ptr; +memptr free_mem_end_ptr;  static char *vidmem;  static int vidport; @@ -139,10 +135,18 @@ static int lines, cols;  #include "../../../../lib/decompress_unlzma.c"  #endif +#ifdef CONFIG_KERNEL_XZ +#include "../../../../lib/decompress_unxz.c" +#endif +  #ifdef CONFIG_KERNEL_LZO  #include "../../../../lib/decompress_unlzo.c"  #endif +#ifdef CONFIG_KERNEL_LZ4 +#include "../../../../lib/decompress_unlz4.c" +#endif +  static void scroll(void)  {  	int i; @@ -166,15 +170,11 @@ static void serial_putchar(int ch)  	outb(ch, early_serial_base + TXR);  } -void __putstr(int error, const char *s) +void __putstr(const char *s)  {  	int x, y, pos;  	char c; -#ifndef CONFIG_X86_VERBOSE_BOOTUP -	if (!error) -		return; -#endif  	if (early_serial_base) {  		const char *str = s;  		while (*str) { @@ -220,54 +220,88 @@ void __putstr(int error, const char *s)  	outb(0xff & (pos >> 1), vidport+1);  } -void *memset(void *s, int c, size_t n) +static void error(char *x)  { -	int i; -	char *ss = s; +	error_putstr("\n\n"); +	error_putstr(x); +	error_putstr("\n\n -- System halted"); -	for (i = 0; i < n; i++) -		ss[i] = c; -	return s; -} -#ifdef CONFIG_X86_32 -void *memcpy(void *dest, const void *src, size_t n) -{ -	int d0, d1, d2; -	asm volatile( -		"rep ; movsl\n\t" -		"movl %4,%%ecx\n\t" -		"rep ; movsb\n\t" -		: "=&c" (d0), "=&D" (d1), "=&S" (d2) -		: "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src) -		: "memory"); - -	return dest; -} -#else -void *memcpy(void *dest, const void *src, size_t n) -{ -	long d0, d1, d2; -	asm volatile( -		"rep ; movsq\n\t" -		"movq %4,%%rcx\n\t" -		"rep ; movsb\n\t" -		: "=&c" (d0), "=&D" (d1), "=&S" (d2) -		: "0" (n >> 3), "g" (n & 7), "1" (dest), "2" (src) -		: "memory"); - -	return dest; +	while (1) +		asm("hlt");  } -#endif -static void error(char *x) +#if CONFIG_X86_NEED_RELOCS +static void handle_relocations(void *output, unsigned long output_len)  { -	__putstr(1, "\n\n"); -	__putstr(1, x); -	__putstr(1, "\n\n -- System halted"); +	int *reloc; +	unsigned long delta, map, ptr; +	unsigned long min_addr = (unsigned long)output; +	unsigned long max_addr = min_addr + output_len; + +	/* +	 * Calculate the delta between where vmlinux was linked to load +	 * and where it was actually loaded. +	 */ +	delta = min_addr - LOAD_PHYSICAL_ADDR; +	if (!delta) { +		debug_putstr("No relocation needed... "); +		return; +	} +	debug_putstr("Performing relocations... "); + +	/* +	 * The kernel contains a table of relocation addresses. Those +	 * addresses have the final load address of the kernel in virtual +	 * memory. We are currently working in the self map. So we need to +	 * create an adjustment for kernel memory addresses to the self map. +	 * This will involve subtracting out the base address of the kernel. +	 */ +	map = delta - __START_KERNEL_map; + +	/* +	 * Process relocations: 32 bit relocations first then 64 bit after. +	 * Two sets of binary relocations are added to the end of the kernel +	 * before compression. Each relocation table entry is the kernel +	 * address of the location which needs to be updated stored as a +	 * 32-bit value which is sign extended to 64 bits. +	 * +	 * Format is: +	 * +	 * kernel bits... +	 * 0 - zero terminator for 64 bit relocations +	 * 64 bit relocation repeated +	 * 0 - zero terminator for 32 bit relocations +	 * 32 bit relocation repeated +	 * +	 * So we work backwards from the end of the decompressed image. +	 */ +	for (reloc = output + output_len - sizeof(*reloc); *reloc; reloc--) { +		int extended = *reloc; +		extended += map; + +		ptr = (unsigned long)extended; +		if (ptr < min_addr || ptr > max_addr) +			error("32-bit relocation outside of kernel!\n"); + +		*(uint32_t *)ptr += delta; +	} +#ifdef CONFIG_X86_64 +	for (reloc--; *reloc; reloc--) { +		long extended = *reloc; +		extended += map; -	while (1) -		asm("hlt"); +		ptr = (unsigned long)extended; +		if (ptr < min_addr || ptr > max_addr) +			error("64-bit relocation outside of kernel!\n"); + +		*(uint64_t *)ptr += delta; +	} +#endif  } +#else +static inline void handle_relocations(void *output, unsigned long output_len) +{ } +#endif  static void parse_elf(void *output)  { @@ -290,8 +324,7 @@ static void parse_elf(void *output)  		return;  	} -	if (!quiet) -		putstr("Parsing ELF... "); +	debug_putstr("Parsing ELF... ");  	phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum);  	if (!phdrs) @@ -317,19 +350,19 @@ static void parse_elf(void *output)  		default: /* Ignore other PT_* */ break;  		}  	} + +	free(phdrs);  } -asmlinkage void decompress_kernel(void *rmode, memptr heap, +asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,  				  unsigned char *input_data,  				  unsigned long input_len, -				  unsigned char *output) +				  unsigned char *output, +				  unsigned long output_len)  {  	real_mode = rmode; -	if (cmdline_find_option_bool("quiet")) -		quiet = 1; -	if (cmdline_find_option_bool("debug")) -		debug = 1; +	sanitize_boot_params(real_mode);  	if (real_mode->screen_info.orig_video_mode == 7) {  		vidmem = (char *) 0xb0000; @@ -343,19 +376,22 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,  	cols = real_mode->screen_info.orig_video_cols;  	console_init(); -	if (debug) -		putstr("early console in decompress_kernel\n"); +	debug_putstr("early console in decompress_kernel\n");  	free_mem_ptr     = heap;	/* Heap */  	free_mem_end_ptr = heap + BOOT_HEAP_SIZE; +	output = choose_kernel_location(input_data, input_len, +					output, output_len); + +	/* Validate memory location choices. */  	if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))  		error("Destination address inappropriately aligned");  #ifdef CONFIG_X86_64  	if (heap > 0x3fffffffffffUL)  		error("Destination address too large");  #else -	if (heap > ((-__PAGE_OFFSET-(512<<20)-1) & 0x7fffffff)) +	if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff))  		error("Destination address too large");  #endif  #ifndef CONFIG_RELOCATABLE @@ -363,11 +399,10 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,  		error("Wrong destination address");  #endif -	if (!quiet) -		putstr("\nDecompressing Linux... "); +	debug_putstr("\nDecompressing Linux... ");  	decompress(input_data, input_len, NULL, NULL, output, NULL, error);  	parse_elf(output); -	if (!quiet) -		putstr("done.\nBooting the kernel.\n"); -	return; +	handle_relocations(output, output_len); +	debug_putstr("done.\nBooting the kernel.\n"); +	return output;  } diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 3f19c81a620..24e3e569a13 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -18,22 +18,69 @@  #include <asm/page.h>  #include <asm/boot.h>  #include <asm/bootparam.h> +#include <asm/bootparam_utils.h>  #define BOOT_BOOT_H  #include "../ctype.h" +#ifdef CONFIG_X86_64 +#define memptr long +#else +#define memptr unsigned +#endif +  /* misc.c */ +extern memptr free_mem_ptr; +extern memptr free_mem_end_ptr;  extern struct boot_params *real_mode;		/* Pointer to real-mode data */ -void __putstr(int error, const char *s); -#define putstr(__x)  __putstr(0, __x) -#define puts(__x)  __putstr(0, __x) +void __putstr(const char *s); +#define error_putstr(__x)  __putstr(__x) + +#ifdef CONFIG_X86_VERBOSE_BOOTUP + +#define debug_putstr(__x)  __putstr(__x) +#else + +static inline void debug_putstr(const char *s) +{ } + +#endif + +#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE  /* cmdline.c */  int cmdline_find_option(const char *option, char *buffer, int bufsize);  int cmdline_find_option_bool(const char *option); +#endif + +#if CONFIG_RANDOMIZE_BASE +/* aslr.c */ +unsigned char *choose_kernel_location(unsigned char *input, +				      unsigned long input_size, +				      unsigned char *output, +				      unsigned long output_size); +/* cpuflags.c */ +bool has_cpuflag(int flag); +#else +static inline +unsigned char *choose_kernel_location(unsigned char *input, +				      unsigned long input_size, +				      unsigned char *output, +				      unsigned long output_size) +{ +	return output; +} +#endif + +#ifdef CONFIG_EARLY_PRINTK  /* early_serial_console.c */  extern int early_serial_base;  void console_init(void); +#else +static const int early_serial_base; +static inline void console_init(void) +{ } +#endif  #endif diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c index 5c228129d17..b669ab65bf6 100644 --- a/arch/x86/boot/compressed/mkpiggy.c +++ b/arch/x86/boot/compressed/mkpiggy.c @@ -29,25 +29,19 @@  #include <stdio.h>  #include <string.h>  #include <inttypes.h> - -static uint32_t getle32(const void *p) -{ -	const uint8_t *cp = p; - -	return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + -		((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); -} +#include <tools/le_byteshift.h>  int main(int argc, char *argv[])  {  	uint32_t olen;  	long ilen;  	unsigned long offs; -	FILE *f; +	FILE *f = NULL; +	int retval = 1;  	if (argc < 2) {  		fprintf(stderr, "Usage: %s compressed_file\n", argv[0]); -		return 1; +		goto bail;  	}  	/* Get the information for the compressed kernel image first */ @@ -55,17 +49,21 @@ int main(int argc, char *argv[])  	f = fopen(argv[1], "r");  	if (!f) {  		perror(argv[1]); -		return 1; +		goto bail;  	}  	if (fseek(f, -4L, SEEK_END)) {  		perror(argv[1]);  	} -	fread(&olen, sizeof olen, 1, f); + +	if (fread(&olen, sizeof(olen), 1, f) != 1) { +		perror(argv[1]); +		goto bail; +	} +  	ilen = ftell(f); -	olen = getle32(&olen); -	fclose(f); +	olen = get_unaligned_le32(&olen);  	/*  	 * Now we have the input (compressed) and output (uncompressed) @@ -74,7 +72,7 @@ int main(int argc, char *argv[])  	offs = (olen > ilen) ? olen - ilen : 0;  	offs += olen >> 12;	/* Add 8 bytes for each 32K block */ -	offs += 32*1024 + 18;	/* Add 32K + 18 bytes slack */ +	offs += 64*1024 + 128;	/* Add 64K + 128 bytes slack */  	offs = (offs+4095) & ~4095; /* Round to a 4K boundary */  	printf(".section \".rodata..compressed\",\"a\",@progbits\n"); @@ -93,5 +91,9 @@ int main(int argc, char *argv[])  	printf(".incbin \"%s\"\n", argv[1]);  	printf("input_data_end:\n"); -	return 0; +	retval = 0; +bail: +	if (f) +		fclose(f); +	return retval;  } diff --git a/arch/x86/boot/compressed/relocs.c b/arch/x86/boot/compressed/relocs.c deleted file mode 100644 index 89bbf4e4d05..00000000000 --- a/arch/x86/boot/compressed/relocs.c +++ /dev/null @@ -1,682 +0,0 @@ -#include <stdio.h> -#include <stdarg.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <elf.h> -#include <byteswap.h> -#define USE_BSD -#include <endian.h> -#include <regex.h> - -static void die(char *fmt, ...); - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -static Elf32_Ehdr ehdr; -static unsigned long reloc_count, reloc_idx; -static unsigned long *relocs; - -struct section { -	Elf32_Shdr     shdr; -	struct section *link; -	Elf32_Sym      *symtab; -	Elf32_Rel      *reltab; -	char           *strtab; -}; -static struct section *secs; - -/* - * Following symbols have been audited. There values are constant and do - * not change if bzImage is loaded at a different physical address than - * the address for which it has been compiled. Don't warn user about - * absolute relocations present w.r.t these symbols. - */ -static const char abs_sym_regex[] = -	"^(xen_irq_disable_direct_reloc$|" -	"xen_save_fl_direct_reloc$|" -	"VDSO|" -	"__crc_)"; -static regex_t abs_sym_regex_c; -static int is_abs_reloc(const char *sym_name) -{ -	return !regexec(&abs_sym_regex_c, sym_name, 0, NULL, 0); -} - -/* - * These symbols are known to be relative, even if the linker marks them - * as absolute (typically defined outside any section in the linker script.) - */ -static const char rel_sym_regex[] = -	"^_end$"; -static regex_t rel_sym_regex_c; -static int is_rel_reloc(const char *sym_name) -{ -	return !regexec(&rel_sym_regex_c, sym_name, 0, NULL, 0); -} - -static void regex_init(void) -{ -        char errbuf[128]; -        int err; -	 -        err = regcomp(&abs_sym_regex_c, abs_sym_regex, -                      REG_EXTENDED|REG_NOSUB); -        if (err) { -                regerror(err, &abs_sym_regex_c, errbuf, sizeof errbuf); -                die("%s", errbuf); -        } - -        err = regcomp(&rel_sym_regex_c, rel_sym_regex, -                      REG_EXTENDED|REG_NOSUB); -        if (err) { -                regerror(err, &rel_sym_regex_c, errbuf, sizeof errbuf); -                die("%s", errbuf); -        } -} - -static void die(char *fmt, ...) -{ -	va_list ap; -	va_start(ap, fmt); -	vfprintf(stderr, fmt, ap); -	va_end(ap); -	exit(1); -} - -static const char *sym_type(unsigned type) -{ -	static const char *type_name[] = { -#define SYM_TYPE(X) [X] = #X -		SYM_TYPE(STT_NOTYPE), -		SYM_TYPE(STT_OBJECT), -		SYM_TYPE(STT_FUNC), -		SYM_TYPE(STT_SECTION), -		SYM_TYPE(STT_FILE), -		SYM_TYPE(STT_COMMON), -		SYM_TYPE(STT_TLS), -#undef SYM_TYPE -	}; -	const char *name = "unknown sym type name"; -	if (type < ARRAY_SIZE(type_name)) { -		name = type_name[type]; -	} -	return name; -} - -static const char *sym_bind(unsigned bind) -{ -	static const char *bind_name[] = { -#define SYM_BIND(X) [X] = #X -		SYM_BIND(STB_LOCAL), -		SYM_BIND(STB_GLOBAL), -		SYM_BIND(STB_WEAK), -#undef SYM_BIND -	}; -	const char *name = "unknown sym bind name"; -	if (bind < ARRAY_SIZE(bind_name)) { -		name = bind_name[bind]; -	} -	return name; -} - -static const char *sym_visibility(unsigned visibility) -{ -	static const char *visibility_name[] = { -#define SYM_VISIBILITY(X) [X] = #X -		SYM_VISIBILITY(STV_DEFAULT), -		SYM_VISIBILITY(STV_INTERNAL), -		SYM_VISIBILITY(STV_HIDDEN), -		SYM_VISIBILITY(STV_PROTECTED), -#undef SYM_VISIBILITY -	}; -	const char *name = "unknown sym visibility name"; -	if (visibility < ARRAY_SIZE(visibility_name)) { -		name = visibility_name[visibility]; -	} -	return name; -} - -static const char *rel_type(unsigned type) -{ -	static const char *type_name[] = { -#define REL_TYPE(X) [X] = #X -		REL_TYPE(R_386_NONE), -		REL_TYPE(R_386_32), -		REL_TYPE(R_386_PC32), -		REL_TYPE(R_386_GOT32), -		REL_TYPE(R_386_PLT32), -		REL_TYPE(R_386_COPY), -		REL_TYPE(R_386_GLOB_DAT), -		REL_TYPE(R_386_JMP_SLOT), -		REL_TYPE(R_386_RELATIVE), -		REL_TYPE(R_386_GOTOFF), -		REL_TYPE(R_386_GOTPC), -#undef REL_TYPE -	}; -	const char *name = "unknown type rel type name"; -	if (type < ARRAY_SIZE(type_name) && type_name[type]) { -		name = type_name[type]; -	} -	return name; -} - -static const char *sec_name(unsigned shndx) -{ -	const char *sec_strtab; -	const char *name; -	sec_strtab = secs[ehdr.e_shstrndx].strtab; -	name = "<noname>"; -	if (shndx < ehdr.e_shnum) { -		name = sec_strtab + secs[shndx].shdr.sh_name; -	} -	else if (shndx == SHN_ABS) { -		name = "ABSOLUTE"; -	} -	else if (shndx == SHN_COMMON) { -		name = "COMMON"; -	} -	return name; -} - -static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym) -{ -	const char *name; -	name = "<noname>"; -	if (sym->st_name) { -		name = sym_strtab + sym->st_name; -	} -	else { -		name = sec_name(secs[sym->st_shndx].shdr.sh_name); -	} -	return name; -} - - - -#if BYTE_ORDER == LITTLE_ENDIAN -#define le16_to_cpu(val) (val) -#define le32_to_cpu(val) (val) -#endif -#if BYTE_ORDER == BIG_ENDIAN -#define le16_to_cpu(val) bswap_16(val) -#define le32_to_cpu(val) bswap_32(val) -#endif - -static uint16_t elf16_to_cpu(uint16_t val) -{ -	return le16_to_cpu(val); -} - -static uint32_t elf32_to_cpu(uint32_t val) -{ -	return le32_to_cpu(val); -} - -static void read_ehdr(FILE *fp) -{ -	if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { -		die("Cannot read ELF header: %s\n", -			strerror(errno)); -	} -	if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { -		die("No ELF magic\n"); -	} -	if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) { -		die("Not a 32 bit executable\n"); -	} -	if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { -		die("Not a LSB ELF executable\n"); -	} -	if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { -		die("Unknown ELF version\n"); -	} -	/* Convert the fields to native endian */ -	ehdr.e_type      = elf16_to_cpu(ehdr.e_type); -	ehdr.e_machine   = elf16_to_cpu(ehdr.e_machine); -	ehdr.e_version   = elf32_to_cpu(ehdr.e_version); -	ehdr.e_entry     = elf32_to_cpu(ehdr.e_entry); -	ehdr.e_phoff     = elf32_to_cpu(ehdr.e_phoff); -	ehdr.e_shoff     = elf32_to_cpu(ehdr.e_shoff); -	ehdr.e_flags     = elf32_to_cpu(ehdr.e_flags); -	ehdr.e_ehsize    = elf16_to_cpu(ehdr.e_ehsize); -	ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize); -	ehdr.e_phnum     = elf16_to_cpu(ehdr.e_phnum); -	ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize); -	ehdr.e_shnum     = elf16_to_cpu(ehdr.e_shnum); -	ehdr.e_shstrndx  = elf16_to_cpu(ehdr.e_shstrndx); - -	if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) { -		die("Unsupported ELF header type\n"); -	} -	if (ehdr.e_machine != EM_386) { -		die("Not for x86\n"); -	} -	if (ehdr.e_version != EV_CURRENT) { -		die("Unknown ELF version\n"); -	} -	if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) { -		die("Bad Elf header size\n"); -	} -	if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { -		die("Bad program header entry\n"); -	} -	if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) { -		die("Bad section header entry\n"); -	} -	if (ehdr.e_shstrndx >= ehdr.e_shnum) { -		die("String table index out of bounds\n"); -	} -} - -static void read_shdrs(FILE *fp) -{ -	int i; -	Elf32_Shdr shdr; - -	secs = calloc(ehdr.e_shnum, sizeof(struct section)); -	if (!secs) { -		die("Unable to allocate %d section headers\n", -		    ehdr.e_shnum); -	} -	if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) { -		die("Seek to %d failed: %s\n", -			ehdr.e_shoff, strerror(errno)); -	} -	for (i = 0; i < ehdr.e_shnum; i++) { -		struct section *sec = &secs[i]; -		if (fread(&shdr, sizeof shdr, 1, fp) != 1) -			die("Cannot read ELF section headers %d/%d: %s\n", -			    i, ehdr.e_shnum, strerror(errno)); -		sec->shdr.sh_name      = elf32_to_cpu(shdr.sh_name); -		sec->shdr.sh_type      = elf32_to_cpu(shdr.sh_type); -		sec->shdr.sh_flags     = elf32_to_cpu(shdr.sh_flags); -		sec->shdr.sh_addr      = elf32_to_cpu(shdr.sh_addr); -		sec->shdr.sh_offset    = elf32_to_cpu(shdr.sh_offset); -		sec->shdr.sh_size      = elf32_to_cpu(shdr.sh_size); -		sec->shdr.sh_link      = elf32_to_cpu(shdr.sh_link); -		sec->shdr.sh_info      = elf32_to_cpu(shdr.sh_info); -		sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign); -		sec->shdr.sh_entsize   = elf32_to_cpu(shdr.sh_entsize); -		if (sec->shdr.sh_link < ehdr.e_shnum) -			sec->link = &secs[sec->shdr.sh_link]; -	} - -} - -static void read_strtabs(FILE *fp) -{ -	int i; -	for (i = 0; i < ehdr.e_shnum; i++) { -		struct section *sec = &secs[i]; -		if (sec->shdr.sh_type != SHT_STRTAB) { -			continue; -		} -		sec->strtab = malloc(sec->shdr.sh_size); -		if (!sec->strtab) { -			die("malloc of %d bytes for strtab failed\n", -				sec->shdr.sh_size); -		} -		if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { -			die("Seek to %d failed: %s\n", -				sec->shdr.sh_offset, strerror(errno)); -		} -		if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) -		    != sec->shdr.sh_size) { -			die("Cannot read symbol table: %s\n", -				strerror(errno)); -		} -	} -} - -static void read_symtabs(FILE *fp) -{ -	int i,j; -	for (i = 0; i < ehdr.e_shnum; i++) { -		struct section *sec = &secs[i]; -		if (sec->shdr.sh_type != SHT_SYMTAB) { -			continue; -		} -		sec->symtab = malloc(sec->shdr.sh_size); -		if (!sec->symtab) { -			die("malloc of %d bytes for symtab failed\n", -				sec->shdr.sh_size); -		} -		if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { -			die("Seek to %d failed: %s\n", -				sec->shdr.sh_offset, strerror(errno)); -		} -		if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) -		    != sec->shdr.sh_size) { -			die("Cannot read symbol table: %s\n", -				strerror(errno)); -		} -		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { -			Elf32_Sym *sym = &sec->symtab[j]; -			sym->st_name  = elf32_to_cpu(sym->st_name); -			sym->st_value = elf32_to_cpu(sym->st_value); -			sym->st_size  = elf32_to_cpu(sym->st_size); -			sym->st_shndx = elf16_to_cpu(sym->st_shndx); -		} -	} -} - - -static void read_relocs(FILE *fp) -{ -	int i,j; -	for (i = 0; i < ehdr.e_shnum; i++) { -		struct section *sec = &secs[i]; -		if (sec->shdr.sh_type != SHT_REL) { -			continue; -		} -		sec->reltab = malloc(sec->shdr.sh_size); -		if (!sec->reltab) { -			die("malloc of %d bytes for relocs failed\n", -				sec->shdr.sh_size); -		} -		if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { -			die("Seek to %d failed: %s\n", -				sec->shdr.sh_offset, strerror(errno)); -		} -		if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) -		    != sec->shdr.sh_size) { -			die("Cannot read symbol table: %s\n", -				strerror(errno)); -		} -		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { -			Elf32_Rel *rel = &sec->reltab[j]; -			rel->r_offset = elf32_to_cpu(rel->r_offset); -			rel->r_info   = elf32_to_cpu(rel->r_info); -		} -	} -} - - -static void print_absolute_symbols(void) -{ -	int i; -	printf("Absolute symbols\n"); -	printf(" Num:    Value Size  Type       Bind        Visibility  Name\n"); -	for (i = 0; i < ehdr.e_shnum; i++) { -		struct section *sec = &secs[i]; -		char *sym_strtab; -		Elf32_Sym *sh_symtab; -		int j; - -		if (sec->shdr.sh_type != SHT_SYMTAB) { -			continue; -		} -		sh_symtab = sec->symtab; -		sym_strtab = sec->link->strtab; -		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { -			Elf32_Sym *sym; -			const char *name; -			sym = &sec->symtab[j]; -			name = sym_name(sym_strtab, sym); -			if (sym->st_shndx != SHN_ABS) { -				continue; -			} -			printf("%5d %08x %5d %10s %10s %12s %s\n", -				j, sym->st_value, sym->st_size, -				sym_type(ELF32_ST_TYPE(sym->st_info)), -				sym_bind(ELF32_ST_BIND(sym->st_info)), -				sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)), -				name); -		} -	} -	printf("\n"); -} - -static void print_absolute_relocs(void) -{ -	int i, printed = 0; - -	for (i = 0; i < ehdr.e_shnum; i++) { -		struct section *sec = &secs[i]; -		struct section *sec_applies, *sec_symtab; -		char *sym_strtab; -		Elf32_Sym *sh_symtab; -		int j; -		if (sec->shdr.sh_type != SHT_REL) { -			continue; -		} -		sec_symtab  = sec->link; -		sec_applies = &secs[sec->shdr.sh_info]; -		if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { -			continue; -		} -		sh_symtab  = sec_symtab->symtab; -		sym_strtab = sec_symtab->link->strtab; -		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { -			Elf32_Rel *rel; -			Elf32_Sym *sym; -			const char *name; -			rel = &sec->reltab[j]; -			sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; -			name = sym_name(sym_strtab, sym); -			if (sym->st_shndx != SHN_ABS) { -				continue; -			} - -			/* Absolute symbols are not relocated if bzImage is -			 * loaded at a non-compiled address. Display a warning -			 * to user at compile time about the absolute -			 * relocations present. -			 * -			 * User need to audit the code to make sure -			 * some symbols which should have been section -			 * relative have not become absolute because of some -			 * linker optimization or wrong programming usage. -			 * -			 * Before warning check if this absolute symbol -			 * relocation is harmless. -			 */ -			if (is_abs_reloc(name) || is_rel_reloc(name)) -				continue; - -			if (!printed) { -				printf("WARNING: Absolute relocations" -					" present\n"); -				printf("Offset     Info     Type     Sym.Value " -					"Sym.Name\n"); -				printed = 1; -			} - -			printf("%08x %08x %10s %08x  %s\n", -				rel->r_offset, -				rel->r_info, -				rel_type(ELF32_R_TYPE(rel->r_info)), -				sym->st_value, -				name); -		} -	} - -	if (printed) -		printf("\n"); -} - -static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) -{ -	int i; -	/* Walk through the relocations */ -	for (i = 0; i < ehdr.e_shnum; i++) { -		char *sym_strtab; -		Elf32_Sym *sh_symtab; -		struct section *sec_applies, *sec_symtab; -		int j; -		struct section *sec = &secs[i]; - -		if (sec->shdr.sh_type != SHT_REL) { -			continue; -		} -		sec_symtab  = sec->link; -		sec_applies = &secs[sec->shdr.sh_info]; -		if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { -			continue; -		} -		sh_symtab = sec_symtab->symtab; -		sym_strtab = sec_symtab->link->strtab; -		for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { -			Elf32_Rel *rel; -			Elf32_Sym *sym; -			unsigned r_type; -			rel = &sec->reltab[j]; -			sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; -			r_type = ELF32_R_TYPE(rel->r_info); -			/* Don't visit relocations to absolute symbols */ -			if (sym->st_shndx == SHN_ABS && -			    !is_rel_reloc(sym_name(sym_strtab, sym))) { -				continue; -			} -			switch (r_type) { -			case R_386_NONE: -			case R_386_PC32: -				/* -				 * NONE can be ignored and and PC relative -				 * relocations don't need to be adjusted. -				 */ -				break; -			case R_386_32: -				/* Visit relocations that need to be adjusted */ -				visit(rel, sym); -				break; -			default: -				die("Unsupported relocation type: %s (%d)\n", -				    rel_type(r_type), r_type); -				break; -			} -		} -	} -} - -static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym) -{ -	reloc_count += 1; -} - -static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) -{ -	/* Remember the address that needs to be adjusted. */ -	relocs[reloc_idx++] = rel->r_offset; -} - -static int cmp_relocs(const void *va, const void *vb) -{ -	const unsigned long *a, *b; -	a = va; b = vb; -	return (*a == *b)? 0 : (*a > *b)? 1 : -1; -} - -static void emit_relocs(int as_text) -{ -	int i; -	/* Count how many relocations I have and allocate space for them. */ -	reloc_count = 0; -	walk_relocs(count_reloc); -	relocs = malloc(reloc_count * sizeof(relocs[0])); -	if (!relocs) { -		die("malloc of %d entries for relocs failed\n", -			reloc_count); -	} -	/* Collect up the relocations */ -	reloc_idx = 0; -	walk_relocs(collect_reloc); - -	/* Order the relocations for more efficient processing */ -	qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); - -	/* Print the relocations */ -	if (as_text) { -		/* Print the relocations in a form suitable that -		 * gas will like. -		 */ -		printf(".section \".data.reloc\",\"a\"\n"); -		printf(".balign 4\n"); -		for (i = 0; i < reloc_count; i++) { -			printf("\t .long 0x%08lx\n", relocs[i]); -		} -		printf("\n"); -	} -	else { -		unsigned char buf[4]; -		/* Print a stop */ -		fwrite("\0\0\0\0", 4, 1, stdout); -		/* Now print each relocation */ -		for (i = 0; i < reloc_count; i++) { -			buf[0] = (relocs[i] >>  0) & 0xff; -			buf[1] = (relocs[i] >>  8) & 0xff; -			buf[2] = (relocs[i] >> 16) & 0xff; -			buf[3] = (relocs[i] >> 24) & 0xff; -			fwrite(buf, 4, 1, stdout); -		} -	} -} - -static void usage(void) -{ -	die("relocs [--abs-syms |--abs-relocs | --text] vmlinux\n"); -} - -int main(int argc, char **argv) -{ -	int show_absolute_syms, show_absolute_relocs; -	int as_text; -	const char *fname; -	FILE *fp; -	int i; - -	regex_init(); - -	show_absolute_syms = 0; -	show_absolute_relocs = 0; -	as_text = 0; -	fname = NULL; -	for (i = 1; i < argc; i++) { -		char *arg = argv[i]; -		if (*arg == '-') { -			if (strcmp(argv[1], "--abs-syms") == 0) { -				show_absolute_syms = 1; -				continue; -			} - -			if (strcmp(argv[1], "--abs-relocs") == 0) { -				show_absolute_relocs = 1; -				continue; -			} -			else if (strcmp(argv[1], "--text") == 0) { -				as_text = 1; -				continue; -			} -		} -		else if (!fname) { -			fname = arg; -			continue; -		} -		usage(); -	} -	if (!fname) { -		usage(); -	} -	fp = fopen(fname, "r"); -	if (!fp) { -		die("Cannot open %s: %s\n", -			fname, strerror(errno)); -	} -	read_ehdr(fp); -	read_shdrs(fp); -	read_strtabs(fp); -	read_symtabs(fp); -	read_relocs(fp); -	if (show_absolute_syms) { -		print_absolute_symbols(); -		return 0; -	} -	if (show_absolute_relocs) { -		print_absolute_relocs(); -		return 0; -	} -	emit_relocs(as_text); -	return 0; -} diff --git a/arch/x86/boot/compressed/string.c b/arch/x86/boot/compressed/string.c index 19b3e693cd7..00e788be1db 100644 --- a/arch/x86/boot/compressed/string.c +++ b/arch/x86/boot/compressed/string.c @@ -1,2 +1,41 @@ -#include "misc.h"  #include "../string.c" + +#ifdef CONFIG_X86_32 +void *memcpy(void *dest, const void *src, size_t n) +{ +	int d0, d1, d2; +	asm volatile( +		"rep ; movsl\n\t" +		"movl %4,%%ecx\n\t" +		"rep ; movsb\n\t" +		: "=&c" (d0), "=&D" (d1), "=&S" (d2) +		: "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src) +		: "memory"); + +	return dest; +} +#else +void *memcpy(void *dest, const void *src, size_t n) +{ +	long d0, d1, d2; +	asm volatile( +		"rep ; movsq\n\t" +		"movq %4,%%rcx\n\t" +		"rep ; movsb\n\t" +		: "=&c" (d0), "=&D" (d1), "=&S" (d2) +		: "0" (n >> 3), "g" (n & 7), "1" (dest), "2" (src) +		: "memory"); + +	return dest; +} +#endif + +void *memset(void *s, int c, size_t n) +{ +	int i; +	char *ss = s; + +	for (i = 0; i < n; i++) +		ss[i] = c; +	return s; +}  | 
