Merge tag 'gpio-v5.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux...
[linux-2.6-microblaze.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         leaq    1f(%rip), %rbp
31         leaq    efi_gdt64(%rip), %rbx
32         movl    %ebx, 2(%rbx)           /* Fixup the gdt base address */
33
34         movl    %ds, %eax
35         push    %rax
36         movl    %es, %eax
37         push    %rax
38         movl    %ss, %eax
39         push    %rax
40
41         /*
42          * Convert x86-64 ABI params to i386 ABI
43          */
44         subq    $32, %rsp
45         movl    %esi, 0x0(%rsp)
46         movl    %edx, 0x4(%rsp)
47         movl    %ecx, 0x8(%rsp)
48         movl    %r8d, 0xc(%rsp)
49         movl    %r9d, 0x10(%rsp)
50
51         sgdt    0x14(%rsp)
52
53         /*
54          * Switch to gdt with 32-bit segments. This is the firmware GDT
55          * that was installed when the kernel started executing. This
56          * pointer was saved at the EFI stub entry point in head_64.S.
57          *
58          * Pass the saved DS selector to the 32-bit code, and use far return to
59          * restore the saved CS selector.
60          */
61         leaq    efi32_boot_gdt(%rip), %rax
62         lgdt    (%rax)
63
64         movzwl  efi32_boot_ds(%rip), %edx
65         movzwq  efi32_boot_cs(%rip), %rax
66         pushq   %rax
67         leaq    efi_enter32(%rip), %rax
68         pushq   %rax
69         lretq
70
71 1:      lgdt    0x14(%rsp)
72         addq    $32, %rsp
73         movq    %rdi, %rax
74
75         pop     %rbx
76         movl    %ebx, %ss
77         pop     %rbx
78         movl    %ebx, %es
79         pop     %rbx
80         movl    %ebx, %ds
81         /* Clear out 32-bit selector from FS and GS */
82         xorl    %ebx, %ebx
83         movl    %ebx, %fs
84         movl    %ebx, %gs
85
86         /*
87          * Convert 32-bit status code into 64-bit.
88          */
89         roll    $1, %eax
90         rorq    $1, %rax
91
92         pop     %rbx
93         pop     %rbp
94         ret
95 SYM_FUNC_END(__efi64_thunk)
96
97         .code32
98 /*
99  * EFI service pointer must be in %edi.
100  *
101  * The stack should represent the 32-bit calling convention.
102  */
103 SYM_FUNC_START_LOCAL(efi_enter32)
104         /* Load firmware selector into data and stack segment registers */
105         movl    %edx, %ds
106         movl    %edx, %es
107         movl    %edx, %fs
108         movl    %edx, %gs
109         movl    %edx, %ss
110
111         /* Reload pgtables */
112         movl    %cr3, %eax
113         movl    %eax, %cr3
114
115         /* Disable paging */
116         movl    %cr0, %eax
117         btrl    $X86_CR0_PG_BIT, %eax
118         movl    %eax, %cr0
119
120         /* Disable long mode via EFER */
121         movl    $MSR_EFER, %ecx
122         rdmsr
123         btrl    $_EFER_LME, %eax
124         wrmsr
125
126         call    *%edi
127
128         /* We must preserve return value */
129         movl    %eax, %edi
130
131         /*
132          * Some firmware will return with interrupts enabled. Be sure to
133          * disable them before we switch GDTs.
134          */
135         cli
136
137         lgdtl   (%ebx)
138
139         movl    %cr4, %eax
140         btsl    $(X86_CR4_PAE_BIT), %eax
141         movl    %eax, %cr4
142
143         movl    %cr3, %eax
144         movl    %eax, %cr3
145
146         movl    $MSR_EFER, %ecx
147         rdmsr
148         btsl    $_EFER_LME, %eax
149         wrmsr
150
151         xorl    %eax, %eax
152         lldt    %ax
153
154         pushl   $__KERNEL_CS
155         pushl   %ebp
156
157         /* Enable paging */
158         movl    %cr0, %eax
159         btsl    $X86_CR0_PG_BIT, %eax
160         movl    %eax, %cr0
161         lret
162 SYM_FUNC_END(efi_enter32)
163
164         .data
165         .balign 8
166 SYM_DATA_START(efi32_boot_gdt)
167         .word   0
168         .quad   0
169 SYM_DATA_END(efi32_boot_gdt)
170
171 SYM_DATA_START(efi32_boot_cs)
172         .word   0
173 SYM_DATA_END(efi32_boot_cs)
174
175 SYM_DATA_START(efi32_boot_ds)
176         .word   0
177 SYM_DATA_END(efi32_boot_ds)
178
179 SYM_DATA_START(efi_gdt64)
180         .word   efi_gdt64_end - efi_gdt64
181         .long   0                       /* Filled out by user */
182         .word   0
183         .quad   0x0000000000000000      /* NULL descriptor */
184         .quad   0x00af9a000000ffff      /* __KERNEL_CS */
185         .quad   0x00cf92000000ffff      /* __KERNEL_DS */
186         .quad   0x0080890000000000      /* TS descriptor */
187         .quad   0x0000000000000000      /* TS continued */
188 SYM_DATA_END_LABEL(efi_gdt64, SYM_L_LOCAL, efi_gdt64_end)