Skip to content

Commit ea5e191

Browse files
ardbiesheuvelIngo Molnar
authored andcommitted
efi/x86: Simplify mixed mode call wrapper
Calling 32-bit EFI runtime services from a 64-bit OS involves switching back to the flat mapping with a stack carved out of memory that is 32-bit addressable. There is no need to actually execute the 64-bit part of this routine from the flat mapping as well, as long as the entry and return address fit in 32 bits. There is also no need to preserve part of the calling context in global variables: we can simply push the old stack pointer value to the new stack, and keep the return address from the code32 section in EBX. While at it, move the conditional check whether to invoke the mixed mode version of SetVirtualAddressMap() into the 64-bit implementation of the wrapper routine. Signed-off-by: Ard Biesheuvel <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Arvind Sankar <[email protected]> Cc: Matthew Garrett <[email protected]> Cc: [email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent e5f930f commit ea5e191

File tree

4 files changed

+71
-148
lines changed

4 files changed

+71
-148
lines changed

arch/x86/include/asm/efi.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,6 @@ extern void parse_efi_setup(u64 phys_addr, u32 data_len);
164164
extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
165165

166166
extern void efi_thunk_runtime_setup(void);
167-
extern efi_status_t efi_thunk_set_virtual_address_map(
168-
void *phys_set_virtual_address_map,
169-
unsigned long memory_map_size,
170-
unsigned long descriptor_size,
171-
u32 descriptor_version,
172-
efi_memory_desc_t *virtual_map);
173167
efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size,
174168
unsigned long descriptor_size,
175169
u32 descriptor_version,

arch/x86/platform/efi/efi.c

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,21 +1015,10 @@ static void __init __efi_enter_virtual_mode(void)
10151015

10161016
efi_sync_low_kernel_mappings();
10171017

1018-
if (!efi_is_mixed()) {
1019-
status = efi_set_virtual_address_map(
1020-
efi.memmap.desc_size * count,
1021-
efi.memmap.desc_size,
1022-
efi.memmap.desc_version,
1023-
(efi_memory_desc_t *)pa);
1024-
} else {
1025-
status = efi_thunk_set_virtual_address_map(
1026-
efi_phys.set_virtual_address_map,
1027-
efi.memmap.desc_size * count,
1028-
efi.memmap.desc_size,
1029-
efi.memmap.desc_version,
1030-
(efi_memory_desc_t *)pa);
1031-
}
1032-
1018+
status = efi_set_virtual_address_map(efi.memmap.desc_size * count,
1019+
efi.memmap.desc_size,
1020+
efi.memmap.desc_version,
1021+
(efi_memory_desc_t *)pa);
10331022
if (status != EFI_SUCCESS) {
10341023
pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n",
10351024
status);

arch/x86/platform/efi/efi_64.c

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -626,61 +626,74 @@ void efi_switch_mm(struct mm_struct *mm)
626626
switch_mm(efi_scratch.prev_mm, mm, NULL);
627627
}
628628

629-
#ifdef CONFIG_EFI_MIXED
630629
static DEFINE_SPINLOCK(efi_runtime_lock);
631630

632-
#define runtime_service32(func) \
633-
({ \
634-
u32 table = (u32)(unsigned long)efi.systab; \
635-
u32 *rt, *___f; \
636-
\
637-
rt = (u32 *)(table + offsetof(efi_system_table_32_t, runtime)); \
638-
___f = (u32 *)(*rt + offsetof(efi_runtime_services_32_t, func)); \
639-
*___f; \
631+
/*
632+
* DS and ES contain user values. We need to save them.
633+
* The 32-bit EFI code needs a valid DS, ES, and SS. There's no
634+
* need to save the old SS: __KERNEL_DS is always acceptable.
635+
*/
636+
#define __efi_thunk(func, ...) \
637+
({ \
638+
efi_runtime_services_32_t *__rt; \
639+
unsigned short __ds, __es; \
640+
efi_status_t ____s; \
641+
\
642+
__rt = (void *)(unsigned long)efi.systab->mixed_mode.runtime; \
643+
\
644+
savesegment(ds, __ds); \
645+
savesegment(es, __es); \
646+
\
647+
loadsegment(ss, __KERNEL_DS); \
648+
loadsegment(ds, __KERNEL_DS); \
649+
loadsegment(es, __KERNEL_DS); \
650+
\
651+
____s = efi64_thunk(__rt->func, __VA_ARGS__); \
652+
\
653+
loadsegment(ds, __ds); \
654+
loadsegment(es, __es); \
655+
\
656+
____s ^= (____s & BIT(31)) | (____s & BIT_ULL(31)) << 32; \
657+
____s; \
640658
})
641659

642660
/*
643661
* Switch to the EFI page tables early so that we can access the 1:1
644662
* runtime services mappings which are not mapped in any other page
645-
* tables. This function must be called before runtime_service32().
663+
* tables.
646664
*
647665
* Also, disable interrupts because the IDT points to 64-bit handlers,
648666
* which aren't going to function correctly when we switch to 32-bit.
649667
*/
650-
#define efi_thunk(f, ...) \
668+
#define efi_thunk(func...) \
651669
({ \
652670
efi_status_t __s; \
653-
u32 __func; \
654671
\
655672
arch_efi_call_virt_setup(); \
656673
\
657-
__func = runtime_service32(f); \
658-
__s = efi64_thunk(__func, __VA_ARGS__); \
674+
__s = __efi_thunk(func); \
659675
\
660676
arch_efi_call_virt_teardown(); \
661677
\
662678
__s; \
663679
})
664680

665-
efi_status_t efi_thunk_set_virtual_address_map(
666-
void *phys_set_virtual_address_map,
667-
unsigned long memory_map_size,
668-
unsigned long descriptor_size,
669-
u32 descriptor_version,
670-
efi_memory_desc_t *virtual_map)
681+
static efi_status_t __init
682+
efi_thunk_set_virtual_address_map(unsigned long memory_map_size,
683+
unsigned long descriptor_size,
684+
u32 descriptor_version,
685+
efi_memory_desc_t *virtual_map)
671686
{
672687
efi_status_t status;
673688
unsigned long flags;
674-
u32 func;
675689

676690
efi_sync_low_kernel_mappings();
677691
local_irq_save(flags);
678692

679693
efi_switch_mm(&efi_mm);
680694

681-
func = (u32)(unsigned long)phys_set_virtual_address_map;
682-
status = efi64_thunk(func, memory_map_size, descriptor_size,
683-
descriptor_version, virtual_map);
695+
status = __efi_thunk(set_virtual_address_map, memory_map_size,
696+
descriptor_size, descriptor_version, virtual_map);
684697

685698
efi_switch_mm(efi_scratch.prev_mm);
686699
local_irq_restore(flags);
@@ -983,8 +996,11 @@ efi_thunk_query_capsule_caps(efi_capsule_header_t **capsules,
983996
return EFI_UNSUPPORTED;
984997
}
985998

986-
void efi_thunk_runtime_setup(void)
999+
void __init efi_thunk_runtime_setup(void)
9871000
{
1001+
if (!IS_ENABLED(CONFIG_EFI_MIXED))
1002+
return;
1003+
9881004
efi.get_time = efi_thunk_get_time;
9891005
efi.set_time = efi_thunk_set_time;
9901006
efi.get_wakeup_time = efi_thunk_get_wakeup_time;
@@ -1000,7 +1016,6 @@ void efi_thunk_runtime_setup(void)
10001016
efi.update_capsule = efi_thunk_update_capsule;
10011017
efi.query_capsule_caps = efi_thunk_query_capsule_caps;
10021018
}
1003-
#endif /* CONFIG_EFI_MIXED */
10041019

10051020
efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
10061021
unsigned long descriptor_size,
@@ -1011,6 +1026,12 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
10111026
unsigned long flags;
10121027
pgd_t *save_pgd = NULL;
10131028

1029+
if (efi_is_mixed())
1030+
return efi_thunk_set_virtual_address_map(memory_map_size,
1031+
descriptor_size,
1032+
descriptor_version,
1033+
virtual_map);
1034+
10141035
if (efi_enabled(EFI_OLD_MEMMAP)) {
10151036
save_pgd = efi_old_memmap_phys_prolog();
10161037
if (!save_pgd)

arch/x86/platform/efi/efi_thunk_64.S

Lines changed: 20 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -25,129 +25,48 @@
2525

2626
.text
2727
.code64
28-
SYM_FUNC_START(efi64_thunk)
28+
SYM_CODE_START(efi64_thunk)
2929
push %rbp
3030
push %rbx
3131

3232
/*
3333
* Switch to 1:1 mapped 32-bit stack pointer.
3434
*/
35-
movq %rsp, efi_saved_sp(%rip)
35+
movq %rsp, %rax
3636
movq efi_scratch(%rip), %rsp
37+
push %rax
3738

3839
/*
3940
* Calculate the physical address of the kernel text.
4041
*/
4142
movq $__START_KERNEL_map, %rax
4243
subq phys_base(%rip), %rax
4344

44-
/*
45-
* Push some physical addresses onto the stack. This is easier
46-
* to do now in a code64 section while the assembler can address
47-
* 64-bit values. Note that all the addresses on the stack are
48-
* 32-bit.
49-
*/
50-
subq $16, %rsp
51-
leaq efi_exit32(%rip), %rbx
52-
subq %rax, %rbx
53-
movl %ebx, 8(%rsp)
54-
55-
leaq __efi64_thunk(%rip), %rbx
45+
leaq 1f(%rip), %rbp
46+
leaq 2f(%rip), %rbx
47+
subq %rax, %rbp
5648
subq %rax, %rbx
57-
call *%rbx
58-
59-
movq efi_saved_sp(%rip), %rsp
60-
pop %rbx
61-
pop %rbp
62-
retq
63-
SYM_FUNC_END(efi64_thunk)
6449

65-
/*
66-
* We run this function from the 1:1 mapping.
67-
*
68-
* This function must be invoked with a 1:1 mapped stack.
69-
*/
70-
SYM_FUNC_START_LOCAL(__efi64_thunk)
71-
movl %ds, %eax
72-
push %rax
73-
movl %es, %eax
74-
push %rax
75-
movl %ss, %eax
76-
push %rax
77-
78-
subq $32, %rsp
79-
movl %esi, 0x0(%rsp)
80-
movl %edx, 0x4(%rsp)
81-
movl %ecx, 0x8(%rsp)
82-
movq %r8, %rsi
83-
movl %esi, 0xc(%rsp)
84-
movq %r9, %rsi
85-
movl %esi, 0x10(%rsp)
86-
87-
leaq 1f(%rip), %rbx
88-
movq %rbx, func_rt_ptr(%rip)
50+
subq $28, %rsp
51+
movl %ebx, 0x0(%rsp) /* return address */
52+
movl %esi, 0x4(%rsp)
53+
movl %edx, 0x8(%rsp)
54+
movl %ecx, 0xc(%rsp)
55+
movl %r8d, 0x10(%rsp)
56+
movl %r9d, 0x14(%rsp)
8957

9058
/* Switch to 32-bit descriptor */
9159
pushq $__KERNEL32_CS
92-
leaq efi_enter32(%rip), %rax
93-
pushq %rax
60+
pushq %rdi /* EFI runtime service address */
9461
lretq
9562

96-
1: addq $32, %rsp
97-
63+
1: movq 24(%rsp), %rsp
9864
pop %rbx
99-
movl %ebx, %ss
100-
pop %rbx
101-
movl %ebx, %es
102-
pop %rbx
103-
movl %ebx, %ds
104-
105-
/*
106-
* Convert 32-bit status code into 64-bit.
107-
*/
108-
test %rax, %rax
109-
jz 1f
110-
movl %eax, %ecx
111-
andl $0x0fffffff, %ecx
112-
andl $0xf0000000, %eax
113-
shl $32, %rax
114-
or %rcx, %rax
115-
1:
116-
ret
117-
SYM_FUNC_END(__efi64_thunk)
118-
119-
SYM_FUNC_START_LOCAL(efi_exit32)
120-
movq func_rt_ptr(%rip), %rax
121-
push %rax
122-
mov %rdi, %rax
123-
ret
124-
SYM_FUNC_END(efi_exit32)
65+
pop %rbp
66+
retq
12567

12668
.code32
127-
/*
128-
* EFI service pointer must be in %edi.
129-
*
130-
* The stack should represent the 32-bit calling convention.
131-
*/
132-
SYM_FUNC_START_LOCAL(efi_enter32)
133-
movl $__KERNEL_DS, %eax
134-
movl %eax, %ds
135-
movl %eax, %es
136-
movl %eax, %ss
137-
138-
call *%edi
139-
140-
/* We must preserve return value */
141-
movl %eax, %edi
142-
143-
movl 72(%esp), %eax
144-
pushl $__KERNEL_CS
145-
pushl %eax
146-
69+
2: pushl $__KERNEL_CS
70+
pushl %ebp
14771
lret
148-
SYM_FUNC_END(efi_enter32)
149-
150-
.data
151-
.balign 8
152-
func_rt_ptr: .quad 0
153-
efi_saved_sp: .quad 0
72+
SYM_CODE_END(efi64_thunk)

0 commit comments

Comments
 (0)