# | |
# Copyright (c) 2010, Intel Corporation. All rights reserved.<BR> | |
# This program and the accompanying materials | |
# are licensed and made available under the terms and conditions of the BSD License | |
# which accompanies this distribution. The full text of the license may be found at | |
# http://opensource.org/licenses/bsd-license.php | |
# | |
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
# | |
# | |
# Module Name: | |
# | |
# AsmDispatchExecute.asm | |
# | |
# Abstract: | |
# | |
# This is the assembly code to transition from long mode to compatibility mode to execute 32-bit code and then | |
# transit back to long mode. | |
# | |
#------------------------------------------------------------------------------- | |
#---------------------------------------------------------------------------- | |
# Procedure: AsmExecute32BitCode | |
# | |
# Input: None | |
# | |
# Output: None | |
# | |
# Prototype: EFI_STATUS | |
# AsmExecute32BitCode ( | |
# IN UINT64 Function, | |
# IN UINT64 Param1, | |
# IN UINT64 Param2, | |
# IN IA32_DESCRIPTOR *InternalGdtr | |
# ); | |
# | |
# | |
# Description: A thunk function to execute 32-bit code in long mode. | |
# | |
#---------------------------------------------------------------------------- | |
ASM_GLOBAL ASM_PFX(AsmExecute32BitCode) | |
ASM_PFX(AsmExecute32BitCode): | |
# | |
# save orignal GDTR and CS | |
# | |
movl %ds, %eax | |
push %rax | |
movl %cs, %eax | |
push %rax | |
subq $0x10, %rsp | |
sgdt (%rsp) | |
# | |
# load internal GDT | |
# | |
lgdt (%r9) | |
# | |
# Save general purpose register and rflag register | |
# | |
pushfq | |
push %rdi | |
push %rsi | |
push %rbp | |
push %rbx | |
# | |
# save CR3 | |
# | |
movq %cr3, %rax | |
movq %rax, %rbp | |
# | |
# Prepare the CS and return address for the transition from 32-bit to 64-bit mode | |
# | |
movq $0x10, %rax # load long mode selector | |
shl $32, %rax | |
lea ReloadCS(%rip), %r9 #Assume the ReloadCS is under 4G | |
orq %r9, %rax | |
push %rax | |
# | |
# Save parameters for 32-bit function call | |
# | |
movq %r8, %rax | |
shl $32, %rax | |
orq %rdx, %rax | |
push %rax | |
# | |
# save the 32-bit function entry and the return address into stack which will be | |
# retrieve in compatibility mode. | |
# | |
lea ReturnBack(%rip), %rax #Assume the ReloadCS is under 4G | |
shl $32, %rax | |
orq %rcx, %rax | |
push %rax | |
# | |
# let rax save DS | |
# | |
movq $0x18, %rax | |
# | |
# Change to Compatible Segment | |
# | |
movq $8, %rcx # load compatible mode selector | |
shl $32, %rcx | |
lea Compatible(%rip), %rdx # assume address < 4G | |
orq %rdx, %rcx | |
push %rcx | |
.byte 0xcb # retf | |
Compatible: | |
# reload DS/ES/SS to make sure they are correct referred to current GDT | |
movw %ax, %ds | |
movw %ax, %es | |
movw %ax, %ss | |
# | |
# Disable paging | |
# | |
movq %cr0, %rcx | |
btc $31, %ecx | |
movq %rcx, %cr0 | |
# | |
# Clear EFER.LME | |
# | |
movl $0xC0000080, %ecx | |
rdmsr | |
btc $8, %eax | |
wrmsr | |
# Now we are in protected mode | |
# | |
# Call 32-bit function. Assume the function entry address and parameter value is less than 4G | |
# | |
pop %rax # Here is the function entry | |
# | |
# Now the parameter is at the bottom of the stack, then call in to IA32 function. | |
# | |
jmp *%rax | |
ReturnBack: | |
pop %rcx # drop param1 | |
pop %rcx # drop param2 | |
# | |
# restore CR4 | |
# | |
movq %cr4, %rax | |
bts $5, %eax | |
movq %rax, %cr4 | |
# | |
# restore CR3 | |
# | |
movl %ebp, %eax | |
movq %rax, %cr3 | |
# | |
# Set EFER.LME to re-enable ia32-e | |
# | |
movl $0xC0000080, %ecx | |
rdmsr | |
bts $8, %eax | |
wrmsr | |
# | |
# Enable paging | |
# | |
movq %cr0, %rax | |
bts $31, %eax | |
mov %rax, %cr0 | |
# Now we are in compatible mode | |
# | |
# Reload cs register | |
# | |
.byte 0xcb # retf | |
ReloadCS: | |
# | |
# Now we're in Long Mode | |
# | |
# | |
# Restore C register and eax hold the return status from 32-bit function. | |
# Note: Do not touch rax from now which hold the return value from IA32 function | |
# | |
pop %rbx | |
pop %rbp | |
pop %rsi | |
pop %rdi | |
popfq | |
# | |
# Switch to orignal GDT and CS. here rsp is pointer to the orignal GDT descriptor. | |
# | |
lgdt (%rsp) | |
# | |
# drop GDT descriptor in stack | |
# | |
addq $0x10, %rsp | |
# | |
# switch to orignal CS and GDTR | |
# | |
pop %r9 # get CS | |
shl $32, %r9 # rcx[32..47] <- Cs | |
lea ReturnToLongMode(%rip), %rcx | |
orq %r9, %rcx | |
push %rcx | |
.byte 0xcb # retf | |
ReturnToLongMode: | |
# | |
# Reload original DS/ES/SS | |
# | |
pop %rcx | |
movl %ecx, %ds | |
movl %ecx, %es | |
movl %ecx, %ss | |
ret | |