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