xref: /OK3568_Linux_fs/kernel/arch/x86/boot/compressed/efi_thunk_64.S (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun/* SPDX-License-Identifier: GPL-2.0 */
2*4882a593Smuzhiyun/*
3*4882a593Smuzhiyun * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Early support for invoking 32-bit EFI services from a 64-bit kernel.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Because this thunking occurs before ExitBootServices() we have to
8*4882a593Smuzhiyun * restore the firmware's 32-bit GDT before we make EFI serivce calls,
9*4882a593Smuzhiyun * since the firmware's 32-bit IDT is still currently installed and it
10*4882a593Smuzhiyun * needs to be able to service interrupts.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * On the plus side, we don't have to worry about mangling 64-bit
13*4882a593Smuzhiyun * addresses into 32-bits because we're executing with an identity
14*4882a593Smuzhiyun * mapped pagetable and haven't transitioned to 64-bit virtual addresses
15*4882a593Smuzhiyun * yet.
16*4882a593Smuzhiyun */
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun#include <linux/linkage.h>
19*4882a593Smuzhiyun#include <asm/msr.h>
20*4882a593Smuzhiyun#include <asm/page_types.h>
21*4882a593Smuzhiyun#include <asm/processor-flags.h>
22*4882a593Smuzhiyun#include <asm/segment.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun	.code64
25*4882a593Smuzhiyun	.text
26*4882a593SmuzhiyunSYM_FUNC_START(__efi64_thunk)
27*4882a593Smuzhiyun	push	%rbp
28*4882a593Smuzhiyun	push	%rbx
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun	leaq	1f(%rip), %rbp
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun	movl	%ds, %eax
33*4882a593Smuzhiyun	push	%rax
34*4882a593Smuzhiyun	movl	%es, %eax
35*4882a593Smuzhiyun	push	%rax
36*4882a593Smuzhiyun	movl	%ss, %eax
37*4882a593Smuzhiyun	push	%rax
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun	/*
40*4882a593Smuzhiyun	 * Convert x86-64 ABI params to i386 ABI
41*4882a593Smuzhiyun	 */
42*4882a593Smuzhiyun	subq	$32, %rsp
43*4882a593Smuzhiyun	movl	%esi, 0x0(%rsp)
44*4882a593Smuzhiyun	movl	%edx, 0x4(%rsp)
45*4882a593Smuzhiyun	movl	%ecx, 0x8(%rsp)
46*4882a593Smuzhiyun	movl	%r8d, 0xc(%rsp)
47*4882a593Smuzhiyun	movl	%r9d, 0x10(%rsp)
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun	leaq	0x14(%rsp), %rbx
50*4882a593Smuzhiyun	sgdt	(%rbx)
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun	/*
53*4882a593Smuzhiyun	 * Switch to gdt with 32-bit segments. This is the firmware GDT
54*4882a593Smuzhiyun	 * that was installed when the kernel started executing. This
55*4882a593Smuzhiyun	 * pointer was saved at the EFI stub entry point in head_64.S.
56*4882a593Smuzhiyun	 *
57*4882a593Smuzhiyun	 * Pass the saved DS selector to the 32-bit code, and use far return to
58*4882a593Smuzhiyun	 * restore the saved CS selector.
59*4882a593Smuzhiyun	 */
60*4882a593Smuzhiyun	leaq	efi32_boot_gdt(%rip), %rax
61*4882a593Smuzhiyun	lgdt	(%rax)
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun	movzwl	efi32_boot_ds(%rip), %edx
64*4882a593Smuzhiyun	movzwq	efi32_boot_cs(%rip), %rax
65*4882a593Smuzhiyun	pushq	%rax
66*4882a593Smuzhiyun	leaq	efi_enter32(%rip), %rax
67*4882a593Smuzhiyun	pushq	%rax
68*4882a593Smuzhiyun	lretq
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun1:	addq	$32, %rsp
71*4882a593Smuzhiyun	movq	%rdi, %rax
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun	pop	%rbx
74*4882a593Smuzhiyun	movl	%ebx, %ss
75*4882a593Smuzhiyun	pop	%rbx
76*4882a593Smuzhiyun	movl	%ebx, %es
77*4882a593Smuzhiyun	pop	%rbx
78*4882a593Smuzhiyun	movl	%ebx, %ds
79*4882a593Smuzhiyun	/* Clear out 32-bit selector from FS and GS */
80*4882a593Smuzhiyun	xorl	%ebx, %ebx
81*4882a593Smuzhiyun	movl	%ebx, %fs
82*4882a593Smuzhiyun	movl	%ebx, %gs
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun	/*
85*4882a593Smuzhiyun	 * Convert 32-bit status code into 64-bit.
86*4882a593Smuzhiyun	 */
87*4882a593Smuzhiyun	roll	$1, %eax
88*4882a593Smuzhiyun	rorq	$1, %rax
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun	pop	%rbx
91*4882a593Smuzhiyun	pop	%rbp
92*4882a593Smuzhiyun	RET
93*4882a593SmuzhiyunSYM_FUNC_END(__efi64_thunk)
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun	.code32
96*4882a593Smuzhiyun/*
97*4882a593Smuzhiyun * EFI service pointer must be in %edi.
98*4882a593Smuzhiyun *
99*4882a593Smuzhiyun * The stack should represent the 32-bit calling convention.
100*4882a593Smuzhiyun */
101*4882a593SmuzhiyunSYM_FUNC_START_LOCAL(efi_enter32)
102*4882a593Smuzhiyun	/* Load firmware selector into data and stack segment registers */
103*4882a593Smuzhiyun	movl	%edx, %ds
104*4882a593Smuzhiyun	movl	%edx, %es
105*4882a593Smuzhiyun	movl	%edx, %fs
106*4882a593Smuzhiyun	movl	%edx, %gs
107*4882a593Smuzhiyun	movl	%edx, %ss
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun	/* Reload pgtables */
110*4882a593Smuzhiyun	movl	%cr3, %eax
111*4882a593Smuzhiyun	movl	%eax, %cr3
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun	/* Disable paging */
114*4882a593Smuzhiyun	movl	%cr0, %eax
115*4882a593Smuzhiyun	btrl	$X86_CR0_PG_BIT, %eax
116*4882a593Smuzhiyun	movl	%eax, %cr0
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun	/* Disable long mode via EFER */
119*4882a593Smuzhiyun	movl	$MSR_EFER, %ecx
120*4882a593Smuzhiyun	rdmsr
121*4882a593Smuzhiyun	btrl	$_EFER_LME, %eax
122*4882a593Smuzhiyun	wrmsr
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun	call	*%edi
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun	/* We must preserve return value */
127*4882a593Smuzhiyun	movl	%eax, %edi
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun	/*
130*4882a593Smuzhiyun	 * Some firmware will return with interrupts enabled. Be sure to
131*4882a593Smuzhiyun	 * disable them before we switch GDTs.
132*4882a593Smuzhiyun	 */
133*4882a593Smuzhiyun	cli
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun	lgdtl	(%ebx)
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun	movl	%cr4, %eax
138*4882a593Smuzhiyun	btsl	$(X86_CR4_PAE_BIT), %eax
139*4882a593Smuzhiyun	movl	%eax, %cr4
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun	movl	%cr3, %eax
142*4882a593Smuzhiyun	movl	%eax, %cr3
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun	movl	$MSR_EFER, %ecx
145*4882a593Smuzhiyun	rdmsr
146*4882a593Smuzhiyun	btsl	$_EFER_LME, %eax
147*4882a593Smuzhiyun	wrmsr
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun	xorl	%eax, %eax
150*4882a593Smuzhiyun	lldt	%ax
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun	pushl	$__KERNEL_CS
153*4882a593Smuzhiyun	pushl	%ebp
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun	/* Enable paging */
156*4882a593Smuzhiyun	movl	%cr0, %eax
157*4882a593Smuzhiyun	btsl	$X86_CR0_PG_BIT, %eax
158*4882a593Smuzhiyun	movl	%eax, %cr0
159*4882a593Smuzhiyun	lret
160*4882a593SmuzhiyunSYM_FUNC_END(efi_enter32)
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun	.data
163*4882a593Smuzhiyun	.balign	8
164*4882a593SmuzhiyunSYM_DATA_START(efi32_boot_gdt)
165*4882a593Smuzhiyun	.word	0
166*4882a593Smuzhiyun	.quad	0
167*4882a593SmuzhiyunSYM_DATA_END(efi32_boot_gdt)
168*4882a593Smuzhiyun
169*4882a593SmuzhiyunSYM_DATA_START(efi32_boot_cs)
170*4882a593Smuzhiyun	.word	0
171*4882a593SmuzhiyunSYM_DATA_END(efi32_boot_cs)
172*4882a593Smuzhiyun
173*4882a593SmuzhiyunSYM_DATA_START(efi32_boot_ds)
174*4882a593Smuzhiyun	.word	0
175*4882a593SmuzhiyunSYM_DATA_END(efi32_boot_ds)
176