root/arch/x86/boot/compressed/efi_thunk_64.S

/* [<][>][^][v][top][bottom][index][help] */
   1 /* SPDX-License-Identifier: GPL-2.0 */
   2 /*
   3  * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
   4  *
   5  * Early support for invoking 32-bit EFI services from a 64-bit kernel.
   6  *
   7  * Because this thunking occurs before ExitBootServices() we have to
   8  * restore the firmware's 32-bit GDT before we make EFI serivce calls,
   9  * since the firmware's 32-bit IDT is still currently installed and it
  10  * needs to be able to service interrupts.
  11  *
  12  * On the plus side, we don't have to worry about mangling 64-bit
  13  * addresses into 32-bits because we're executing with an identify
  14  * mapped pagetable and haven't transitioned to 64-bit virtual addresses
  15  * yet.
  16  */
  17 
  18 #include <linux/linkage.h>
  19 #include <asm/msr.h>
  20 #include <asm/page_types.h>
  21 #include <asm/processor-flags.h>
  22 #include <asm/segment.h>
  23 
  24         .code64
  25         .text
  26 ENTRY(efi64_thunk)
  27         push    %rbp
  28         push    %rbx
  29 
  30         subq    $8, %rsp
  31         leaq    efi_exit32(%rip), %rax
  32         movl    %eax, 4(%rsp)
  33         leaq    efi_gdt64(%rip), %rax
  34         movl    %eax, (%rsp)
  35         movl    %eax, 2(%rax)           /* Fixup the gdt base address */
  36 
  37         movl    %ds, %eax
  38         push    %rax
  39         movl    %es, %eax
  40         push    %rax
  41         movl    %ss, %eax
  42         push    %rax
  43 
  44         /*
  45          * Convert x86-64 ABI params to i386 ABI
  46          */
  47         subq    $32, %rsp
  48         movl    %esi, 0x0(%rsp)
  49         movl    %edx, 0x4(%rsp)
  50         movl    %ecx, 0x8(%rsp)
  51         movq    %r8, %rsi
  52         movl    %esi, 0xc(%rsp)
  53         movq    %r9, %rsi
  54         movl    %esi,  0x10(%rsp)
  55 
  56         sgdt    save_gdt(%rip)
  57 
  58         leaq    1f(%rip), %rbx
  59         movq    %rbx, func_rt_ptr(%rip)
  60 
  61         /*
  62          * Switch to gdt with 32-bit segments. This is the firmware GDT
  63          * that was installed when the kernel started executing. This
  64          * pointer was saved at the EFI stub entry point in head_64.S.
  65          */
  66         leaq    efi32_boot_gdt(%rip), %rax
  67         lgdt    (%rax)
  68 
  69         pushq   $__KERNEL_CS
  70         leaq    efi_enter32(%rip), %rax
  71         pushq   %rax
  72         lretq
  73 
  74 1:      addq    $32, %rsp
  75 
  76         lgdt    save_gdt(%rip)
  77 
  78         pop     %rbx
  79         movl    %ebx, %ss
  80         pop     %rbx
  81         movl    %ebx, %es
  82         pop     %rbx
  83         movl    %ebx, %ds
  84 
  85         /*
  86          * Convert 32-bit status code into 64-bit.
  87          */
  88         test    %rax, %rax
  89         jz      1f
  90         movl    %eax, %ecx
  91         andl    $0x0fffffff, %ecx
  92         andl    $0xf0000000, %eax
  93         shl     $32, %rax
  94         or      %rcx, %rax
  95 1:
  96         addq    $8, %rsp
  97         pop     %rbx
  98         pop     %rbp
  99         ret
 100 ENDPROC(efi64_thunk)
 101 
 102 ENTRY(efi_exit32)
 103         movq    func_rt_ptr(%rip), %rax
 104         push    %rax
 105         mov     %rdi, %rax
 106         ret
 107 ENDPROC(efi_exit32)
 108 
 109         .code32
 110 /*
 111  * EFI service pointer must be in %edi.
 112  *
 113  * The stack should represent the 32-bit calling convention.
 114  */
 115 ENTRY(efi_enter32)
 116         movl    $__KERNEL_DS, %eax
 117         movl    %eax, %ds
 118         movl    %eax, %es
 119         movl    %eax, %ss
 120 
 121         /* Reload pgtables */
 122         movl    %cr3, %eax
 123         movl    %eax, %cr3
 124 
 125         /* Disable paging */
 126         movl    %cr0, %eax
 127         btrl    $X86_CR0_PG_BIT, %eax
 128         movl    %eax, %cr0
 129 
 130         /* Disable long mode via EFER */
 131         movl    $MSR_EFER, %ecx
 132         rdmsr
 133         btrl    $_EFER_LME, %eax
 134         wrmsr
 135 
 136         call    *%edi
 137 
 138         /* We must preserve return value */
 139         movl    %eax, %edi
 140 
 141         /*
 142          * Some firmware will return with interrupts enabled. Be sure to
 143          * disable them before we switch GDTs.
 144          */
 145         cli
 146 
 147         movl    56(%esp), %eax
 148         movl    %eax, 2(%eax)
 149         lgdtl   (%eax)
 150 
 151         movl    %cr4, %eax
 152         btsl    $(X86_CR4_PAE_BIT), %eax
 153         movl    %eax, %cr4
 154 
 155         movl    %cr3, %eax
 156         movl    %eax, %cr3
 157 
 158         movl    $MSR_EFER, %ecx
 159         rdmsr
 160         btsl    $_EFER_LME, %eax
 161         wrmsr
 162 
 163         xorl    %eax, %eax
 164         lldt    %ax
 165 
 166         movl    60(%esp), %eax
 167         pushl   $__KERNEL_CS
 168         pushl   %eax
 169 
 170         /* Enable paging */
 171         movl    %cr0, %eax
 172         btsl    $X86_CR0_PG_BIT, %eax
 173         movl    %eax, %cr0
 174         lret
 175 ENDPROC(efi_enter32)
 176 
 177         .data
 178         .balign 8
 179         .global efi32_boot_gdt
 180 efi32_boot_gdt: .word   0
 181                 .quad   0
 182 
 183 save_gdt:       .word   0
 184                 .quad   0
 185 func_rt_ptr:    .quad   0
 186 
 187         .global efi_gdt64
 188 efi_gdt64:
 189         .word   efi_gdt64_end - efi_gdt64
 190         .long   0                       /* Filled out by user */
 191         .word   0
 192         .quad   0x0000000000000000      /* NULL descriptor */
 193         .quad   0x00af9a000000ffff      /* __KERNEL_CS */
 194         .quad   0x00cf92000000ffff      /* __KERNEL_DS */
 195         .quad   0x0080890000000000      /* TS descriptor */
 196         .quad   0x0000000000000000      /* TS continued */
 197 efi_gdt64_end:

/* [<][>][^][v][top][bottom][index][help] */