aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2014-01-10 10:54:31 -0500
committerMatt Fleming <matt.fleming@intel.com>2014-03-04 16:25:06 -0500
commitb8ff87a6158886771677e6dc8139bac6e3cba717 (patch)
treec781f609a9949236ae88a302d330e5d319ea9e1a
parentc116e8d60adabfd545a269fccab85e77febc1643 (diff)
x86/efi: Firmware agnostic handover entry points
The EFI handover code only works if the "bitness" of the firmware and the kernel match, i.e. 64-bit firmware and 64-bit kernel - it is not possible to mix the two. This goes against the tradition that a 32-bit kernel can be loaded on a 64-bit BIOS platform without having to do anything special in the boot loader. Linux distributions, for one thing, regularly run only 32-bit kernels on their live media. Despite having only one 'handover_offset' field in the kernel header, EFI boot loaders use two separate entry points to enter the kernel based on the architecture the boot loader was compiled for, (1) 32-bit loader: handover_offset (2) 64-bit loader: handover_offset + 512 Since we already have two entry points, we can leverage them to infer the bitness of the firmware we're running on, without requiring any boot loader modifications, by making (1) and (2) valid entry points for both CONFIG_X86_32 and CONFIG_X86_64 kernels. To be clear, a 32-bit boot loader will always use (1) and a 64-bit boot loader will always use (2). It's just that, if a single kernel image supports (1) and (2) that image can be used with both 32-bit and 64-bit boot loaders, and hence both 32-bit and 64-bit EFI. (1) and (2) must be 512 bytes apart at all times, but that is already part of the boot ABI and we could never change that delta without breaking existing boot loaders anyhow. Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--arch/x86/boot/Makefile2
-rw-r--r--arch/x86/boot/compressed/eboot.c9
-rw-r--r--arch/x86/boot/compressed/head_32.S2
-rw-r--r--arch/x86/boot/compressed/head_64.S62
-rw-r--r--arch/x86/boot/tools/build.c22
-rw-r--r--arch/x86/include/asm/efi.h6
6 files changed, 80 insertions, 23 deletions
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 878df7e88cd4..abb9eba61b50 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -80,7 +80,7 @@ targets += voffset.h
80$(obj)/voffset.h: vmlinux FORCE 80$(obj)/voffset.h: vmlinux FORCE
81 $(call if_changed,voffset) 81 $(call if_changed,voffset)
82 82
83sed-zoffset := -e 's/^\([0-9a-fA-F]*\) . \(startup_32\|startup_64\|efi_pe_entry\|efi_stub_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p' 83sed-zoffset := -e 's/^\([0-9a-fA-F]*\) . \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p'
84 84
85quiet_cmd_zoffset = ZOFFSET $@ 85quiet_cmd_zoffset = ZOFFSET $@
86 cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@ 86 cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index ab1f3a2f1e1e..5e1ba4fa3f79 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -1256,12 +1256,13 @@ static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
1256} 1256}
1257 1257
1258static efi_status_t exit_boot(struct boot_params *boot_params, 1258static efi_status_t exit_boot(struct boot_params *boot_params,
1259 void *handle) 1259 void *handle, bool is64)
1260{ 1260{
1261 struct efi_info *efi = &boot_params->efi_info; 1261 struct efi_info *efi = &boot_params->efi_info;
1262 unsigned long map_sz, key, desc_size; 1262 unsigned long map_sz, key, desc_size;
1263 efi_memory_desc_t *mem_map; 1263 efi_memory_desc_t *mem_map;
1264 struct setup_data *e820ext; 1264 struct setup_data *e820ext;
1265 const char *signature;
1265 __u32 e820ext_size; 1266 __u32 e820ext_size;
1266 __u32 nr_desc, prev_nr_desc; 1267 __u32 nr_desc, prev_nr_desc;
1267 efi_status_t status; 1268 efi_status_t status;
@@ -1295,7 +1296,9 @@ get_map:
1295 goto get_map; /* Allocated memory, get map again */ 1296 goto get_map; /* Allocated memory, get map again */
1296 } 1297 }
1297 1298
1298 memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); 1299 signature = is64 ? EFI64_LOADER_SIGNATURE : EFI32_LOADER_SIGNATURE;
1300 memcpy(&efi->efi_loader_signature, signature, sizeof(__u32));
1301
1299 efi->efi_systab = (unsigned long)sys_table; 1302 efi->efi_systab = (unsigned long)sys_table;
1300 efi->efi_memdesc_size = desc_size; 1303 efi->efi_memdesc_size = desc_size;
1301 efi->efi_memdesc_version = desc_version; 1304 efi->efi_memdesc_version = desc_version;
@@ -1408,7 +1411,7 @@ struct boot_params *efi_main(struct efi_config *c,
1408 hdr->code32_start = bzimage_addr; 1411 hdr->code32_start = bzimage_addr;
1409 } 1412 }
1410 1413
1411 status = exit_boot(boot_params, handle); 1414 status = exit_boot(boot_params, handle, is64);
1412 if (status != EFI_SUCCESS) 1415 if (status != EFI_SUCCESS)
1413 goto fail; 1416 goto fail;
1414 1417
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index eed23c087d6c..cccc05f0681c 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -64,7 +64,7 @@ ENTRY(efi_pe_entry)
64 pushl %ecx 64 pushl %ecx
65 jmp 2f /* Skip efi_config initialization */ 65 jmp 2f /* Skip efi_config initialization */
66 66
67ENTRY(efi_stub_entry) 67ENTRY(efi32_stub_entry)
68 add $0x4, %esp 68 add $0x4, %esp
69 popl %ecx 69 popl %ecx
70 popl %edx 70 popl %edx
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 1bc206fa4bd0..37c741b0d2ac 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -178,6 +178,13 @@ ENTRY(startup_32)
178 */ 178 */
179 pushl $__KERNEL_CS 179 pushl $__KERNEL_CS
180 leal startup_64(%ebp), %eax 180 leal startup_64(%ebp), %eax
181#ifdef CONFIG_EFI_MIXED
182 movl efi32_config(%ebp), %ebx
183 cmp $0, %ebx
184 jz 1f
185 leal handover_entry(%ebp), %eax
1861:
187#endif
181 pushl %eax 188 pushl %eax
182 189
183 /* Enter paged protected Mode, activating Long Mode */ 190 /* Enter paged protected Mode, activating Long Mode */
@@ -188,6 +195,30 @@ ENTRY(startup_32)
188 lret 195 lret
189ENDPROC(startup_32) 196ENDPROC(startup_32)
190 197
198#ifdef CONFIG_EFI_MIXED
199 .org 0x190
200ENTRY(efi32_stub_entry)
201 add $0x4, %esp /* Discard return address */
202 popl %ecx
203 popl %edx
204 popl %esi
205
206 leal (BP_scratch+4)(%esi), %esp
207 call 1f
2081: pop %ebp
209 subl $1b, %ebp
210
211 movl %ecx, efi32_config(%ebp)
212 movl %edx, efi32_config+8(%ebp)
213 sgdtl efi32_boot_gdt(%ebp)
214
215 leal efi32_config(%ebp), %eax
216 movl %eax, efi_config(%ebp)
217
218 jmp startup_32
219ENDPROC(efi32_stub_entry)
220#endif
221
191 .code64 222 .code64
192 .org 0x200 223 .org 0x200
193ENTRY(startup_64) 224ENTRY(startup_64)
@@ -231,13 +262,7 @@ ENTRY(efi_pe_entry)
231 mov %rax, %rsi 262 mov %rax, %rsi
232 jmp 2f /* Skip the relocation */ 263 jmp 2f /* Skip the relocation */
233 264
234ENTRY(efi_stub_entry) 265handover_entry:
235 movq %rdi, efi64_config(%rip) /* Handle */
236 movq %rsi, efi64_config+8(%rip) /* EFI System table pointer */
237
238 leaq efi64_config(%rip), %rax
239 movq %rax, efi_config(%rip)
240
241 call 1f 266 call 1f
2421: popq %rbp 2671: popq %rbp
243 subq $1b, %rbp 268 subq $1b, %rbp
@@ -247,7 +272,6 @@ ENTRY(efi_stub_entry)
247 */ 272 */
248 movq efi_config(%rip), %rax 273 movq efi_config(%rip), %rax
249 addq %rbp, 88(%rax) 274 addq %rbp, 88(%rax)
250 movq %rdx, %rsi
2512: 2752:
252 movq efi_config(%rip), %rdi 276 movq efi_config(%rip), %rdi
253 call efi_main 277 call efi_main
@@ -336,6 +360,20 @@ preferred_addr:
336 leaq relocated(%rbx), %rax 360 leaq relocated(%rbx), %rax
337 jmp *%rax 361 jmp *%rax
338 362
363#ifdef CONFIG_EFI_STUB
364 .org 0x390
365ENTRY(efi64_stub_entry)
366 movq %rdi, efi64_config(%rip) /* Handle */
367 movq %rsi, efi64_config+8(%rip) /* EFI System table pointer */
368
369 leaq efi64_config(%rip), %rax
370 movq %rax, efi_config(%rip)
371
372 movq %rdx, %rsi
373 jmp handover_entry
374ENDPROC(efi64_stub_entry)
375#endif
376
339 .text 377 .text
340relocated: 378relocated:
341 379
@@ -404,6 +442,14 @@ gdt_end:
404efi_config: 442efi_config:
405 .quad 0 443 .quad 0
406 444
445#ifdef CONFIG_EFI_MIXED
446 .global efi32_config
447efi32_config:
448 .fill 11,8,0
449 .quad efi64_thunk
450 .byte 0
451#endif
452
407 .global efi64_config 453 .global efi64_config
408efi64_config: 454efi64_config:
409 .fill 11,8,0 455 .fill 11,8,0
diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c
index bf262077ec92..4f07df5ac5d9 100644
--- a/arch/x86/boot/tools/build.c
+++ b/arch/x86/boot/tools/build.c
@@ -53,7 +53,8 @@ int is_big_kernel;
53 53
54#define PECOFF_RELOC_RESERVE 0x20 54#define PECOFF_RELOC_RESERVE 0x20
55 55
56unsigned long efi_stub_entry; 56unsigned long efi32_stub_entry;
57unsigned long efi64_stub_entry;
57unsigned long efi_pe_entry; 58unsigned long efi_pe_entry;
58unsigned long startup_64; 59unsigned long startup_64;
59 60
@@ -231,20 +232,26 @@ static void efi_stub_defaults(void)
231 /* Defaults for old kernel */ 232 /* Defaults for old kernel */
232#ifdef CONFIG_X86_32 233#ifdef CONFIG_X86_32
233 efi_pe_entry = 0x10; 234 efi_pe_entry = 0x10;
234 efi_stub_entry = 0x30;
235#else 235#else
236 efi_pe_entry = 0x210; 236 efi_pe_entry = 0x210;
237 efi_stub_entry = 0x230;
238 startup_64 = 0x200; 237 startup_64 = 0x200;
239#endif 238#endif
240} 239}
241 240
242static void efi_stub_entry_update(void) 241static void efi_stub_entry_update(void)
243{ 242{
244#ifdef CONFIG_X86_64 /* Yes, this is really how we defined it :( */ 243 unsigned long addr = efi32_stub_entry;
245 efi_stub_entry -= 0x200; 244
245#ifdef CONFIG_X86_64
246 /* Yes, this is really how we defined it :( */
247 addr = efi64_stub_entry - 0x200;
248#endif
249
250#ifdef CONFIG_EFI_MIXED
251 if (efi32_stub_entry != addr)
252 die("32-bit and 64-bit EFI entry points do not match\n");
246#endif 253#endif
247 put_unaligned_le32(efi_stub_entry, &buf[0x264]); 254 put_unaligned_le32(addr, &buf[0x264]);
248} 255}
249 256
250#else 257#else
@@ -289,7 +296,8 @@ static void parse_zoffset(char *fname)
289 p = (char *)buf; 296 p = (char *)buf;
290 297
291 while (p && *p) { 298 while (p && *p) {
292 PARSE_ZOFS(p, efi_stub_entry); 299 PARSE_ZOFS(p, efi32_stub_entry);
300 PARSE_ZOFS(p, efi64_stub_entry);
293 PARSE_ZOFS(p, efi_pe_entry); 301 PARSE_ZOFS(p, efi_pe_entry);
294 PARSE_ZOFS(p, startup_64); 302 PARSE_ZOFS(p, startup_64);
295 303
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 3d6b9f81cc68..85856868507b 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -19,9 +19,11 @@
19 */ 19 */
20#define EFI_OLD_MEMMAP EFI_ARCH_1 20#define EFI_OLD_MEMMAP EFI_ARCH_1
21 21
22#define EFI32_LOADER_SIGNATURE "EL32"
23#define EFI64_LOADER_SIGNATURE "EL64"
24
22#ifdef CONFIG_X86_32 25#ifdef CONFIG_X86_32
23 26
24#define EFI_LOADER_SIGNATURE "EL32"
25 27
26extern unsigned long asmlinkage efi_call_phys(void *, ...); 28extern unsigned long asmlinkage efi_call_phys(void *, ...);
27 29
@@ -57,8 +59,6 @@ extern unsigned long asmlinkage efi_call_phys(void *, ...);
57 59
58#else /* !CONFIG_X86_32 */ 60#else /* !CONFIG_X86_32 */
59 61
60#define EFI_LOADER_SIGNATURE "EL64"
61
62extern u64 efi_call0(void *fp); 62extern u64 efi_call0(void *fp);
63extern u64 efi_call1(void *fp, u64 arg1); 63extern u64 efi_call1(void *fp, u64 arg1);
64extern u64 efi_call2(void *fp, u64 arg1, u64 arg2); 64extern u64 efi_call2(void *fp, u64 arg1, u64 arg2);