]> asedeno.scripts.mit.edu Git - linux.git/blob - arch/x86/boot/compressed/efi_thunk_64.S
efi/libstub/x86: Avoid globals to store context during mixed mode calls
[linux.git] / arch / x86 / boot / compressed / efi_thunk_64.S
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 identity
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 SYM_FUNC_START(efi64_thunk)
27         push    %rbp
28         push    %rbx
29
30         subq    $8, %rsp
31         leaq    1f(%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         /*
59          * Switch to gdt with 32-bit segments. This is the firmware GDT
60          * that was installed when the kernel started executing. This
61          * pointer was saved at the EFI stub entry point in head_64.S.
62          */
63         leaq    efi32_boot_gdt(%rip), %rax
64         lgdt    (%rax)
65
66         pushq   $__KERNEL_CS
67         leaq    efi_enter32(%rip), %rax
68         pushq   %rax
69         lretq
70
71 1:      addq    $32, %rsp
72         movq    %rdi, %rax
73
74         lgdt    save_gdt(%rip)
75
76         pop     %rbx
77         movl    %ebx, %ss
78         pop     %rbx
79         movl    %ebx, %es
80         pop     %rbx
81         movl    %ebx, %ds
82
83         /*
84          * Convert 32-bit status code into 64-bit.
85          */
86         test    %rax, %rax
87         jz      1f
88         movl    %eax, %ecx
89         andl    $0x0fffffff, %ecx
90         andl    $0xf0000000, %eax
91         shl     $32, %rax
92         or      %rcx, %rax
93 1:
94         addq    $8, %rsp
95         pop     %rbx
96         pop     %rbp
97         ret
98 SYM_FUNC_END(efi64_thunk)
99
100         .code32
101 /*
102  * EFI service pointer must be in %edi.
103  *
104  * The stack should represent the 32-bit calling convention.
105  */
106 SYM_FUNC_START_LOCAL(efi_enter32)
107         movl    $__KERNEL_DS, %eax
108         movl    %eax, %ds
109         movl    %eax, %es
110         movl    %eax, %ss
111
112         /* Reload pgtables */
113         movl    %cr3, %eax
114         movl    %eax, %cr3
115
116         /* Disable paging */
117         movl    %cr0, %eax
118         btrl    $X86_CR0_PG_BIT, %eax
119         movl    %eax, %cr0
120
121         /* Disable long mode via EFER */
122         movl    $MSR_EFER, %ecx
123         rdmsr
124         btrl    $_EFER_LME, %eax
125         wrmsr
126
127         call    *%edi
128
129         /* We must preserve return value */
130         movl    %eax, %edi
131
132         /*
133          * Some firmware will return with interrupts enabled. Be sure to
134          * disable them before we switch GDTs.
135          */
136         cli
137
138         movl    56(%esp), %eax
139         movl    %eax, 2(%eax)
140         lgdtl   (%eax)
141
142         movl    %cr4, %eax
143         btsl    $(X86_CR4_PAE_BIT), %eax
144         movl    %eax, %cr4
145
146         movl    %cr3, %eax
147         movl    %eax, %cr3
148
149         movl    $MSR_EFER, %ecx
150         rdmsr
151         btsl    $_EFER_LME, %eax
152         wrmsr
153
154         xorl    %eax, %eax
155         lldt    %ax
156
157         movl    60(%esp), %eax
158         pushl   $__KERNEL_CS
159         pushl   %eax
160
161         /* Enable paging */
162         movl    %cr0, %eax
163         btsl    $X86_CR0_PG_BIT, %eax
164         movl    %eax, %cr0
165         lret
166 SYM_FUNC_END(efi_enter32)
167
168         .data
169         .balign 8
170 SYM_DATA_START(efi32_boot_gdt)
171         .word   0
172         .quad   0
173 SYM_DATA_END(efi32_boot_gdt)
174
175 SYM_DATA_START_LOCAL(save_gdt)
176         .word   0
177         .quad   0
178 SYM_DATA_END(save_gdt)
179
180 SYM_DATA_START(efi_gdt64)
181         .word   efi_gdt64_end - efi_gdt64
182         .long   0                       /* Filled out by user */
183         .word   0
184         .quad   0x0000000000000000      /* NULL descriptor */
185         .quad   0x00af9a000000ffff      /* __KERNEL_CS */
186         .quad   0x00cf92000000ffff      /* __KERNEL_DS */
187         .quad   0x0080890000000000      /* TS descriptor */
188         .quad   0x0000000000000000      /* TS continued */
189 SYM_DATA_END_LABEL(efi_gdt64, SYM_L_LOCAL, efi_gdt64_end)