Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 /*
0003  * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
0004  *
0005  * Early support for invoking 32-bit EFI services from a 64-bit kernel.
0006  *
0007  * Because this thunking occurs before ExitBootServices() we have to
0008  * restore the firmware's 32-bit GDT and IDT before we make EFI service
0009  * calls.
0010  *
0011  * On the plus side, we don't have to worry about mangling 64-bit
0012  * addresses into 32-bits because we're executing with an identity
0013  * mapped pagetable and haven't transitioned to 64-bit virtual addresses
0014  * yet.
0015  */
0016 
0017 #include <linux/linkage.h>
0018 #include <asm/msr.h>
0019 #include <asm/page_types.h>
0020 #include <asm/processor-flags.h>
0021 #include <asm/segment.h>
0022 
0023     .code64
0024     .text
0025 SYM_FUNC_START(__efi64_thunk)
0026     push    %rbp
0027     push    %rbx
0028 
0029     movl    %ds, %eax
0030     push    %rax
0031     movl    %es, %eax
0032     push    %rax
0033     movl    %ss, %eax
0034     push    %rax
0035 
0036     /* Copy args passed on stack */
0037     movq    0x30(%rsp), %rbp
0038     movq    0x38(%rsp), %rbx
0039     movq    0x40(%rsp), %rax
0040 
0041     /*
0042      * Convert x86-64 ABI params to i386 ABI
0043      */
0044     subq    $64, %rsp
0045     movl    %esi, 0x0(%rsp)
0046     movl    %edx, 0x4(%rsp)
0047     movl    %ecx, 0x8(%rsp)
0048     movl    %r8d, 0xc(%rsp)
0049     movl    %r9d, 0x10(%rsp)
0050     movl    %ebp, 0x14(%rsp)
0051     movl    %ebx, 0x18(%rsp)
0052     movl    %eax, 0x1c(%rsp)
0053 
0054     leaq    0x20(%rsp), %rbx
0055     sgdt    (%rbx)
0056 
0057     addq    $16, %rbx
0058     sidt    (%rbx)
0059 
0060     leaq    1f(%rip), %rbp
0061 
0062     /*
0063      * Switch to IDT and GDT with 32-bit segments. This is the firmware GDT
0064      * and IDT that was installed when the kernel started executing. The
0065      * pointers were saved at the EFI stub entry point in head_64.S.
0066      *
0067      * Pass the saved DS selector to the 32-bit code, and use far return to
0068      * restore the saved CS selector.
0069      */
0070     leaq    efi32_boot_idt(%rip), %rax
0071     lidt    (%rax)
0072     leaq    efi32_boot_gdt(%rip), %rax
0073     lgdt    (%rax)
0074 
0075     movzwl  efi32_boot_ds(%rip), %edx
0076     movzwq  efi32_boot_cs(%rip), %rax
0077     pushq   %rax
0078     leaq    efi_enter32(%rip), %rax
0079     pushq   %rax
0080     lretq
0081 
0082 1:  addq    $64, %rsp
0083     movq    %rdi, %rax
0084 
0085     pop %rbx
0086     movl    %ebx, %ss
0087     pop %rbx
0088     movl    %ebx, %es
0089     pop %rbx
0090     movl    %ebx, %ds
0091     /* Clear out 32-bit selector from FS and GS */
0092     xorl    %ebx, %ebx
0093     movl    %ebx, %fs
0094     movl    %ebx, %gs
0095 
0096     /*
0097      * Convert 32-bit status code into 64-bit.
0098      */
0099     roll    $1, %eax
0100     rorq    $1, %rax
0101 
0102     pop %rbx
0103     pop %rbp
0104     RET
0105 SYM_FUNC_END(__efi64_thunk)
0106 
0107     .code32
0108 /*
0109  * EFI service pointer must be in %edi.
0110  *
0111  * The stack should represent the 32-bit calling convention.
0112  */
0113 SYM_FUNC_START_LOCAL(efi_enter32)
0114     /* Load firmware selector into data and stack segment registers */
0115     movl    %edx, %ds
0116     movl    %edx, %es
0117     movl    %edx, %fs
0118     movl    %edx, %gs
0119     movl    %edx, %ss
0120 
0121     /* Reload pgtables */
0122     movl    %cr3, %eax
0123     movl    %eax, %cr3
0124 
0125     /* Disable paging */
0126     movl    %cr0, %eax
0127     btrl    $X86_CR0_PG_BIT, %eax
0128     movl    %eax, %cr0
0129 
0130     /* Disable long mode via EFER */
0131     movl    $MSR_EFER, %ecx
0132     rdmsr
0133     btrl    $_EFER_LME, %eax
0134     wrmsr
0135 
0136     call    *%edi
0137 
0138     /* We must preserve return value */
0139     movl    %eax, %edi
0140 
0141     /*
0142      * Some firmware will return with interrupts enabled. Be sure to
0143      * disable them before we switch GDTs and IDTs.
0144      */
0145     cli
0146 
0147     lidtl   (%ebx)
0148     subl    $16, %ebx
0149 
0150     lgdtl   (%ebx)
0151 
0152     movl    %cr4, %eax
0153     btsl    $(X86_CR4_PAE_BIT), %eax
0154     movl    %eax, %cr4
0155 
0156     movl    %cr3, %eax
0157     movl    %eax, %cr3
0158 
0159     movl    $MSR_EFER, %ecx
0160     rdmsr
0161     btsl    $_EFER_LME, %eax
0162     wrmsr
0163 
0164     xorl    %eax, %eax
0165     lldt    %ax
0166 
0167     pushl   $__KERNEL_CS
0168     pushl   %ebp
0169 
0170     /* Enable paging */
0171     movl    %cr0, %eax
0172     btsl    $X86_CR0_PG_BIT, %eax
0173     movl    %eax, %cr0
0174     lret
0175 SYM_FUNC_END(efi_enter32)
0176 
0177     .data
0178     .balign 8
0179 SYM_DATA_START(efi32_boot_gdt)
0180     .word   0
0181     .quad   0
0182 SYM_DATA_END(efi32_boot_gdt)
0183 
0184 SYM_DATA_START(efi32_boot_idt)
0185     .word   0
0186     .quad   0
0187 SYM_DATA_END(efi32_boot_idt)
0188 
0189 SYM_DATA_START(efi32_boot_cs)
0190     .word   0
0191 SYM_DATA_END(efi32_boot_cs)
0192 
0193 SYM_DATA_START(efi32_boot_ds)
0194     .word   0
0195 SYM_DATA_END(efi32_boot_ds)