mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 04:35:27 +07:00
90ff226281
When booting in mixed mode, the firmware's GDT is still installed at handover entry in efi32_stub_entry. We save the GDTR for later use in __efi64_thunk but we are assuming that descriptor 2 (__KERNEL_CS) is a valid 32-bit code segment descriptor and that descriptor 3 (__KERNEL_DS/__BOOT_DS) is a valid data segment descriptor. This happens to be true for OVMF (it actually uses descriptor 1 for data segments, but descriptor 3 is also setup as data), but we shouldn't depend on this being the case. Fix this by saving the code and data selectors in addition to the GDTR in efi32_stub_entry, and restoring them in __efi64_thunk before calling the firmware. The UEFI specification guarantees that selectors will be flat, so using the DS selector for all the segment registers should be enough. We also need to install our own GDT before initializing segment registers in startup_32, so move the GDT load up to the beginning of the function. [ardb: mention mixed mode in the commit log] Signed-off-by: Arvind Sankar <nivedita@alum.mit.edu> Link: https://lore.kernel.org/r/20200202171353.3736319-3-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
189 lines
3.7 KiB
ArmAsm
189 lines
3.7 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
|
|
*
|
|
* 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 serivce calls,
|
|
* since the firmware's 32-bit IDT is still currently installed and it
|
|
* needs to be able to service interrupts.
|
|
*
|
|
* 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
|
|
* mapped pagetable and haven't transitioned to 64-bit virtual addresses
|
|
* yet.
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/msr.h>
|
|
#include <asm/page_types.h>
|
|
#include <asm/processor-flags.h>
|
|
#include <asm/segment.h>
|
|
|
|
.code64
|
|
.text
|
|
SYM_FUNC_START(__efi64_thunk)
|
|
push %rbp
|
|
push %rbx
|
|
|
|
leaq 1f(%rip), %rbp
|
|
leaq efi_gdt64(%rip), %rbx
|
|
movl %ebx, 2(%rbx) /* Fixup the gdt base address */
|
|
|
|
movl %ds, %eax
|
|
push %rax
|
|
movl %es, %eax
|
|
push %rax
|
|
movl %ss, %eax
|
|
push %rax
|
|
|
|
/*
|
|
* Convert x86-64 ABI params to i386 ABI
|
|
*/
|
|
subq $32, %rsp
|
|
movl %esi, 0x0(%rsp)
|
|
movl %edx, 0x4(%rsp)
|
|
movl %ecx, 0x8(%rsp)
|
|
movl %r8d, 0xc(%rsp)
|
|
movl %r9d, 0x10(%rsp)
|
|
|
|
sgdt 0x14(%rsp)
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* Pass the saved DS selector to the 32-bit code, and use far return to
|
|
* restore the saved CS selector.
|
|
*/
|
|
leaq efi32_boot_gdt(%rip), %rax
|
|
lgdt (%rax)
|
|
|
|
movzwl efi32_boot_ds(%rip), %edx
|
|
movzwq efi32_boot_cs(%rip), %rax
|
|
pushq %rax
|
|
leaq efi_enter32(%rip), %rax
|
|
pushq %rax
|
|
lretq
|
|
|
|
1: lgdt 0x14(%rsp)
|
|
addq $32, %rsp
|
|
movq %rdi, %rax
|
|
|
|
pop %rbx
|
|
movl %ebx, %ss
|
|
pop %rbx
|
|
movl %ebx, %es
|
|
pop %rbx
|
|
movl %ebx, %ds
|
|
/* Clear out 32-bit selector from FS and GS */
|
|
xorl %ebx, %ebx
|
|
movl %ebx, %fs
|
|
movl %ebx, %gs
|
|
|
|
/*
|
|
* Convert 32-bit status code into 64-bit.
|
|
*/
|
|
roll $1, %eax
|
|
rorq $1, %rax
|
|
|
|
pop %rbx
|
|
pop %rbp
|
|
ret
|
|
SYM_FUNC_END(__efi64_thunk)
|
|
|
|
.code32
|
|
/*
|
|
* EFI service pointer must be in %edi.
|
|
*
|
|
* The stack should represent the 32-bit calling convention.
|
|
*/
|
|
SYM_FUNC_START_LOCAL(efi_enter32)
|
|
/* Load firmware selector into data and stack segment registers */
|
|
movl %edx, %ds
|
|
movl %edx, %es
|
|
movl %edx, %fs
|
|
movl %edx, %gs
|
|
movl %edx, %ss
|
|
|
|
/* Reload pgtables */
|
|
movl %cr3, %eax
|
|
movl %eax, %cr3
|
|
|
|
/* Disable paging */
|
|
movl %cr0, %eax
|
|
btrl $X86_CR0_PG_BIT, %eax
|
|
movl %eax, %cr0
|
|
|
|
/* Disable long mode via EFER */
|
|
movl $MSR_EFER, %ecx
|
|
rdmsr
|
|
btrl $_EFER_LME, %eax
|
|
wrmsr
|
|
|
|
call *%edi
|
|
|
|
/* We must preserve return value */
|
|
movl %eax, %edi
|
|
|
|
/*
|
|
* Some firmware will return with interrupts enabled. Be sure to
|
|
* disable them before we switch GDTs.
|
|
*/
|
|
cli
|
|
|
|
lgdtl (%ebx)
|
|
|
|
movl %cr4, %eax
|
|
btsl $(X86_CR4_PAE_BIT), %eax
|
|
movl %eax, %cr4
|
|
|
|
movl %cr3, %eax
|
|
movl %eax, %cr3
|
|
|
|
movl $MSR_EFER, %ecx
|
|
rdmsr
|
|
btsl $_EFER_LME, %eax
|
|
wrmsr
|
|
|
|
xorl %eax, %eax
|
|
lldt %ax
|
|
|
|
pushl $__KERNEL_CS
|
|
pushl %ebp
|
|
|
|
/* Enable paging */
|
|
movl %cr0, %eax
|
|
btsl $X86_CR0_PG_BIT, %eax
|
|
movl %eax, %cr0
|
|
lret
|
|
SYM_FUNC_END(efi_enter32)
|
|
|
|
.data
|
|
.balign 8
|
|
SYM_DATA_START(efi32_boot_gdt)
|
|
.word 0
|
|
.quad 0
|
|
SYM_DATA_END(efi32_boot_gdt)
|
|
|
|
SYM_DATA_START(efi32_boot_cs)
|
|
.word 0
|
|
SYM_DATA_END(efi32_boot_cs)
|
|
|
|
SYM_DATA_START(efi32_boot_ds)
|
|
.word 0
|
|
SYM_DATA_END(efi32_boot_ds)
|
|
|
|
SYM_DATA_START(efi_gdt64)
|
|
.word efi_gdt64_end - efi_gdt64
|
|
.long 0 /* Filled out by user */
|
|
.word 0
|
|
.quad 0x0000000000000000 /* NULL descriptor */
|
|
.quad 0x00af9a000000ffff /* __KERNEL_CS */
|
|
.quad 0x00cf92000000ffff /* __KERNEL_DS */
|
|
.quad 0x0080890000000000 /* TS descriptor */
|
|
.quad 0x0000000000000000 /* TS continued */
|
|
SYM_DATA_END_LABEL(efi_gdt64, SYM_L_LOCAL, efi_gdt64_end)
|