/* SPDX-License-Identifier: GPL-2.0 */1/*2* Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming3*4* Early support for invoking 32-bit EFI services from a 64-bit kernel.5*6* Because this thunking occurs before ExitBootServices() we have to7* restore the firmware's 32-bit GDT and IDT before we make EFI service8* calls.9*10* On the plus side, we don't have to worry about mangling 64-bit11* addresses into 32-bits because we're executing with an identity12* mapped pagetable and haven't transitioned to 64-bit virtual addresses13* yet.14*/1516#include <linux/linkage.h>17#include <asm/desc_defs.h>18#include <asm/msr.h>19#include <asm/page_types.h>20#include <asm/pgtable_types.h>21#include <asm/processor-flags.h>22#include <asm/segment.h>2324.text25.code3226#ifdef CONFIG_EFI_HANDOVER_PROTOCOL27SYM_FUNC_START(efi32_stub_entry)28call 1f291: popl %ecx3031/* Clear BSS */32xorl %eax, %eax33leal (_bss - 1b)(%ecx), %edi34leal (_ebss - 1b)(%ecx), %ecx35subl %edi, %ecx36shrl $2, %ecx37cld38rep stosl3940add $0x4, %esp /* Discard return address */41movl 8(%esp), %ebx /* struct boot_params pointer */42jmp efi32_startup43SYM_FUNC_END(efi32_stub_entry)44#endif4546/*47* Called using a far call from __efi64_thunk() below, using the x86_64 SysV48* ABI (except for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are49* used instead). EBP+16 points to the arguments passed via the stack.50*51* The first argument (EDI) is a pointer to the boot service or protocol, to52* which the remaining arguments are passed, each truncated to 32 bits.53*/54SYM_FUNC_START_LOCAL(efi_enter32)55/*56* Convert x86-64 SysV ABI params to i386 ABI57*/58pushl 32(%ebp) /* Up to 3 args passed via the stack */59pushl 24(%ebp)60pushl 16(%ebp)61pushl %ebx /* R9 */62pushl %eax /* R8 */63pushl %ecx64pushl %edx65pushl %esi6667/* Disable paging */68movl %cr0, %eax69btrl $X86_CR0_PG_BIT, %eax70movl %eax, %cr07172/* Disable long mode via EFER */73movl $MSR_EFER, %ecx74rdmsr75btrl $_EFER_LME, %eax76wrmsr7778call *%edi7980/* We must preserve return value */81movl %eax, %edi8283call efi32_enable_long_mode8485addl $32, %esp86movl %edi, %eax87lret88SYM_FUNC_END(efi_enter32)8990.code6491SYM_FUNC_START(__efi64_thunk)92push %rbp93movl %esp, %ebp94push %rbx9596/* Move args #5 and #6 into 32-bit accessible registers */97movl %r8d, %eax98movl %r9d, %ebx99100lcalll *efi32_call(%rip)101102pop %rbx103pop %rbp104RET105SYM_FUNC_END(__efi64_thunk)106107.code32108SYM_FUNC_START_LOCAL(efi32_enable_long_mode)109movl %cr4, %eax110btsl $(X86_CR4_PAE_BIT), %eax111movl %eax, %cr4112113movl $MSR_EFER, %ecx114rdmsr115btsl $_EFER_LME, %eax116wrmsr117118/* Disable interrupts - the firmware's IDT does not work in long mode */119cli120121/* Enable paging */122movl %cr0, %eax123btsl $X86_CR0_PG_BIT, %eax124movl %eax, %cr0125ret126SYM_FUNC_END(efi32_enable_long_mode)127128/*129* This is the common EFI stub entry point for mixed mode. It sets up the GDT130* and page tables needed for 64-bit execution, after which it calls the131* common 64-bit EFI entrypoint efi_stub_entry().132*133* Arguments: 0(%esp) image handle134* 4(%esp) EFI system table pointer135* %ebx struct boot_params pointer (or NULL)136*137* Since this is the point of no return for ordinary execution, no registers138* are considered live except for the function parameters. [Note that the EFI139* stub may still exit and return to the firmware using the Exit() EFI boot140* service.]141*/142SYM_FUNC_START_LOCAL(efi32_startup)143movl %esp, %ebp144145subl $8, %esp146sgdtl (%esp) /* Save GDT descriptor to the stack */147movl 2(%esp), %esi /* Existing GDT pointer */148movzwl (%esp), %ecx /* Existing GDT limit */149inc %ecx /* Existing GDT size */150andl $~7, %ecx /* Ensure size is multiple of 8 */151152subl %ecx, %esp /* Allocate new GDT */153andl $~15, %esp /* Realign the stack */154movl %esp, %edi /* New GDT address */155leal 7(%ecx), %eax /* New GDT limit */156pushw %cx /* Push 64-bit CS (for LJMP below) */157pushl %edi /* Push new GDT address */158pushw %ax /* Push new GDT limit */159160/* Copy GDT to the stack and add a 64-bit code segment at the end */161movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) & 0xffffffff, (%edi,%ecx)162movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) >> 32, 4(%edi,%ecx)163shrl $2, %ecx164cld165rep movsl /* Copy the firmware GDT */166lgdtl (%esp) /* Switch to the new GDT */167168call 1f1691: pop %edi170171/* Record mixed mode entry */172movb $0x0, (efi_is64 - 1b)(%edi)173174/* Set up indirect far call to re-enter 32-bit mode */175leal (efi32_call - 1b)(%edi), %eax176addl %eax, (%eax)177movw %cs, 4(%eax)178179/* Disable paging */180movl %cr0, %eax181btrl $X86_CR0_PG_BIT, %eax182movl %eax, %cr0183184/* Set up 1:1 mapping */185leal (pte - 1b)(%edi), %eax186movl $_PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, %ecx187leal (_PAGE_PRESENT | _PAGE_RW)(%eax), %edx1882: movl %ecx, (%eax)189addl $8, %eax190addl $PMD_SIZE, %ecx191jnc 2b192193movl $PAGE_SIZE, %ecx194.irpc l, 0123195movl %edx, \l * 8(%eax)196addl %ecx, %edx197.endr198addl %ecx, %eax199movl %edx, (%eax)200movl %eax, %cr3201202call efi32_enable_long_mode203204/* Set up far jump to 64-bit mode (CS is already on the stack) */205leal (efi_stub_entry - 1b)(%edi), %eax206movl %eax, 2(%esp)207208movl 0(%ebp), %edi209movl 4(%ebp), %esi210movl %ebx, %edx211ljmpl *2(%esp)212SYM_FUNC_END(efi32_startup)213214/*215* efi_status_t efi32_pe_entry(efi_handle_t image_handle,216* efi_system_table_32_t *sys_table)217*/218SYM_FUNC_START(efi32_pe_entry)219pushl %ebx // save callee-save registers220221/* Check whether the CPU supports long mode */222movl $0x80000001, %eax // assume extended info support223cpuid224btl $29, %edx // check long mode bit225jnc 1f226leal 8(%esp), %esp // preserve stack alignment227xor %ebx, %ebx // no struct boot_params pointer228jmp efi32_startup // only ESP and EBX remain live2291: movl $0x80000003, %eax // EFI_UNSUPPORTED230popl %ebx231RET232SYM_FUNC_END(efi32_pe_entry)233234#ifdef CONFIG_EFI_HANDOVER_PROTOCOL235.org efi32_stub_entry + 0x200236.code64237SYM_FUNC_START_NOALIGN(efi64_stub_entry)238jmp efi_handover_entry239SYM_FUNC_END(efi64_stub_entry)240#endif241242.data243.balign 8244SYM_DATA_START_LOCAL(efi32_call)245.long efi_enter32 - .246.word 0x0247SYM_DATA_END(efi32_call)248SYM_DATA(efi_is64, .byte 1)249250.bss251.balign PAGE_SIZE252SYM_DATA_LOCAL(pte, .fill 6 * PAGE_SIZE, 1, 0)253254255