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