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