Skip to content

Commit 22aa45c

Browse files
joergroedelsuryasaimadhu
authored andcommitted
x86/efi: Restore Firmware IDT before calling ExitBootServices()
Commit 79419e1 ("x86/boot/compressed/64: Setup IDT in startup_32 boot path") introduced an IDT into the 32-bit boot path of the decompressor stub. But the IDT is set up before ExitBootServices() is called, and some UEFI firmwares rely on their own IDT. Save the firmware IDT on boot and restore it before calling into EFI functions to fix boot failures introduced by above commit. Fixes: 79419e1 ("x86/boot/compressed/64: Setup IDT in startup_32 boot path") Reported-by: Fabio Aiuto <[email protected]> Signed-off-by: Joerg Roedel <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Acked-by: Ard Biesheuvel <[email protected]> Cc: [email protected] # 5.13+ Link: https://lkml.kernel.org/r/[email protected]
1 parent 7c60610 commit 22aa45c

File tree

2 files changed

+24
-9
lines changed

2 files changed

+24
-9
lines changed

arch/x86/boot/compressed/efi_thunk_64.S

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
* Early support for invoking 32-bit EFI services from a 64-bit kernel.
66
*
77
* Because this thunking occurs before ExitBootServices() we have to
8-
* restore the firmware's 32-bit GDT before we make EFI service calls,
9-
* since the firmware's 32-bit IDT is still currently installed and it
10-
* needs to be able to service interrupts.
8+
* restore the firmware's 32-bit GDT and IDT before we make EFI service
9+
* calls.
1110
*
1211
* On the plus side, we don't have to worry about mangling 64-bit
1312
* addresses into 32-bits because we're executing with an identity
@@ -39,7 +38,7 @@ SYM_FUNC_START(__efi64_thunk)
3938
/*
4039
* Convert x86-64 ABI params to i386 ABI
4140
*/
42-
subq $32, %rsp
41+
subq $64, %rsp
4342
movl %esi, 0x0(%rsp)
4443
movl %edx, 0x4(%rsp)
4544
movl %ecx, 0x8(%rsp)
@@ -49,14 +48,19 @@ SYM_FUNC_START(__efi64_thunk)
4948
leaq 0x14(%rsp), %rbx
5049
sgdt (%rbx)
5150

51+
addq $16, %rbx
52+
sidt (%rbx)
53+
5254
/*
53-
* Switch to gdt with 32-bit segments. This is the firmware GDT
54-
* that was installed when the kernel started executing. This
55-
* pointer was saved at the EFI stub entry point in head_64.S.
55+
* Switch to IDT and GDT with 32-bit segments. This is the firmware GDT
56+
* and IDT that was installed when the kernel started executing. The
57+
* pointers were saved at the EFI stub entry point in head_64.S.
5658
*
5759
* Pass the saved DS selector to the 32-bit code, and use far return to
5860
* restore the saved CS selector.
5961
*/
62+
leaq efi32_boot_idt(%rip), %rax
63+
lidt (%rax)
6064
leaq efi32_boot_gdt(%rip), %rax
6165
lgdt (%rax)
6266

@@ -67,7 +71,7 @@ SYM_FUNC_START(__efi64_thunk)
6771
pushq %rax
6872
lretq
6973

70-
1: addq $32, %rsp
74+
1: addq $64, %rsp
7175
movq %rdi, %rax
7276

7377
pop %rbx
@@ -128,10 +132,13 @@ SYM_FUNC_START_LOCAL(efi_enter32)
128132

129133
/*
130134
* Some firmware will return with interrupts enabled. Be sure to
131-
* disable them before we switch GDTs.
135+
* disable them before we switch GDTs and IDTs.
132136
*/
133137
cli
134138

139+
lidtl (%ebx)
140+
subl $16, %ebx
141+
135142
lgdtl (%ebx)
136143

137144
movl %cr4, %eax
@@ -166,6 +173,11 @@ SYM_DATA_START(efi32_boot_gdt)
166173
.quad 0
167174
SYM_DATA_END(efi32_boot_gdt)
168175

176+
SYM_DATA_START(efi32_boot_idt)
177+
.word 0
178+
.quad 0
179+
SYM_DATA_END(efi32_boot_idt)
180+
169181
SYM_DATA_START(efi32_boot_cs)
170182
.word 0
171183
SYM_DATA_END(efi32_boot_cs)

arch/x86/boot/compressed/head_64.S

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,9 @@ SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
319319
movw %cs, rva(efi32_boot_cs)(%ebp)
320320
movw %ds, rva(efi32_boot_ds)(%ebp)
321321

322+
/* Store firmware IDT descriptor */
323+
sidtl rva(efi32_boot_idt)(%ebp)
324+
322325
/* Disable paging */
323326
movl %cr0, %eax
324327
btrl $X86_CR0_PG_BIT, %eax

0 commit comments

Comments
 (0)