Skip to content

Commit

Permalink
x86/efi: Restore Firmware IDT before calling ExitBootServices()
Browse files Browse the repository at this point in the history
commit 22aa45c upstream.

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 <fabioaiuto83@gmail.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Cc: stable@vger.kernel.org # 5.13+
Link: https://lkml.kernel.org/r/20210820125703.32410-1-joro@8bytes.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
joergroedel authored and gregkh committed Sep 15, 2021
1 parent 495bb7e commit 7609064
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 9 deletions.
30 changes: 21 additions & 9 deletions arch/x86/boot/compressed/efi_thunk_64.S
Expand Up @@ -5,9 +5,8 @@
* Early support for invoking 32-bit EFI services from a 64-bit kernel.
*
* Because this thunking occurs before ExitBootServices() we have to
* restore the firmware's 32-bit GDT before we make EFI service calls,
* since the firmware's 32-bit IDT is still currently installed and it
* needs to be able to service interrupts.
* restore the firmware's 32-bit GDT and IDT before we make EFI service
* calls.
*
* On the plus side, we don't have to worry about mangling 64-bit
* addresses into 32-bits because we're executing with an identity
Expand Down Expand Up @@ -39,7 +38,7 @@ SYM_FUNC_START(__efi64_thunk)
/*
* Convert x86-64 ABI params to i386 ABI
*/
subq $32, %rsp
subq $64, %rsp
movl %esi, 0x0(%rsp)
movl %edx, 0x4(%rsp)
movl %ecx, 0x8(%rsp)
Expand All @@ -49,14 +48,19 @@ SYM_FUNC_START(__efi64_thunk)
leaq 0x14(%rsp), %rbx
sgdt (%rbx)

addq $16, %rbx
sidt (%rbx)

/*
* Switch to gdt with 32-bit segments. This is the firmware GDT
* that was installed when the kernel started executing. This
* pointer was saved at the EFI stub entry point in head_64.S.
* Switch to IDT and GDT with 32-bit segments. This is the firmware GDT
* and IDT that was installed when the kernel started executing. The
* pointers were saved at the EFI stub entry point in head_64.S.
*
* Pass the saved DS selector to the 32-bit code, and use far return to
* restore the saved CS selector.
*/
leaq efi32_boot_idt(%rip), %rax
lidt (%rax)
leaq efi32_boot_gdt(%rip), %rax
lgdt (%rax)

Expand All @@ -67,7 +71,7 @@ SYM_FUNC_START(__efi64_thunk)
pushq %rax
lretq

1: addq $32, %rsp
1: addq $64, %rsp
movq %rdi, %rax

pop %rbx
Expand Down Expand Up @@ -128,10 +132,13 @@ SYM_FUNC_START_LOCAL(efi_enter32)

/*
* Some firmware will return with interrupts enabled. Be sure to
* disable them before we switch GDTs.
* disable them before we switch GDTs and IDTs.
*/
cli

lidtl (%ebx)
subl $16, %ebx

lgdtl (%ebx)

movl %cr4, %eax
Expand Down Expand Up @@ -166,6 +173,11 @@ SYM_DATA_START(efi32_boot_gdt)
.quad 0
SYM_DATA_END(efi32_boot_gdt)

SYM_DATA_START(efi32_boot_idt)
.word 0
.quad 0
SYM_DATA_END(efi32_boot_idt)

SYM_DATA_START(efi32_boot_cs)
.word 0
SYM_DATA_END(efi32_boot_cs)
Expand Down
3 changes: 3 additions & 0 deletions arch/x86/boot/compressed/head_64.S
Expand Up @@ -319,6 +319,9 @@ SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
movw %cs, rva(efi32_boot_cs)(%ebp)
movw %ds, rva(efi32_boot_ds)(%ebp)

/* Store firmware IDT descriptor */
sidtl rva(efi32_boot_idt)(%ebp)

/* Disable paging */
movl %cr0, %eax
btrl $X86_CR0_PG_BIT, %eax
Expand Down

0 comments on commit 7609064

Please sign in to comment.