On Tue, 6 Sept 2022 at 12:42, Evgeniy Baskov <baskov@xxxxxxxxx> wrote: > > Currently kernel image is not fully compliant PE image, so it may > fail to boot with stricter implementations of UEFI PE loaders. > > Set minimal alignments and sizes specified by PE documentation [1] > referenced by UEFI specification [2]. Align PE header to 8 bytes. > Generate '.reloc' section with 2 entries and set reloc data directory. Why? > > To make code more readable refactor tools/build.c: > - Use mmap() to access kernel image. > - Generate sections dynamically. > - Setup sections protection. Since we cannot fit every > needed section, set a part of protection flags > dynamically during initialization. This step is omitted > if CONFIG_EFI_DXE_MEM_ATTRIBUTES is not set. > If the commit log of a patch contains a bulleted list of the changes that it implements, it is a very strong indicator that it needs to be split up. Presenting this as a big ball of changes makes the life of a reviewed unnecessarily hard. > Reduce boot sector error message size since the space for the PE header > before the zero page beginning is constrained. > > Explicitly change sections permissions in efi_pe_entry in case > of incorrect EFI implementations and to reduce access rights to > compressed kernel blob. By default it is set executable due to > restriction in maximum number of sections that can fit before zero > page. > > [1] https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v83.docx > [2] https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf > > Signed-off-by: Evgeniy Baskov <baskov@xxxxxxxxx> > --- > arch/x86/boot/Makefile | 2 +- > arch/x86/boot/header.S | 110 +---- > arch/x86/boot/tools/build.c | 575 +++++++++++++++--------- > drivers/firmware/efi/libstub/x86-stub.c | 63 ++- > 4 files changed, 452 insertions(+), 298 deletions(-) > > diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile > index ffec8bb01ba8..828eb41c2603 100644 > --- a/arch/x86/boot/Makefile > +++ b/arch/x86/boot/Makefile > @@ -90,7 +90,7 @@ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE > > SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) > > -sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|efi32_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$$/\#define ZO_\2 0x\1/p' > +sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|efi32_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|_rodata\|z_.*\)$$/\#define ZO_\2 0x\1/p' > > quiet_cmd_zoffset = ZOFFSET $@ > cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@ > diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S > index f912d7770130..05a75f0a1876 100644 > --- a/arch/x86/boot/header.S > +++ b/arch/x86/boot/header.S > @@ -59,17 +59,16 @@ start2: > cld > > movw $bugger_off_msg, %si > + movw $bugger_off_msg_size, %cx > > msg_loop: > lodsb > - andb %al, %al > - jz bs_die > movb $0xe, %ah > movw $7, %bx > int $0x10 > - jmp msg_loop > + decw %cx > + jnz msg_loop > > -bs_die: > # Allow the user to press a key, then reboot > xorw %ax, %ax > int $0x16 > @@ -89,12 +88,12 @@ bs_die: > > .section ".bsdata", "a" > bugger_off_msg: > - .ascii "Use a boot loader.\r\n" > - .ascii "\n" > - .ascii "Remove disk and press any key to reboot...\r\n" > - .byte 0 > + .ascii "Use a boot loader. " > + .ascii "Press a key to reboot" > + .set bugger_off_msg_size, . - bugger_off_msg > > #ifdef CONFIG_EFI_STUB > + .align 8 > pe_header: > .long PE_MAGIC > > @@ -108,7 +107,7 @@ coff_header: > .set pe_opt_magic, PE_OPT_MAGIC_PE32PLUS > .word IMAGE_FILE_MACHINE_AMD64 > #endif > - .word section_count # nr_sections > + .word 0 # nr_sections > .long 0 # TimeDateStamp > .long 0 # PointerToSymbolTable > .long 1 # NumberOfSymbols > @@ -132,7 +131,7 @@ optional_header: > # Filled in by build.c > .long 0x0000 # AddressOfEntryPoint > > - .long 0x0200 # BaseOfCode > + .long 0x1000 # BaseOfCode > #ifdef CONFIG_X86_32 > .long 0 # data > #endif > @@ -145,8 +144,8 @@ extra_header_fields: > #else > .quad image_base # ImageBase > #endif > - .long 0x20 # SectionAlignment > - .long 0x20 # FileAlignment > + .long 0x1000 # SectionAlignment > + .long 0x200 # FileAlignment > .word 0 # MajorOperatingSystemVersion > .word 0 # MinorOperatingSystemVersion > .word LINUX_EFISTUB_MAJOR_VERSION # MajorImageVersion > @@ -189,91 +188,14 @@ extra_header_fields: > .quad 0 # CertificationTable > .quad 0 # BaseRelocationTable > > - # Section table > -section_table: > - # > - # The offset & size fields are filled in by build.c. > - # > - .ascii ".setup" > - .byte 0 > - .byte 0 > - .long 0 > - .long 0x0 # startup_{32,64} > - .long 0 # Size of initialized data > - # on disk > - .long 0x0 # startup_{32,64} > - .long 0 # PointerToRelocations > - .long 0 # PointerToLineNumbers > - .word 0 # NumberOfRelocations > - .word 0 # NumberOfLineNumbers > - .long IMAGE_SCN_CNT_CODE | \ > - IMAGE_SCN_MEM_READ | \ > - IMAGE_SCN_MEM_EXECUTE | \ > - IMAGE_SCN_ALIGN_16BYTES # Characteristics > - > - # > - # The EFI application loader requires a relocation section > - # because EFI applications must be relocatable. The .reloc > - # offset & size fields are filled in by build.c. > # > - .ascii ".reloc" > - .byte 0 > - .byte 0 > - .long 0 > - .long 0 > - .long 0 # SizeOfRawData > - .long 0 # PointerToRawData > - .long 0 # PointerToRelocations > - .long 0 # PointerToLineNumbers > - .word 0 # NumberOfRelocations > - .word 0 # NumberOfLineNumbers > - .long IMAGE_SCN_CNT_INITIALIZED_DATA | \ > - IMAGE_SCN_MEM_READ | \ > - IMAGE_SCN_MEM_DISCARDABLE | \ > - IMAGE_SCN_ALIGN_1BYTES # Characteristics > - > -#ifdef CONFIG_EFI_MIXED > - # > - # The offset & size fields are filled in by build.c. > + # Section table > + # It is generated by build.c and here we just need > + # to reserve some space for sections > # > - .asciz ".compat" > - .long 0 > - .long 0x0 > - .long 0 # Size of initialized data > - # on disk > - .long 0x0 > - .long 0 # PointerToRelocations > - .long 0 # PointerToLineNumbers > - .word 0 # NumberOfRelocations > - .word 0 # NumberOfLineNumbers > - .long IMAGE_SCN_CNT_INITIALIZED_DATA | \ > - IMAGE_SCN_MEM_READ | \ > - IMAGE_SCN_MEM_DISCARDABLE | \ > - IMAGE_SCN_ALIGN_1BYTES # Characteristics > -#endif > +section_table: > + .fill 40*5, 1, 0 > > - # > - # The offset & size fields are filled in by build.c. > - # > - .ascii ".text" > - .byte 0 > - .byte 0 > - .byte 0 > - .long 0 > - .long 0x0 # startup_{32,64} > - .long 0 # Size of initialized data > - # on disk > - .long 0x0 # startup_{32,64} > - .long 0 # PointerToRelocations > - .long 0 # PointerToLineNumbers > - .word 0 # NumberOfRelocations > - .word 0 # NumberOfLineNumbers > - .long IMAGE_SCN_CNT_CODE | \ > - IMAGE_SCN_MEM_READ | \ > - IMAGE_SCN_MEM_EXECUTE | \ > - IMAGE_SCN_ALIGN_16BYTES # Characteristics > - > - .set section_count, (. - section_table) / 40 > #endif /* CONFIG_EFI_STUB */ > > # Kernel attributes; used by setup. This is part 1 of the > diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c > index a3725ad46c5a..dc3a1efb290e 100644 > --- a/arch/x86/boot/tools/build.c > +++ b/arch/x86/boot/tools/build.c > @@ -40,6 +40,8 @@ typedef unsigned char u8; > typedef unsigned short u16; > typedef unsigned int u32; > > +#define round_up(x, n) (((x) + (n) - 1) & ~((n) - 1)) > + > #define DEFAULT_MAJOR_ROOT 0 > #define DEFAULT_MINOR_ROOT 0 > #define DEFAULT_ROOT_DEV (DEFAULT_MAJOR_ROOT << 8 | DEFAULT_MINOR_ROOT) > @@ -59,12 +61,74 @@ u8 buf[SETUP_SECT_MAX*512]; > #define PECOFF_COMPAT_RESERVE 0x0 > #endif > > +#define PARAGRAPH_SIZE 16 > +#define SECTOR_SIZE 512 > +#define FILE_ALIGNMENT 512 > +#define SECTION_ALIGNMENT 4096 > + > +#define RELOC_SECTION_SIZE 12 > + > +#ifdef CONFIG_EFI_MIXED > +#define COMPAT_SECTION_SIZE 8 > +#else > +#define COMPAT_SECTION_SIZE 0 > +#endif > + > +#define DOS_PECOFF_HEADER_OFFSET 0x3c > + > +#define PECOFF_CODE_SIZE_OFFSET 0x1c > +#define PECOFF_DATA_SIZE_OFFSET 0x20 > +#define PECOFF_IMAGE_SIZE_OFFSET 0x50 > +#define PECOFF_ENTRY_POINT_OFFSET 0x28 > +#define PECOFF_SECTIONS_COUNT_OFFSET 0x6 > +#define PECOFF_BASE_OF_CODE_OFFSET 0x2c > + > +#ifdef CONFIG_X86_32 > +#define PECOFF_SECTION_TABLE_OFFSET 0xa8 > +#define PECOFF_RELOC_DIR_OFFSET 0xa0 > +#else > +#define PECOFF_SECTION_TABLE_OFFSET 0xb8 > +#define PECOFF_RELOC_DIR_OFFSET 0xb0 > +#endif > + > +#define PECOFF_SECTION_SIZE 0x28 > + > +#define PECOFF_SCN_NAME_OFFSET 0x0 > +#define PECOFF_SCN_NAME_SIZE 8 > +#define PECOFF_SCN_MEMSZ_OFFSET 0x8 > +#define PECOFF_SCN_VADDR_OFFSET 0xc > +#define PECOFF_SCN_FILESZ_OFFSET 0x10 > +#define PECOFF_SCN_OFFSET_OFFSET 0x14 > +#define PECOFF_SCN_FLAGS_OFFSET 0x24 > + > +#define IMAGE_SCN_CNT_CODE 0x00000020 /* .text */ > +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */ > +#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000 > +#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 > +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */ > +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 /* can be executed as code */ > +#define IMAGE_SCN_MEM_READ 0x40000000 /* readable */ > +#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */ > + All of those defines need to go into a header file, probably include/linux/pe.h > +#ifdef CONFIG_EFI_DXE_MEM_ATTRIBUTES > +#define SCN_RW (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_4096BYTES) > +#define SCN_RX (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_ALIGN_4096BYTES) > +#define SCN_RO (IMAGE_SCN_MEM_READ | IMAGE_SCN_ALIGN_4096BYTES) > +#else > +/* With memory protection disabled all sections are RWX */ > +#define SCN_RW (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | \ > + IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_ALIGN_4096BYTES) > +#define SCN_RX SCN_RW > +#define SCN_RO SCN_RW > +#endif > + > static unsigned long efi32_stub_entry; > static unsigned long efi64_stub_entry; > static unsigned long efi_pe_entry; > static unsigned long efi32_pe_entry; > static unsigned long kernel_info; > static unsigned long startup_64; > +static unsigned long _rodata; > static unsigned long _ehead; > static unsigned long _end; > > @@ -152,91 +216,126 @@ static void usage(void) > die("Usage: build setup system zoffset.h image"); > } > > -#ifdef CONFIG_EFI_STUB > - > -static void update_pecoff_section_header_fields(char *section_name, u32 vma, u32 size, u32 datasz, u32 offset) > +static void *map_file(const char *path, size_t *psize) > { > - unsigned int pe_header; > - unsigned short num_sections; > - u8 *section; > - > - pe_header = get_unaligned_le32(&buf[0x3c]); > - num_sections = get_unaligned_le16(&buf[pe_header + 6]); > - > -#ifdef CONFIG_X86_32 > - section = &buf[pe_header + 0xa8]; > -#else > - section = &buf[pe_header + 0xb8]; > -#endif > - > - while (num_sections > 0) { > - if (strncmp((char*)section, section_name, 8) == 0) { > - /* section header size field */ > - put_unaligned_le32(size, section + 0x8); > + struct stat statbuf; > + size_t size; > + void *addr; > + int fd; > > - /* section header vma field */ > - put_unaligned_le32(vma, section + 0xc); > + fd = open(path, O_RDONLY); > + if (fd < 0) > + die("Unable to open `%s': %m", path); > + if (fstat(fd, &statbuf)) > + die("Unable to stat `%s': %m", path); > > - /* section header 'size of initialised data' field */ > - put_unaligned_le32(datasz, section + 0x10); > + size = statbuf.st_size; > + /* > + * Map one byte more, to allow adding null-terminator > + * for text files. > + */ > + addr = mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); > + if (addr == MAP_FAILED) > + die("Unable to mmap '%s': %m", path); > > - /* section header 'file offset' field */ > - put_unaligned_le32(offset, section + 0x14); > + close(fd); > > - break; > - } > - section += 0x28; > - num_sections--; > - } > + *psize = size; > + return addr; > } > > -static void update_pecoff_section_header(char *section_name, u32 offset, u32 size) > +static void unmap_file(void *addr, size_t size) > { > - update_pecoff_section_header_fields(section_name, offset, size, size, offset); > + munmap(addr, size + 1); > } > > -static void update_pecoff_setup_and_reloc(unsigned int size) > +static void *map_output_file(const char *path, size_t size) > { > - u32 setup_offset = 0x200; > - u32 reloc_offset = size - PECOFF_RELOC_RESERVE - PECOFF_COMPAT_RESERVE; > -#ifdef CONFIG_EFI_MIXED > - u32 compat_offset = reloc_offset + PECOFF_RELOC_RESERVE; > -#endif > - u32 setup_size = reloc_offset - setup_offset; > + void *addr; > + int fd; > > - update_pecoff_section_header(".setup", setup_offset, setup_size); > - update_pecoff_section_header(".reloc", reloc_offset, PECOFF_RELOC_RESERVE); > + fd = open(path, O_RDWR | O_CREAT, 0660); > + if (fd < 0) > + die("Unable to create `%s': %m", path); > > - /* > - * Modify .reloc section contents with a single entry. The > - * relocation is applied to offset 10 of the relocation section. > - */ > - put_unaligned_le32(reloc_offset + 10, &buf[reloc_offset]); > - put_unaligned_le32(10, &buf[reloc_offset + 4]); > + if (ftruncate(fd, size)) > + die("Unable to resize `%s': %m", path); > > -#ifdef CONFIG_EFI_MIXED > - update_pecoff_section_header(".compat", compat_offset, PECOFF_COMPAT_RESERVE); > + addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); > + if (addr == MAP_FAILED) > + die("Unable to mmap '%s': %m", path); > > - /* > - * Put the IA-32 machine type (0x14c) and the associated entry point > - * address in the .compat section, so loaders can figure out which other > - * execution modes this image supports. > - */ > - buf[compat_offset] = 0x1; > - buf[compat_offset + 1] = 0x8; > - put_unaligned_le16(0x14c, &buf[compat_offset + 2]); > - put_unaligned_le32(efi32_pe_entry + size, &buf[compat_offset + 4]); > -#endif > + return addr; > } > > -static void update_pecoff_text(unsigned int text_start, unsigned int file_sz, > - unsigned int init_sz) > +#ifdef CONFIG_EFI_STUB > + > +static unsigned int reloc_offset; > +static unsigned int compat_offset; > + > +#define MAX_SECTIONS 5 > + > +static void emit_pecoff_section(const char *section_name, unsigned int size, > + unsigned int bss, unsigned int *file_offset, > + unsigned int *mem_offset, u32 flags) > { > + unsigned int section_memsz, section_filesz; > unsigned int pe_header; > - unsigned int text_sz = file_sz - text_start; > - unsigned int bss_sz = init_sz - file_sz; > + unsigned short num_sections; > + u8 *pnum_sections; > + u8 *section; > + > + pe_header = get_unaligned_le32(&buf[DOS_PECOFF_HEADER_OFFSET]); > + pnum_sections = &buf[pe_header + PECOFF_SECTIONS_COUNT_OFFSET]; > + num_sections = get_unaligned_le16(pnum_sections); > + if (num_sections >= MAX_SECTIONS) > + die("Not enough space to generate all sections"); > + > + section = &buf[pe_header + PECOFF_SECTION_TABLE_OFFSET]; > + section += PECOFF_SECTION_SIZE * num_sections; > + > + if ((size & (FILE_ALIGNMENT - 1)) || (bss & (FILE_ALIGNMENT - 1))) > + die("Section '%s' is improperly aligned", section_name); > + > + section_memsz = round_up(size + bss, SECTION_ALIGNMENT); > + section_filesz = round_up(size, FILE_ALIGNMENT); > + > + /* Zero out all section fields */ > + memset(section, 0, PECOFF_SECTION_SIZE); > + > + /* Section header size field */ > + strncpy((char *)(section + PECOFF_SCN_NAME_OFFSET), > + section_name, PECOFF_SCN_NAME_SIZE); > > - pe_header = get_unaligned_le32(&buf[0x3c]); > + put_unaligned_le32(section_memsz, section + PECOFF_SCN_MEMSZ_OFFSET); > + put_unaligned_le32(*mem_offset, section + PECOFF_SCN_VADDR_OFFSET); > + put_unaligned_le32(section_filesz, section + PECOFF_SCN_FILESZ_OFFSET); > + put_unaligned_le32(*file_offset, section + PECOFF_SCN_OFFSET_OFFSET); > + put_unaligned_le32(flags, section + PECOFF_SCN_FLAGS_OFFSET); > + > + put_unaligned_le16(num_sections + 1, pnum_sections); > + > + *mem_offset += section_memsz; > + *file_offset += section_filesz; > + > +} > + > +#define BASE_RVA 0x1000 > + > +static unsigned int update_pecoff_sections(unsigned int setup_size, > + unsigned int file_size, > + unsigned int init_size, > + unsigned int text_size) > +{ > + /* First section starts at 512 byes, after PE header */ > + unsigned int mem_offset = BASE_RVA, file_offset = SECTOR_SIZE; > + unsigned int compat_size, reloc_size, image_size, text_rva; > + unsigned int pe_header, bss_size, text_rva_diff, reloc_rva; > + > + pe_header = get_unaligned_le32(&buf[DOS_PECOFF_HEADER_OFFSET]); > + > + if (get_unaligned_le32(&buf[pe_header + PECOFF_SECTIONS_COUNT_OFFSET])) > + die("Some sections present in PE file"); > > /* > * The PE/COFF loader may load the image at an address which is > @@ -247,42 +346,121 @@ static void update_pecoff_text(unsigned int text_start, unsigned int file_sz, > * add slack to allow the buffer to be aligned within the declared size > * of the image. > */ > - bss_sz += CONFIG_PHYSICAL_ALIGN; > - init_sz += CONFIG_PHYSICAL_ALIGN; > + init_size += CONFIG_PHYSICAL_ALIGN; > + image_size = init_size; > + > + reloc_size = round_up(RELOC_SECTION_SIZE, FILE_ALIGNMENT); > + compat_size = round_up(COMPAT_SECTION_SIZE, FILE_ALIGNMENT); > + > + /* > + * Let's remove extra memory used by special sections > + * and use it as a part of bss. > + */ > + init_size -= round_up(reloc_size, SECTION_ALIGNMENT); > + init_size -= round_up(compat_size, SECTION_ALIGNMENT); > + if (init_size < file_size + setup_size) { > + init_size = file_size + setup_size; > + image_size += round_up(reloc_size, SECTION_ALIGNMENT); > + image_size += round_up(compat_size, SECTION_ALIGNMENT); > + } > > /* > - * Size of code: Subtract the size of the first sector (512 bytes) > - * which includes the header. > + * Update sections offsets. > + * NOTE: Order is important > */ > - put_unaligned_le32(file_sz - 512 + bss_sz, &buf[pe_header + 0x1c]); > > - /* Size of image */ > - put_unaligned_le32(init_sz, &buf[pe_header + 0x50]); > + bss_size = init_size - file_size - setup_size; > + > + emit_pecoff_section(".setup", setup_size - SECTOR_SIZE, 0, > + &file_offset, &mem_offset, SCN_RO | > + IMAGE_SCN_CNT_INITIALIZED_DATA); > + > + text_rva_diff = mem_offset - file_offset; > + text_rva = mem_offset; > + emit_pecoff_section(".text", text_size, 0, > + &file_offset, &mem_offset, SCN_RX | > + IMAGE_SCN_CNT_CODE); > + > + /* Check that kernel sections mapping is contiguous */ > + if (text_rva_diff != mem_offset - file_offset) > + die("Kernel sections mapping is wrong: %#x != %#x", > + mem_offset - file_offset, text_rva_diff); > + > + emit_pecoff_section(".data", file_size - text_size, bss_size, > + &file_offset, &mem_offset, SCN_RW | > + IMAGE_SCN_CNT_INITIALIZED_DATA); > + > + reloc_offset = file_offset; > + reloc_rva = mem_offset; > + emit_pecoff_section(".reloc", reloc_size, 0, > + &file_offset, &mem_offset, SCN_RW | > + IMAGE_SCN_CNT_INITIALIZED_DATA | > + IMAGE_SCN_MEM_DISCARDABLE); > + > + compat_offset = file_offset; > +#ifdef CONFIG_EFI_MIXED > + emit_pecoff_section(".comat", compat_size, 0, > + &file_offset, &mem_offset, SCN_RW | > + IMAGE_SCN_CNT_INITIALIZED_DATA | > + IMAGE_SCN_MEM_DISCARDABLE); > +#endif > + > + if (file_size + setup_size + reloc_size + compat_size != file_offset) > + die("file_size(%#x) != filesz(%#x)", > + file_size + setup_size + reloc_size + compat_size, file_offset); > + > + /* Size of code. */ > + put_unaligned_le32(round_up(text_size, SECTION_ALIGNMENT), > + &buf[pe_header + PECOFF_CODE_SIZE_OFFSET]); > + /* > + * Size of data. > + * Exclude text size and first sector, which contains PE header. > + */ > + put_unaligned_le32(mem_offset - round_up(text_size, SECTION_ALIGNMENT), > + &buf[pe_header + PECOFF_DATA_SIZE_OFFSET]); > + > + /* Size of image. */ > + put_unaligned_le32(mem_offset, &buf[pe_header + PECOFF_IMAGE_SIZE_OFFSET]); > > /* > * Address of entry point for PE/COFF executable > */ > - put_unaligned_le32(text_start + efi_pe_entry, &buf[pe_header + 0x28]); > + put_unaligned_le32(text_rva + efi_pe_entry, &buf[pe_header + PECOFF_ENTRY_POINT_OFFSET]); > > - update_pecoff_section_header_fields(".text", text_start, text_sz + bss_sz, > - text_sz, text_start); > -} > + /* > + * BaseOfCode for PE/COFF executable > + */ > + put_unaligned_le32(text_rva, &buf[pe_header + PECOFF_BASE_OF_CODE_OFFSET]); > > -static int reserve_pecoff_reloc_section(int c) > -{ > - /* Reserve 0x20 bytes for .reloc section */ > - memset(buf+c, 0, PECOFF_RELOC_RESERVE); > - return PECOFF_RELOC_RESERVE; > + /* > + * Since we have generated .reloc section, we need to > + * fill-in Reloc directory > + */ > + put_unaligned_le32(reloc_rva, &buf[pe_header + PECOFF_RELOC_DIR_OFFSET]); > + put_unaligned_le32(RELOC_SECTION_SIZE, &buf[pe_header + PECOFF_RELOC_DIR_OFFSET + 4]); > + > + return file_offset; > } > > -static void efi_stub_defaults(void) > +static void generate_pecoff_section_data(u8 *output, unsigned int setup_size) > { > - /* Defaults for old kernel */ > -#ifdef CONFIG_X86_32 > - efi_pe_entry = 0x10; > -#else > - efi_pe_entry = 0x210; > - startup_64 = 0x200; > + /* > + * Modify .reloc section contents with a two entries. The > + * relocation is applied to offset 10 of the relocation section. > + */ > + put_unaligned_le32(reloc_offset + RELOC_SECTION_SIZE, &output[reloc_offset]); > + put_unaligned_le32(RELOC_SECTION_SIZE, &output[reloc_offset + 4]); > + > +#ifdef CONFIG_EFI_MIXED > + /* > + * Put the IA-32 machine type (0x14c) and the associated entry point > + * address in the .compat section, so loaders can figure out which other > + * execution modes this image supports. > + */ > + output[compat_offset] = 0x1; > + output[compat_offset + 1] = 0x8; > + put_unaligned_le16(0x14c, &output[compat_offset + 2]); > + put_unaligned_le32(efi32_pe_entry + setup_size, &output[compat_offset + 4]); > #endif > } > > @@ -297,33 +475,27 @@ static void efi_stub_entry_update(void) > > #ifdef CONFIG_EFI_MIXED > if (efi32_stub_entry != addr) > - die("32-bit and 64-bit EFI entry points do not match\n"); > + die("32-bit and 64-bit EFI entry points do not match"); > #endif > put_unaligned_le32(addr, &buf[0x264]); > } > > +static void efi_stub_update_defaults(void) > +{ > + /* Defaults for old kernel */ > +#ifdef CONFIG_X86_32 > + efi_pe_entry = 0x10; > +#else > + efi_pe_entry = 0x210; > + startup_64 = 0x200; > +#endif > +} > #else > > -static inline void update_pecoff_setup_and_reloc(unsigned int size) {} > -static inline void update_pecoff_text(unsigned int text_start, > - unsigned int file_sz, > - unsigned int init_sz) {} > -static inline void efi_stub_defaults(void) {} > -static inline void efi_stub_entry_update(void) {} > +static void efi_stub_update_defaults(void) {} > > -static inline int reserve_pecoff_reloc_section(int c) > -{ > - return 0; > -} > #endif /* CONFIG_EFI_STUB */ > > -static int reserve_pecoff_compat_section(int c) > -{ > - /* Reserve 0x20 bytes for .compat section */ > - memset(buf+c, 0, PECOFF_COMPAT_RESERVE); > - return PECOFF_COMPAT_RESERVE; > -} > - > /* > * Parse zoffset.h and find the entry points. We could just #include zoffset.h > * but that would mean tools/build would have to be rebuilt every time. It's > @@ -336,20 +508,15 @@ static int reserve_pecoff_compat_section(int c) > > static void parse_zoffset(char *fname) > { > - FILE *file; > - char *p; > - int c; > + size_t size; > + char *data, *p; > > - file = fopen(fname, "r"); > - if (!file) > - die("Unable to open `%s': %m", fname); > - c = fread(buf, 1, sizeof(buf) - 1, file); > - if (ferror(file)) > - die("read-error on `zoffset.h'"); > - fclose(file); > - buf[c] = 0; > + data = map_file(fname, &size); > + > + /* We can do that, since we mapped one byte more */ > + data[size] = 0; > > - p = (char *)buf; > + p = (char *)data; > > while (p && *p) { > PARSE_ZOFS(p, efi32_stub_entry); > @@ -358,6 +525,7 @@ static void parse_zoffset(char *fname) > PARSE_ZOFS(p, efi32_pe_entry); > PARSE_ZOFS(p, kernel_info); > PARSE_ZOFS(p, startup_64); > + PARSE_ZOFS(p, _rodata); > PARSE_ZOFS(p, _ehead); > PARSE_ZOFS(p, _end); > > @@ -365,82 +533,93 @@ static void parse_zoffset(char *fname) > while (p && (*p == '\r' || *p == '\n')) > p++; > } > + > + unmap_file(data, size); > } > > -int main(int argc, char ** argv) > +static unsigned int read_setup(char *path) > { > - unsigned int i, sz, setup_sectors, init_sz; > - int c; > - u32 sys_size; > - struct stat sb; > - FILE *file, *dest; > - int fd; > - void *kernel; > - u32 crc = 0xffffffffUL; > - > - efi_stub_defaults(); > - > - if (argc != 5) > - usage(); > - parse_zoffset(argv[3]); > - > - dest = fopen(argv[4], "w"); > - if (!dest) > - die("Unable to write `%s': %m", argv[4]); > + FILE *file; > + unsigned int setup_size, file_size; > > /* Copy the setup code */ > - file = fopen(argv[1], "r"); > + file = fopen(path, "r"); > if (!file) > - die("Unable to open `%s': %m", argv[1]); > - c = fread(buf, 1, sizeof(buf), file); > + die("Unable to open `%s': %m", path); > + > + file_size = fread(buf, 1, sizeof(buf), file); > if (ferror(file)) > die("read-error on `setup'"); > - if (c < 1024) > + > + if (file_size < 2 * SECTOR_SIZE) > die("The setup must be at least 1024 bytes"); > - if (get_unaligned_le16(&buf[510]) != 0xAA55) > + > + if (get_unaligned_le16(&buf[SECTOR_SIZE - 2]) != 0xAA55) > die("Boot block hasn't got boot flag (0xAA55)"); > - fclose(file); > > - c += reserve_pecoff_compat_section(c); > - c += reserve_pecoff_reloc_section(c); > + fclose(file); > > /* Pad unused space with zeros */ > - setup_sectors = (c + 511) / 512; > - if (setup_sectors < SETUP_SECT_MIN) > - setup_sectors = SETUP_SECT_MIN; > - i = setup_sectors*512; > - memset(buf+c, 0, i-c); > + setup_size = round_up(file_size, SECTOR_SIZE); > + > + if (setup_size < SETUP_SECT_MIN * SECTOR_SIZE) > + setup_size = SETUP_SECT_MIN * SECTOR_SIZE; > > - update_pecoff_setup_and_reloc(i); > + /* > + * Global buffer is already initialised > + * to 0, but just in case, zero out padding. > + */ > + > + memset(buf + file_size, 0, setup_size - file_size); > + > + return setup_size; > +} > + > +int main(int argc, char **argv) > +{ > + size_t kern_file_size; > + unsigned int setup_size; > + unsigned int setup_sectors; > + unsigned int init_size; > + unsigned int total_size; > + unsigned int kern_size; > + void *kernel; > + u32 crc = 0xffffffffUL; > + u8 *output; > + > + if (argc != 5) > + usage(); > + > + efi_stub_update_defaults(); > + parse_zoffset(argv[3]); > + > + setup_size = read_setup(argv[1]); > + > + setup_sectors = setup_size/SECTOR_SIZE; > > /* Set the default root device */ > put_unaligned_le16(DEFAULT_ROOT_DEV, &buf[508]); > > - /* Open and stat the kernel file */ > - fd = open(argv[2], O_RDONLY); > - if (fd < 0) > - die("Unable to open `%s': %m", argv[2]); > - if (fstat(fd, &sb)) > - die("Unable to stat `%s': %m", argv[2]); > - sz = sb.st_size; > - kernel = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); > - if (kernel == MAP_FAILED) > - die("Unable to mmap '%s': %m", argv[2]); > - /* Number of 16-byte paragraphs, including space for a 4-byte CRC */ > - sys_size = (sz + 15 + 4) / 16; > + /* Map kernel file to memory */ > + kernel = map_file(argv[2], &kern_file_size); > + > #ifdef CONFIG_EFI_STUB > - /* > - * COFF requires minimum 32-byte alignment of sections, and > - * adding a signature is problematic without that alignment. > - */ > - sys_size = (sys_size + 1) & ~1; > + /* PE specification require 512-byte minimum section file alignment */ > + kern_size = round_up(kern_file_size + 4, SECTOR_SIZE); > +#else > + /* Number of 16-byte paragraphs, including space for a 4-byte CRC */ > + kern_size = round_up(kern_file_size + 4, PARAGRAPH_SIZE); > #endif > > /* Patch the setup code with the appropriate size parameters */ > - buf[0x1f1] = setup_sectors-1; > - put_unaligned_le32(sys_size, &buf[0x1f4]); > + buf[0x1f1] = setup_sectors - 1; > + put_unaligned_le32(kern_size/PARAGRAPH_SIZE, &buf[0x1f4]); > + > + /* Update kernel_info offset. */ > + put_unaligned_le32(kernel_info, &buf[0x268]); > + > + init_size = get_unaligned_le32(&buf[0x260]); > > - init_sz = get_unaligned_le32(&buf[0x260]); > #ifdef CONFIG_EFI_STUB > /* > * The decompression buffer will start at ImageBase. When relocating > @@ -456,45 +635,39 @@ int main(int argc, char ** argv) > * For future-proofing, increase init_sz if necessary. > */ > > - if (init_sz - _end < i + _ehead) { > - init_sz = (i + _ehead + _end + 4095) & ~4095; > - put_unaligned_le32(init_sz, &buf[0x260]); > + if (init_size - _end < setup_size + _ehead) { > + init_size = round_up(setup_size + _ehead + _end, SECTION_ALIGNMENT); > + put_unaligned_le32(init_size, &buf[0x260]); > } > -#endif > - update_pecoff_text(setup_sectors * 512, i + (sys_size * 16), init_sz); > > - efi_stub_entry_update(); > + total_size = update_pecoff_sections(setup_size, kern_size, init_size, _rodata); > > - /* Update kernel_info offset. */ > - put_unaligned_le32(kernel_info, &buf[0x268]); > + efi_stub_entry_update(); > +#else > + (void)init_size; > + total_size = setup_size + kern_size; > +#endif > > - crc = partial_crc32(buf, i, crc); > - if (fwrite(buf, 1, i, dest) != i) > - die("Writing setup failed"); > + output = map_output_file(argv[4], total_size); > > - /* Copy the kernel code */ > - crc = partial_crc32(kernel, sz, crc); > - if (fwrite(kernel, 1, sz, dest) != sz) > - die("Writing kernel failed"); > + memcpy(output, buf, setup_size); > + memcpy(output + setup_size, kernel, kern_file_size); > + memset(output + setup_size + kern_file_size, 0, kern_size - kern_file_size); > > - /* Add padding leaving 4 bytes for the checksum */ > - while (sz++ < (sys_size*16) - 4) { > - crc = partial_crc32_one('\0', crc); > - if (fwrite("\0", 1, 1, dest) != 1) > - die("Writing padding failed"); > - } > +#ifdef CONFIG_EFI_STUB > + generate_pecoff_section_data(output, setup_size); > +#endif > > - /* Write the CRC */ > - put_unaligned_le32(crc, buf); > - if (fwrite(buf, 1, 4, dest) != 4) > - die("Writing CRC failed"); > + /* Calculate and write kernel checksum. */ > + crc = partial_crc32(output, total_size - 4, crc); > + put_unaligned_le32(crc, &output[total_size - 4]); > > - /* Catch any delayed write failures */ > - if (fclose(dest)) > - die("Writing image failed"); > + /* Catch any delayed write failures. */ > + if (munmap(output, total_size) < 0) > + die("Writing kernel failed"); > > - close(fd); > + unmap_file(kernel, kern_file_size); > > - /* Everything is OK */ > + /* Everything is OK. */ > return 0; > } > diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c > index 680184034cb7..914106d547a6 100644 > --- a/drivers/firmware/efi/libstub/x86-stub.c > +++ b/drivers/firmware/efi/libstub/x86-stub.c > @@ -392,6 +392,60 @@ static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status) > asm("hlt"); > } > > + > +/* > + * Manually setup memory protection attributes for each ELF section > + * since we cannot do it properly by using PE sections. > + */ > +static void setup_sections_memory_protection(void *image_base, > + unsigned long init_size) > +{ > +#ifdef CONFIG_EFI_DXE_MEM_ATTRIBUTES > + efi_dxe_table = get_efi_config_table(EFI_DXE_SERVICES_TABLE_GUID); > + > + if (!efi_dxe_table || > + efi_dxe_table->hdr.signature != EFI_DXE_SERVICES_TABLE_SIGNATURE) { > + efi_warn("Unable to locate EFI DXE services table\n"); > + efi_dxe_table = NULL; > + return; > + } > + > + extern char _head[], _ehead[]; > + extern char _compressed[], _ecompressed[]; > + extern char _text[], _etext[]; > + extern char _rodata[], _erodata[]; > + extern char _data[]; > + > + /* .setup [image_base, _head] */ > + efi_adjust_memory_range_protection((unsigned long)image_base, > + (unsigned long)_head - (unsigned long)image_base, > + EFI_MEMORY_RO | EFI_MEMORY_XP); > + /* .head.text [_head, _ehead] */ > + efi_adjust_memory_range_protection((unsigned long)_head, > + (unsigned long)_ehead - (unsigned long)_head, > + EFI_MEMORY_RO); > + /* .rodata..compressed [_compressed, _ecompressed] */ > + efi_adjust_memory_range_protection((unsigned long)_compressed, > + (unsigned long)_ecompressed - (unsigned long)_compressed, > + EFI_MEMORY_RO | EFI_MEMORY_XP); > + /* .text [_text, _etext] */ > + efi_adjust_memory_range_protection((unsigned long)_text, > + (unsigned long)_etext - (unsigned long)_text, > + EFI_MEMORY_RO); > + /* .rodata [_rodata, _erodata] */ > + efi_adjust_memory_range_protection((unsigned long)_rodata, > + (unsigned long)_erodata - (unsigned long)_rodata, > + EFI_MEMORY_RO | EFI_MEMORY_XP); > + /* .data, .bss [_data, image_base + init_size] */ > + efi_adjust_memory_range_protection((unsigned long)_data, > + (unsigned long)image_base + init_size - (unsigned long)_rodata, > + EFI_MEMORY_XP); > +#else > + (void)image_base; > + (void)init_size; > +#endif > +} > + > void __noreturn efi_stub_entry(efi_handle_t handle, > efi_system_table_t *sys_table_arg, > struct boot_params *boot_params); > @@ -438,10 +492,15 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, > > hdr = &boot_params->hdr; > > - /* Copy the setup header from the second sector to boot_params */ > - memcpy(&hdr->jump, image_base + 512, > + /* > + * Copy the setup header from the second sector > + * (mapped to image_base + 0x1000) to boot_params > + */ > + memcpy(&hdr->jump, image_base + 0x1000, > sizeof(struct setup_header) - offsetof(struct setup_header, jump)); > > + setup_sections_memory_protection(image_base, hdr->init_size); > + > /* > * Fill out some of the header fields ourselves because the > * EFI firmware loader doesn't load the first sector. > -- > 2.35.1 >