Skip to content

Commit 97aa276

Browse files
committed
efi/x86: Add true mixed mode entry point into .compat section
Currently, mixed mode is closely tied to the EFI handover protocol and relies on intimate knowledge of the bootparams structure, setup header etc, all of which are rather byzantine and entirely specific to x86. Even though no other EFI supported architectures are currently known that could support something like mixed mode, it still makes sense to abstract a bit from this, and make it part of a generic Linux on EFI boot protocol. To that end, add a .compat section to the mixed mode binary, and populate it with the PE machine type and entry point address, allowing firmware implementations to match it to their native machine type, and invoke non-native binaries using a secondary entry point. Signed-off-by: Ard Biesheuvel <[email protected]>
1 parent 17054f4 commit 97aa276

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

arch/x86/boot/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
8888

8989
SETUP_OBJS = $(addprefix $(obj)/,$(setup-y))
9090

91-
sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$$/\#define ZO_\2 0x\1/p'
91+
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'
9292

9393
quiet_cmd_zoffset = ZOFFSET $@
9494
cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@

arch/x86/boot/header.S

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ coff_header:
106106
#else
107107
.word 0x8664 # x86-64
108108
#endif
109-
.word 3 # nr_sections
109+
.word section_count # nr_sections
110110
.long 0 # TimeDateStamp
111111
.long 0 # PointerToSymbolTable
112112
.long 1 # NumberOfSymbols
@@ -230,6 +230,23 @@ section_table:
230230
.word 0 # NumberOfLineNumbers
231231
.long 0x42100040 # Characteristics (section flags)
232232

233+
#ifdef CONFIG_EFI_MIXED
234+
#
235+
# The offset & size fields are filled in by build.c.
236+
#
237+
.asciz ".compat"
238+
.long 0
239+
.long 0x0
240+
.long 0 # Size of initialized data
241+
# on disk
242+
.long 0x0
243+
.long 0 # PointerToRelocations
244+
.long 0 # PointerToLineNumbers
245+
.word 0 # NumberOfRelocations
246+
.word 0 # NumberOfLineNumbers
247+
.long 0x42100040 # Characteristics (section flags)
248+
#endif
249+
233250
#
234251
# The offset & size fields are filled in by build.c.
235252
#
@@ -248,6 +265,7 @@ section_table:
248265
.word 0 # NumberOfLineNumbers
249266
.long 0x60500020 # Characteristics (section flags)
250267

268+
.set section_count, (. - section_table) / 40
251269
#endif /* CONFIG_EFI_STUB */
252270

253271
# Kernel attributes; used by setup. This is part 1 of the

arch/x86/boot/tools/build.c

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,16 @@ u8 buf[SETUP_SECT_MAX*512];
5353

5454
#define PECOFF_RELOC_RESERVE 0x20
5555

56+
#ifdef CONFIG_EFI_MIXED
57+
#define PECOFF_COMPAT_RESERVE 0x20
58+
#else
59+
#define PECOFF_COMPAT_RESERVE 0x0
60+
#endif
61+
5662
unsigned long efi32_stub_entry;
5763
unsigned long efi64_stub_entry;
5864
unsigned long efi_pe_entry;
65+
unsigned long efi32_pe_entry;
5966
unsigned long kernel_info;
6067
unsigned long startup_64;
6168

@@ -189,7 +196,10 @@ static void update_pecoff_section_header(char *section_name, u32 offset, u32 siz
189196
static void update_pecoff_setup_and_reloc(unsigned int size)
190197
{
191198
u32 setup_offset = 0x200;
192-
u32 reloc_offset = size - PECOFF_RELOC_RESERVE;
199+
u32 reloc_offset = size - PECOFF_RELOC_RESERVE - PECOFF_COMPAT_RESERVE;
200+
#ifdef CONFIG_EFI_MIXED
201+
u32 compat_offset = reloc_offset + PECOFF_RELOC_RESERVE;
202+
#endif
193203
u32 setup_size = reloc_offset - setup_offset;
194204

195205
update_pecoff_section_header(".setup", setup_offset, setup_size);
@@ -201,6 +211,20 @@ static void update_pecoff_setup_and_reloc(unsigned int size)
201211
*/
202212
put_unaligned_le32(reloc_offset + 10, &buf[reloc_offset]);
203213
put_unaligned_le32(10, &buf[reloc_offset + 4]);
214+
215+
#ifdef CONFIG_EFI_MIXED
216+
update_pecoff_section_header(".compat", compat_offset, PECOFF_COMPAT_RESERVE);
217+
218+
/*
219+
* Put the IA-32 machine type (0x14c) and the associated entry point
220+
* address in the .compat section, so loaders can figure out which other
221+
* execution modes this image supports.
222+
*/
223+
buf[compat_offset] = 0x1;
224+
buf[compat_offset + 1] = 0x8;
225+
put_unaligned_le16(0x14c, &buf[compat_offset + 2]);
226+
put_unaligned_le32(efi32_pe_entry + size, &buf[compat_offset + 4]);
227+
#endif
204228
}
205229

206230
static void update_pecoff_text(unsigned int text_start, unsigned int file_sz,
@@ -212,6 +236,22 @@ static void update_pecoff_text(unsigned int text_start, unsigned int file_sz,
212236

213237
pe_header = get_unaligned_le32(&buf[0x3c]);
214238

239+
#ifdef CONFIG_EFI_MIXED
240+
/*
241+
* In mixed mode, we will execute startup_32() at whichever offset in
242+
* memory it happened to land when the PE/COFF loader loaded the image,
243+
* which may be misaligned with respect to the kernel_alignment field
244+
* in the setup header.
245+
*
246+
* In order for startup_32 to safely execute in place at this offset,
247+
* we need to ensure that the CONFIG_PHYSICAL_ALIGN aligned allocation
248+
* it creates for the page tables does not extend beyond the declared
249+
* size of the image in the PE/COFF header. So add the required slack.
250+
*/
251+
bss_sz += CONFIG_PHYSICAL_ALIGN;
252+
init_sz += CONFIG_PHYSICAL_ALIGN;
253+
#endif
254+
215255
/*
216256
* Size of code: Subtract the size of the first sector (512 bytes)
217257
* which includes the header.
@@ -279,6 +319,12 @@ static inline int reserve_pecoff_reloc_section(int c)
279319
}
280320
#endif /* CONFIG_EFI_STUB */
281321

322+
static int reserve_pecoff_compat_section(int c)
323+
{
324+
/* Reserve 0x20 bytes for .compat section */
325+
memset(buf+c, 0, PECOFF_COMPAT_RESERVE);
326+
return PECOFF_COMPAT_RESERVE;
327+
}
282328

283329
/*
284330
* Parse zoffset.h and find the entry points. We could just #include zoffset.h
@@ -311,6 +357,7 @@ static void parse_zoffset(char *fname)
311357
PARSE_ZOFS(p, efi32_stub_entry);
312358
PARSE_ZOFS(p, efi64_stub_entry);
313359
PARSE_ZOFS(p, efi_pe_entry);
360+
PARSE_ZOFS(p, efi32_pe_entry);
314361
PARSE_ZOFS(p, kernel_info);
315362
PARSE_ZOFS(p, startup_64);
316363

@@ -354,6 +401,7 @@ int main(int argc, char ** argv)
354401
die("Boot block hasn't got boot flag (0xAA55)");
355402
fclose(file);
356403

404+
c += reserve_pecoff_compat_section(c);
357405
c += reserve_pecoff_reloc_section(c);
358406

359407
/* Pad unused space with zeros */

0 commit comments

Comments
 (0)