mirror of https://github.com/acidanthera/audk.git
UefiCpuPkg: Add PiSmmCpuDxeSmm module X64 files
Add module that initializes a CPU for the SMM environment and installs the first level SMI handler. This module along with the SMM IPL and SMM Core provide the services required for DXE_SMM_DRIVERS to register hardware and software SMI handlers. CPU specific features are abstracted through the SmmCpuFeaturesLib Platform specific features are abstracted through the SmmCpuPlatformHookLib Several PCDs are added to enable/disable features and configure settings for the PiSmmCpuDxeSmm module [jeff.fan@intel.com: Fix code style issues reported by ECC] Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Michael Kinney <michael.d.kinney@intel.com> Reviewed-by: Jeff Fan <jeff.fan@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18647 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
parent
7947da3ccc
commit
427e357342
|
@ -0,0 +1,204 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (c) 2006 - 2015, 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:
|
||||
#
|
||||
# MpFuncs.S
|
||||
#
|
||||
# Abstract:
|
||||
#
|
||||
# This is the assembly code for Multi-processor S3 support
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
.equ VacantFlag, 0x0
|
||||
.equ NotVacantFlag, 0xff
|
||||
|
||||
.equ LockLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart
|
||||
.equ StackStartAddressLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart + 0x08
|
||||
.equ StackSizeLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart + 0x10
|
||||
.equ CProcedureLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart + 0x18
|
||||
.equ GdtrLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart + 0x20
|
||||
.equ IdtrLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart + 0x2A
|
||||
.equ BufferStartLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart + 0x34
|
||||
.equ Cr3OffsetLocation, RendezvousFunnelProcEnd - RendezvousFunnelProcStart + 0x38
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
#RendezvousFunnelProc procedure follows. All APs execute their procedure. This
|
||||
#procedure serializes all the AP processors through an Init sequence. It must be
|
||||
#noted that APs arrive here very raw...ie: real mode, no stack.
|
||||
#ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC
|
||||
#IS IN MACHINE CODE.
|
||||
#-------------------------------------------------------------------------------------
|
||||
#RendezvousFunnelProc (&WakeUpBuffer,MemAddress);
|
||||
|
||||
.code:
|
||||
|
||||
ASM_GLOBAL ASM_PFX(RendezvousFunnelProc)
|
||||
ASM_PFX(RendezvousFunnelProc):
|
||||
RendezvousFunnelProcStart:
|
||||
|
||||
# At this point CS = 0x(vv00) and ip= 0x0.
|
||||
|
||||
.byte 0x8c,0xc8 # mov ax, cs
|
||||
.byte 0x8e,0xd8 # mov ds, ax
|
||||
.byte 0x8e,0xc0 # mov es, ax
|
||||
.byte 0x8e,0xd0 # mov ss, ax
|
||||
.byte 0x33,0xc0 # xor ax, ax
|
||||
.byte 0x8e,0xe0 # mov fs, ax
|
||||
.byte 0x8e,0xe8 # mov gs, ax
|
||||
|
||||
flat32Start:
|
||||
|
||||
.byte 0xBE
|
||||
.word BufferStartLocation
|
||||
.byte 0x66,0x8B,0x14 # mov edx,dword ptr [si] ; EDX is keeping the start address of wakeup buffer
|
||||
|
||||
.byte 0xBE
|
||||
.word Cr3OffsetLocation
|
||||
.byte 0x66,0x8B,0xC # mov ecx,dword ptr [si] ; ECX is keeping the value of CR3
|
||||
|
||||
.byte 0xBE
|
||||
.word GdtrLocation
|
||||
.byte 0x66 # db 66h
|
||||
.byte 0x2E,0xF,0x1,0x14 # lgdt fword ptr cs:[si]
|
||||
|
||||
.byte 0xBE
|
||||
.word IdtrLocation
|
||||
.byte 0x66 # db 66h
|
||||
.byte 0x2E,0xF,0x1,0x1C # lidt fword ptr cs:[si]
|
||||
|
||||
.byte 0x33,0xC0 # xor ax, ax
|
||||
.byte 0x8E,0xD8 # mov ds, ax
|
||||
|
||||
.byte 0xF,0x20,0xC0 # mov eax, cr0 ; Get control register 0
|
||||
.byte 0x66,0x83,0xC8,0x1 # or eax, 000000001h ; Set PE bit (bit #0)
|
||||
.byte 0xF,0x22,0xC0 # mov cr0, eax
|
||||
|
||||
FLAT32_JUMP:
|
||||
|
||||
.byte 0x66,0x67,0xEA # far jump
|
||||
.long 0x0 # 32-bit offset
|
||||
.word 0x20 # 16-bit selector
|
||||
|
||||
PMODE_ENTRY: # protected mode entry point
|
||||
|
||||
.byte 0x66,0xB8,0x18,0x0 # mov ax, 18h
|
||||
.byte 0x66,0x8E,0xD8 # mov ds, ax
|
||||
.byte 0x66,0x8E,0xC0 # mov es, ax
|
||||
.byte 0x66,0x8E,0xE0 # mov fs, ax
|
||||
.byte 0x66,0x8E,0xE8 # mov gs, ax
|
||||
.byte 0x66,0x8E,0xD0 # mov ss, ax ; Flat mode setup.
|
||||
|
||||
.byte 0xF,0x20,0xE0 # mov eax, cr4
|
||||
.byte 0xF,0xBA,0xE8,0x5 # bts eax, 5
|
||||
.byte 0xF,0x22,0xE0 # mov cr4, eax
|
||||
|
||||
.byte 0xF,0x22,0xD9 # mov cr3, ecx
|
||||
|
||||
.byte 0x8B,0xF2 # mov esi, edx ; Save wakeup buffer address
|
||||
|
||||
.byte 0xB9
|
||||
.long 0xC0000080 # mov ecx, 0c0000080h ; EFER MSR number.
|
||||
.byte 0xF,0x32 # rdmsr ; Read EFER.
|
||||
.byte 0xF,0xBA,0xE8,0x8 # bts eax, 8 ; Set LME=1.
|
||||
.byte 0xF,0x30 # wrmsr ; Write EFER.
|
||||
|
||||
.byte 0xF,0x20,0xC0 # mov eax, cr0 ; Read CR0.
|
||||
.byte 0xF,0xBA,0xE8,0x1F # bts eax, 31 ; Set PG=1.
|
||||
.byte 0xF,0x22,0xC0 # mov cr0, eax ; Write CR0.
|
||||
|
||||
LONG_JUMP:
|
||||
|
||||
.byte 0x67,0xEA # far jump
|
||||
.long 0x0 # 32-bit offset
|
||||
.word 0x38 # 16-bit selector
|
||||
|
||||
LongModeStart:
|
||||
|
||||
movw $0x30,%ax
|
||||
.byte 0x66
|
||||
movw %ax,%ds
|
||||
.byte 0x66
|
||||
movw %ax,%es
|
||||
.byte 0x66
|
||||
movw %ax,%ss
|
||||
|
||||
movl %esi,%edi
|
||||
addl $LockLocation, %edi
|
||||
movb $NotVacantFlag, %al
|
||||
TestLock:
|
||||
xchgb (%edi), %al
|
||||
cmpb $NotVacantFlag, %al
|
||||
jz TestLock
|
||||
|
||||
ProgramStack:
|
||||
|
||||
movl %esi,%edi
|
||||
addl $StackSizeLocation, %edi
|
||||
movq (%edi), %rax
|
||||
movl %esi,%edi
|
||||
addl $StackStartAddressLocation, %edi
|
||||
addq (%edi), %rax
|
||||
movq %rax, %rsp
|
||||
movq %rax, (%edi)
|
||||
|
||||
Releaselock:
|
||||
|
||||
movb $VacantFlag, %al
|
||||
movl %esi,%edi
|
||||
addl $LockLocation, %edi
|
||||
xchgb (%edi), %al
|
||||
|
||||
#
|
||||
# Call assembly function to initialize FPU.
|
||||
#
|
||||
movabsq $ASM_PFX(InitializeFloatingPointUnits), %rax
|
||||
subq $0x20, %rsp
|
||||
call *%rax
|
||||
addq $0x20, %rsp
|
||||
#
|
||||
# Call C Function
|
||||
#
|
||||
movl %esi,%edi
|
||||
addl $CProcedureLocation, %edi
|
||||
movq (%edi), %rax
|
||||
|
||||
testq %rax, %rax
|
||||
jz GoToSleep
|
||||
|
||||
subq $0x20, %rsp
|
||||
call *%rax
|
||||
addq $0x20, %rsp
|
||||
|
||||
GoToSleep:
|
||||
cli
|
||||
hlt
|
||||
jmp .-2
|
||||
|
||||
RendezvousFunnelProcEnd:
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# AsmGetAddressMap (&AddressMap);
|
||||
#-------------------------------------------------------------------------------------
|
||||
# comments here for definition of address map
|
||||
ASM_GLOBAL ASM_PFX(AsmGetAddressMap)
|
||||
ASM_PFX(AsmGetAddressMap):
|
||||
movabsq $RendezvousFunnelProcStart, %rax
|
||||
movq %rax, (%rcx)
|
||||
movq $(PMODE_ENTRY - RendezvousFunnelProcStart), 0x08(%rcx)
|
||||
movq $(FLAT32_JUMP - RendezvousFunnelProcStart), 0x10(%rcx)
|
||||
movq $(RendezvousFunnelProcEnd - RendezvousFunnelProcStart), 0x18(%rcx)
|
||||
movq $(LongModeStart - RendezvousFunnelProcStart), 0x20(%rcx)
|
||||
movq $(LONG_JUMP - RendezvousFunnelProcStart), 0x28(%rcx)
|
||||
ret
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
;------------------------------------------------------------------------------ ;
|
||||
; Copyright (c) 2006 - 2015, 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:
|
||||
;
|
||||
; MpFuncs.asm
|
||||
;
|
||||
; Abstract:
|
||||
;
|
||||
; This is the assembly code for Multi-processor S3 support
|
||||
;
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
EXTERN InitializeFloatingPointUnits:PROC
|
||||
|
||||
VacantFlag Equ 00h
|
||||
NotVacantFlag Equ 0ffh
|
||||
|
||||
LockLocation equ RendezvousFunnelProcEnd - RendezvousFunnelProcStart
|
||||
StackStartAddressLocation equ LockLocation + 08h
|
||||
StackSizeLocation equ LockLocation + 10h
|
||||
CProcedureLocation equ LockLocation + 18h
|
||||
GdtrLocation equ LockLocation + 20h
|
||||
IdtrLocation equ LockLocation + 2Ah
|
||||
BufferStartLocation equ LockLocation + 34h
|
||||
Cr3OffsetLocation equ LockLocation + 38h
|
||||
|
||||
;-------------------------------------------------------------------------------------
|
||||
;RendezvousFunnelProc procedure follows. All APs execute their procedure. This
|
||||
;procedure serializes all the AP processors through an Init sequence. It must be
|
||||
;noted that APs arrive here very raw...ie: real mode, no stack.
|
||||
;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC
|
||||
;IS IN MACHINE CODE.
|
||||
;-------------------------------------------------------------------------------------
|
||||
;RendezvousFunnelProc (&WakeUpBuffer,MemAddress);
|
||||
|
||||
;text SEGMENT
|
||||
.code
|
||||
|
||||
RendezvousFunnelProc PROC
|
||||
RendezvousFunnelProcStart::
|
||||
|
||||
; At this point CS = 0x(vv00) and ip= 0x0.
|
||||
|
||||
db 8ch, 0c8h ; mov ax, cs
|
||||
db 8eh, 0d8h ; mov ds, ax
|
||||
db 8eh, 0c0h ; mov es, ax
|
||||
db 8eh, 0d0h ; mov ss, ax
|
||||
db 33h, 0c0h ; xor ax, ax
|
||||
db 8eh, 0e0h ; mov fs, ax
|
||||
db 8eh, 0e8h ; mov gs, ax
|
||||
|
||||
flat32Start::
|
||||
|
||||
db 0BEh
|
||||
dw BufferStartLocation ; mov si, BufferStartLocation
|
||||
db 66h, 8Bh, 14h ; mov edx,dword ptr [si] ; EDX is keeping the start address of wakeup buffer
|
||||
|
||||
db 0BEh
|
||||
dw Cr3OffsetLocation ; mov si, Cr3Location
|
||||
db 66h, 8Bh, 0Ch ; mov ecx,dword ptr [si] ; ECX is keeping the value of CR3
|
||||
|
||||
db 0BEh
|
||||
dw GdtrLocation ; mov si, GdtrProfile
|
||||
db 66h ; db 66h
|
||||
db 2Eh, 0Fh, 01h, 14h ; lgdt fword ptr cs:[si]
|
||||
|
||||
db 0BEh
|
||||
dw IdtrLocation ; mov si, IdtrProfile
|
||||
db 66h ; db 66h
|
||||
db 2Eh, 0Fh, 01h, 1Ch ; lidt fword ptr cs:[si]
|
||||
|
||||
db 33h, 0C0h ; xor ax, ax
|
||||
db 8Eh, 0D8h ; mov ds, ax
|
||||
|
||||
db 0Fh, 20h, 0C0h ; mov eax, cr0 ; Get control register 0
|
||||
db 66h, 83h, 0C8h, 01h ; or eax, 000000001h ; Set PE bit (bit #0)
|
||||
db 0Fh, 22h, 0C0h ; mov cr0, eax
|
||||
|
||||
FLAT32_JUMP::
|
||||
|
||||
db 66h, 67h, 0EAh ; far jump
|
||||
dd 0h ; 32-bit offset
|
||||
dw 20h ; 16-bit selector
|
||||
|
||||
PMODE_ENTRY:: ; protected mode entry point
|
||||
|
||||
db 66h, 0B8h, 18h, 00h ; mov ax, 18h
|
||||
db 66h, 8Eh, 0D8h ; mov ds, ax
|
||||
db 66h, 8Eh, 0C0h ; mov es, ax
|
||||
db 66h, 8Eh, 0E0h ; mov fs, ax
|
||||
db 66h, 8Eh, 0E8h ; mov gs, ax
|
||||
db 66h, 8Eh, 0D0h ; mov ss, ax ; Flat mode setup.
|
||||
|
||||
db 0Fh, 20h, 0E0h ; mov eax, cr4
|
||||
db 0Fh, 0BAh, 0E8h, 05h ; bts eax, 5
|
||||
db 0Fh, 22h, 0E0h ; mov cr4, eax
|
||||
|
||||
db 0Fh, 22h, 0D9h ; mov cr3, ecx
|
||||
|
||||
db 8Bh, 0F2h ; mov esi, edx ; Save wakeup buffer address
|
||||
|
||||
db 0B9h
|
||||
dd 0C0000080h ; mov ecx, 0c0000080h ; EFER MSR number.
|
||||
db 0Fh, 32h ; rdmsr ; Read EFER.
|
||||
db 0Fh, 0BAh, 0E8h, 08h ; bts eax, 8 ; Set LME=1.
|
||||
db 0Fh, 30h ; wrmsr ; Write EFER.
|
||||
|
||||
db 0Fh, 20h, 0C0h ; mov eax, cr0 ; Read CR0.
|
||||
db 0Fh, 0BAh, 0E8h, 1Fh ; bts eax, 31 ; Set PG=1.
|
||||
db 0Fh, 22h, 0C0h ; mov cr0, eax ; Write CR0.
|
||||
|
||||
LONG_JUMP::
|
||||
|
||||
db 67h, 0EAh ; far jump
|
||||
dd 0h ; 32-bit offset
|
||||
dw 38h ; 16-bit selector
|
||||
|
||||
LongModeStart::
|
||||
|
||||
mov ax, 30h
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
|
||||
mov edi, esi
|
||||
add edi, LockLocation
|
||||
mov al, NotVacantFlag
|
||||
TestLock::
|
||||
xchg byte ptr [edi], al
|
||||
cmp al, NotVacantFlag
|
||||
jz TestLock
|
||||
|
||||
ProgramStack::
|
||||
|
||||
mov edi, esi
|
||||
add edi, StackSizeLocation
|
||||
mov rax, qword ptr [edi]
|
||||
mov edi, esi
|
||||
add edi, StackStartAddressLocation
|
||||
add rax, qword ptr [edi]
|
||||
mov rsp, rax
|
||||
mov qword ptr [edi], rax
|
||||
|
||||
Releaselock::
|
||||
|
||||
mov al, VacantFlag
|
||||
mov edi, esi
|
||||
add edi, LockLocation
|
||||
xchg byte ptr [edi], al
|
||||
|
||||
;
|
||||
; Call assembly function to initialize FPU.
|
||||
;
|
||||
mov rax, InitializeFloatingPointUnits
|
||||
sub rsp, 20h
|
||||
call rax
|
||||
add rsp, 20h
|
||||
|
||||
;
|
||||
; Call C Function
|
||||
;
|
||||
mov edi, esi
|
||||
add edi, CProcedureLocation
|
||||
mov rax, qword ptr [edi]
|
||||
|
||||
test rax, rax
|
||||
jz GoToSleep
|
||||
|
||||
sub rsp, 20h
|
||||
call rax
|
||||
add rsp, 20h
|
||||
|
||||
GoToSleep::
|
||||
cli
|
||||
hlt
|
||||
jmp $-2
|
||||
|
||||
RendezvousFunnelProcEnd::
|
||||
RendezvousFunnelProc ENDP
|
||||
|
||||
|
||||
;-------------------------------------------------------------------------------------
|
||||
; AsmGetAddressMap (&AddressMap);
|
||||
;-------------------------------------------------------------------------------------
|
||||
; comments here for definition of address map
|
||||
AsmGetAddressMap PROC
|
||||
mov rax, offset RendezvousFunnelProcStart
|
||||
mov qword ptr [rcx], rax
|
||||
mov qword ptr [rcx+8h], PMODE_ENTRY - RendezvousFunnelProcStart
|
||||
mov qword ptr [rcx+10h], FLAT32_JUMP - RendezvousFunnelProcStart
|
||||
mov qword ptr [rcx+18h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart
|
||||
mov qword ptr [rcx+20h], LongModeStart - RendezvousFunnelProcStart
|
||||
mov qword ptr [rcx+28h], LONG_JUMP - RendezvousFunnelProcStart
|
||||
ret
|
||||
|
||||
AsmGetAddressMap ENDP
|
||||
|
||||
END
|
|
@ -0,0 +1,692 @@
|
|||
/** @file
|
||||
Page Fault (#PF) handler for X64 processors
|
||||
|
||||
Copyright (c) 2009 - 2015, 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.
|
||||
|
||||
**/
|
||||
|
||||
#include "PiSmmCpuDxeSmm.h"
|
||||
|
||||
#define PAGE_TABLE_PAGES 8
|
||||
#define ACC_MAX_BIT BIT3
|
||||
LIST_ENTRY mPagePool = INITIALIZE_LIST_HEAD_VARIABLE (mPagePool);
|
||||
SPIN_LOCK mPFLock;
|
||||
BOOLEAN m1GPageTableSupport = FALSE;
|
||||
|
||||
/**
|
||||
Check if 1-GByte pages is supported by processor or not.
|
||||
|
||||
@retval TRUE 1-GByte pages is supported.
|
||||
@retval FALSE 1-GByte pages is not supported.
|
||||
|
||||
**/
|
||||
BOOLEAN
|
||||
Is1GPageSupport (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
UINT32 RegEax;
|
||||
UINT32 RegEdx;
|
||||
|
||||
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
|
||||
if (RegEax >= 0x80000001) {
|
||||
AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
|
||||
if ((RegEdx & BIT26) != 0) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
Set sub-entries number in entry.
|
||||
|
||||
@param[in, out] Entry Pointer to entry
|
||||
@param[in] SubEntryNum Sub-entries number based on 0:
|
||||
0 means there is 1 sub-entry under this entry
|
||||
0x1ff means there is 512 sub-entries under this entry
|
||||
|
||||
**/
|
||||
VOID
|
||||
SetSubEntriesNum (
|
||||
IN OUT UINT64 *Entry,
|
||||
IN UINT64 SubEntryNum
|
||||
)
|
||||
{
|
||||
//
|
||||
// Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
|
||||
//
|
||||
*Entry = BitFieldWrite64 (*Entry, 52, 60, SubEntryNum);
|
||||
}
|
||||
|
||||
/**
|
||||
Return sub-entries number in entry.
|
||||
|
||||
@param[in] Entry Pointer to entry
|
||||
|
||||
@return Sub-entries number based on 0:
|
||||
0 means there is 1 sub-entry under this entry
|
||||
0x1ff means there is 512 sub-entries under this entry
|
||||
**/
|
||||
UINT64
|
||||
GetSubEntriesNum (
|
||||
IN UINT64 *Entry
|
||||
)
|
||||
{
|
||||
//
|
||||
// Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
|
||||
//
|
||||
return BitFieldRead64 (*Entry, 52, 60);
|
||||
}
|
||||
|
||||
/**
|
||||
Create PageTable for SMM use.
|
||||
|
||||
@return The address of PML4 (to set CR3).
|
||||
|
||||
**/
|
||||
UINT32
|
||||
SmmInitPageTable (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
EFI_PHYSICAL_ADDRESS Pages;
|
||||
UINT64 *PTEntry;
|
||||
LIST_ENTRY *FreePage;
|
||||
UINTN Index;
|
||||
UINTN PageFaultHandlerHookAddress;
|
||||
IA32_IDT_GATE_DESCRIPTOR *IdtEntry;
|
||||
|
||||
//
|
||||
// Initialize spin lock
|
||||
//
|
||||
InitializeSpinLock (&mPFLock);
|
||||
|
||||
m1GPageTableSupport = Is1GPageSupport ();
|
||||
//
|
||||
// Generate PAE page table for the first 4GB memory space
|
||||
//
|
||||
Pages = Gen4GPageTable (PAGE_TABLE_PAGES + 1);
|
||||
|
||||
//
|
||||
// Set IA32_PG_PMNT bit to mask this entry
|
||||
//
|
||||
PTEntry = (UINT64*)(UINTN)Pages;
|
||||
for (Index = 0; Index < 4; Index++) {
|
||||
PTEntry[Index] |= IA32_PG_PMNT;
|
||||
}
|
||||
|
||||
//
|
||||
// Fill Page-Table-Level4 (PML4) entry
|
||||
//
|
||||
PTEntry = (UINT64*)(UINTN)(Pages - EFI_PAGES_TO_SIZE (PAGE_TABLE_PAGES + 1));
|
||||
*PTEntry = Pages + IA32_PG_P;
|
||||
ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry));
|
||||
//
|
||||
// Set sub-entries number
|
||||
//
|
||||
SetSubEntriesNum (PTEntry, 3);
|
||||
|
||||
//
|
||||
// Add remaining pages to page pool
|
||||
//
|
||||
FreePage = (LIST_ENTRY*)(PTEntry + EFI_PAGE_SIZE / sizeof (*PTEntry));
|
||||
while ((UINTN)FreePage < Pages) {
|
||||
InsertTailList (&mPagePool, FreePage);
|
||||
FreePage += EFI_PAGE_SIZE / sizeof (*FreePage);
|
||||
}
|
||||
|
||||
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
|
||||
//
|
||||
// Set own Page Fault entry instead of the default one, because SMM Profile
|
||||
// feature depends on IRET instruction to do Single Step
|
||||
//
|
||||
PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
|
||||
IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;
|
||||
IdtEntry += EXCEPT_IA32_PAGE_FAULT;
|
||||
IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
|
||||
IdtEntry->Bits.Reserved_0 = 0;
|
||||
IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
|
||||
IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
|
||||
IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32);
|
||||
IdtEntry->Bits.Reserved_1 = 0;
|
||||
} else {
|
||||
//
|
||||
// Register Smm Page Fault Handler
|
||||
//
|
||||
SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
|
||||
}
|
||||
|
||||
//
|
||||
// Additional SMM IDT initialization for SMM stack guard
|
||||
//
|
||||
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
|
||||
InitializeIDTSmmStackGuard ();
|
||||
}
|
||||
|
||||
//
|
||||
// Return the address of PML4 (to set CR3)
|
||||
//
|
||||
return (UINT32)(UINTN)PTEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
Set access record in entry.
|
||||
|
||||
@param[in, out] Entry Pointer to entry
|
||||
@param[in] Acc Access record value
|
||||
|
||||
**/
|
||||
VOID
|
||||
SetAccNum (
|
||||
IN OUT UINT64 *Entry,
|
||||
IN UINT64 Acc
|
||||
)
|
||||
{
|
||||
//
|
||||
// Access record is saved in BIT9 to BIT11 (reserved field) in Entry
|
||||
//
|
||||
*Entry = BitFieldWrite64 (*Entry, 9, 11, Acc);
|
||||
}
|
||||
|
||||
/**
|
||||
Return access record in entry.
|
||||
|
||||
@param[in] Entry Pointer to entry
|
||||
|
||||
@return Access record value.
|
||||
|
||||
**/
|
||||
UINT64
|
||||
GetAccNum (
|
||||
IN UINT64 *Entry
|
||||
)
|
||||
{
|
||||
//
|
||||
// Access record is saved in BIT9 to BIT11 (reserved field) in Entry
|
||||
//
|
||||
return BitFieldRead64 (*Entry, 9, 11);
|
||||
}
|
||||
|
||||
/**
|
||||
Return and update the access record in entry.
|
||||
|
||||
@param[in, out] Entry Pointer to entry
|
||||
|
||||
@return Access record value.
|
||||
|
||||
**/
|
||||
UINT64
|
||||
GetAndUpdateAccNum (
|
||||
IN OUT UINT64 *Entry
|
||||
)
|
||||
{
|
||||
UINT64 Acc;
|
||||
|
||||
Acc = GetAccNum (Entry);
|
||||
if ((*Entry & IA32_PG_A) != 0) {
|
||||
//
|
||||
// If this entry has been accessed, clear access flag in Entry and update access record
|
||||
// to the initial value 7, adding ACC_MAX_BIT is to make it larger than others
|
||||
//
|
||||
*Entry &= ~(UINT64)(UINTN)IA32_PG_A;
|
||||
SetAccNum (Entry, 0x7);
|
||||
return (0x7 + ACC_MAX_BIT);
|
||||
} else {
|
||||
if (Acc != 0) {
|
||||
//
|
||||
// If the access record is not the smallest value 0, minus 1 and update the access record field
|
||||
//
|
||||
SetAccNum (Entry, Acc - 1);
|
||||
}
|
||||
}
|
||||
return Acc;
|
||||
}
|
||||
|
||||
/**
|
||||
Reclaim free pages for PageFault handler.
|
||||
|
||||
Search the whole entries tree to find the leaf entry that has the smallest
|
||||
access record value. Insert the page pointed by this leaf entry into the
|
||||
page pool. And check its upper entries if need to be inserted into the page
|
||||
pool or not.
|
||||
|
||||
**/
|
||||
VOID
|
||||
ReclaimPages (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
UINT64 *Pml4;
|
||||
UINT64 *Pdpt;
|
||||
UINT64 *Pdt;
|
||||
UINTN Pml4Index;
|
||||
UINTN PdptIndex;
|
||||
UINTN PdtIndex;
|
||||
UINTN MinPml4;
|
||||
UINTN MinPdpt;
|
||||
UINTN MinPdt;
|
||||
UINT64 MinAcc;
|
||||
UINT64 Acc;
|
||||
UINT64 SubEntriesNum;
|
||||
BOOLEAN PML4EIgnore;
|
||||
BOOLEAN PDPTEIgnore;
|
||||
UINT64 *ReleasePageAddress;
|
||||
|
||||
Pml4 = NULL;
|
||||
Pdpt = NULL;
|
||||
Pdt = NULL;
|
||||
MinAcc = (UINT64)-1;
|
||||
MinPml4 = (UINTN)-1;
|
||||
MinPdpt = (UINTN)-1;
|
||||
MinPdt = (UINTN)-1;
|
||||
Acc = 0;
|
||||
ReleasePageAddress = 0;
|
||||
|
||||
//
|
||||
// First, find the leaf entry has the smallest access record value
|
||||
//
|
||||
Pml4 = (UINT64*)(UINTN)(AsmReadCr3 () & gPhyMask);
|
||||
for (Pml4Index = 0; Pml4Index < EFI_PAGE_SIZE / sizeof (*Pml4); Pml4Index++) {
|
||||
if ((Pml4[Pml4Index] & IA32_PG_P) == 0 || (Pml4[Pml4Index] & IA32_PG_PMNT) != 0) {
|
||||
//
|
||||
// If the PML4 entry is not present or is masked, skip it
|
||||
//
|
||||
continue;
|
||||
}
|
||||
Pdpt = (UINT64*)(UINTN)(Pml4[Pml4Index] & gPhyMask);
|
||||
PML4EIgnore = FALSE;
|
||||
for (PdptIndex = 0; PdptIndex < EFI_PAGE_SIZE / sizeof (*Pdpt); PdptIndex++) {
|
||||
if ((Pdpt[PdptIndex] & IA32_PG_P) == 0 || (Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {
|
||||
//
|
||||
// If the PDPT entry is not present or is masked, skip it
|
||||
//
|
||||
if ((Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {
|
||||
//
|
||||
// If the PDPT entry is masked, we will ignore checking the PML4 entry
|
||||
//
|
||||
PML4EIgnore = TRUE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((Pdpt[PdptIndex] & IA32_PG_PS) == 0) {
|
||||
//
|
||||
// It's not 1-GByte pages entry, it should be a PDPT entry,
|
||||
// we will not check PML4 entry more
|
||||
//
|
||||
PML4EIgnore = TRUE;
|
||||
Pdt = (UINT64*)(UINTN)(Pdpt[PdptIndex] & gPhyMask);
|
||||
PDPTEIgnore = FALSE;
|
||||
for (PdtIndex = 0; PdtIndex < EFI_PAGE_SIZE / sizeof(*Pdt); PdtIndex++) {
|
||||
if ((Pdt[PdtIndex] & IA32_PG_P) == 0 || (Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {
|
||||
//
|
||||
// If the PD entry is not present or is masked, skip it
|
||||
//
|
||||
if ((Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {
|
||||
//
|
||||
// If the PD entry is masked, we will not PDPT entry more
|
||||
//
|
||||
PDPTEIgnore = TRUE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((Pdt[PdtIndex] & IA32_PG_PS) == 0) {
|
||||
//
|
||||
// It's not 2 MByte page table entry, it should be PD entry
|
||||
// we will find the entry has the smallest access record value
|
||||
//
|
||||
PDPTEIgnore = TRUE;
|
||||
Acc = GetAndUpdateAccNum (Pdt + PdtIndex);
|
||||
if (Acc < MinAcc) {
|
||||
//
|
||||
// If the PD entry has the smallest access record value,
|
||||
// save the Page address to be released
|
||||
//
|
||||
MinAcc = Acc;
|
||||
MinPml4 = Pml4Index;
|
||||
MinPdpt = PdptIndex;
|
||||
MinPdt = PdtIndex;
|
||||
ReleasePageAddress = Pdt + PdtIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!PDPTEIgnore) {
|
||||
//
|
||||
// If this PDPT entry has no PDT entries pointer to 4 KByte pages,
|
||||
// it should only has the entries point to 2 MByte Pages
|
||||
//
|
||||
Acc = GetAndUpdateAccNum (Pdpt + PdptIndex);
|
||||
if (Acc < MinAcc) {
|
||||
//
|
||||
// If the PDPT entry has the smallest access record value,
|
||||
// save the Page address to be released
|
||||
//
|
||||
MinAcc = Acc;
|
||||
MinPml4 = Pml4Index;
|
||||
MinPdpt = PdptIndex;
|
||||
MinPdt = (UINTN)-1;
|
||||
ReleasePageAddress = Pdpt + PdptIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!PML4EIgnore) {
|
||||
//
|
||||
// If PML4 entry has no the PDPT entry pointer to 2 MByte pages,
|
||||
// it should only has the entries point to 1 GByte Pages
|
||||
//
|
||||
Acc = GetAndUpdateAccNum (Pml4 + Pml4Index);
|
||||
if (Acc < MinAcc) {
|
||||
//
|
||||
// If the PML4 entry has the smallest access record value,
|
||||
// save the Page address to be released
|
||||
//
|
||||
MinAcc = Acc;
|
||||
MinPml4 = Pml4Index;
|
||||
MinPdpt = (UINTN)-1;
|
||||
MinPdt = (UINTN)-1;
|
||||
ReleasePageAddress = Pml4 + Pml4Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Make sure one PML4/PDPT/PD entry is selected
|
||||
//
|
||||
ASSERT (MinAcc != (UINT64)-1);
|
||||
|
||||
//
|
||||
// Secondly, insert the page pointed by this entry into page pool and clear this entry
|
||||
//
|
||||
InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(*ReleasePageAddress & gPhyMask));
|
||||
*ReleasePageAddress = 0;
|
||||
|
||||
//
|
||||
// Lastly, check this entry's upper entries if need to be inserted into page pool
|
||||
// or not
|
||||
//
|
||||
while (TRUE) {
|
||||
if (MinPdt != (UINTN)-1) {
|
||||
//
|
||||
// If 4 KByte Page Table is released, check the PDPT entry
|
||||
//
|
||||
Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & gPhyMask);
|
||||
SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt);
|
||||
if (SubEntriesNum == 0) {
|
||||
//
|
||||
// Release the empty Page Directory table if there was no more 4 KByte Page Table entry
|
||||
// clear the Page directory entry
|
||||
//
|
||||
InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pdpt[MinPdpt] & gPhyMask));
|
||||
Pdpt[MinPdpt] = 0;
|
||||
//
|
||||
// Go on checking the PML4 table
|
||||
//
|
||||
MinPdt = (UINTN)-1;
|
||||
continue;
|
||||
}
|
||||
//
|
||||
// Update the sub-entries filed in PDPT entry and exit
|
||||
//
|
||||
SetSubEntriesNum (Pdpt + MinPdpt, SubEntriesNum - 1);
|
||||
break;
|
||||
}
|
||||
if (MinPdpt != (UINTN)-1) {
|
||||
//
|
||||
// One 2MB Page Table is released or Page Directory table is released, check the PML4 entry
|
||||
//
|
||||
SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4);
|
||||
if (SubEntriesNum == 0) {
|
||||
//
|
||||
// Release the empty PML4 table if there was no more 1G KByte Page Table entry
|
||||
// clear the Page directory entry
|
||||
//
|
||||
InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pml4[MinPml4] & gPhyMask));
|
||||
Pml4[MinPml4] = 0;
|
||||
MinPdpt = (UINTN)-1;
|
||||
continue;
|
||||
}
|
||||
//
|
||||
// Update the sub-entries filed in PML4 entry and exit
|
||||
//
|
||||
SetSubEntriesNum (Pml4 + MinPml4, SubEntriesNum - 1);
|
||||
break;
|
||||
}
|
||||
//
|
||||
// PLM4 table has been released before, exit it
|
||||
//
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Allocate free Page for PageFault handler use.
|
||||
|
||||
@return Page address.
|
||||
|
||||
**/
|
||||
UINT64
|
||||
AllocPage (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
UINT64 RetVal;
|
||||
|
||||
if (IsListEmpty (&mPagePool)) {
|
||||
//
|
||||
// If page pool is empty, reclaim the used pages and insert one into page pool
|
||||
//
|
||||
ReclaimPages ();
|
||||
}
|
||||
|
||||
//
|
||||
// Get one free page and remove it from page pool
|
||||
//
|
||||
RetVal = (UINT64)(UINTN)mPagePool.ForwardLink;
|
||||
RemoveEntryList (mPagePool.ForwardLink);
|
||||
//
|
||||
// Clean this page and return
|
||||
//
|
||||
ZeroMem ((VOID*)(UINTN)RetVal, EFI_PAGE_SIZE);
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Page Fault handler for SMM use.
|
||||
|
||||
**/
|
||||
VOID
|
||||
SmiDefaultPFHandler (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
UINT64 *PageTable;
|
||||
UINT64 *Pml4;
|
||||
UINT64 PFAddress;
|
||||
UINTN StartBit;
|
||||
UINTN EndBit;
|
||||
UINT64 PTIndex;
|
||||
UINTN Index;
|
||||
SMM_PAGE_SIZE_TYPE PageSize;
|
||||
UINTN NumOfPages;
|
||||
UINTN PageAttribute;
|
||||
EFI_STATUS Status;
|
||||
UINT64 *UpperEntry;
|
||||
|
||||
//
|
||||
// Set default SMM page attribute
|
||||
//
|
||||
PageSize = SmmPageSize2M;
|
||||
NumOfPages = 1;
|
||||
PageAttribute = 0;
|
||||
|
||||
EndBit = 0;
|
||||
Pml4 = (UINT64*)(AsmReadCr3 () & gPhyMask);
|
||||
PFAddress = AsmReadCr2 ();
|
||||
|
||||
Status = GetPlatformPageTableAttribute (PFAddress, &PageSize, &NumOfPages, &PageAttribute);
|
||||
//
|
||||
// If platform not support page table attribute, set default SMM page attribute
|
||||
//
|
||||
if (Status != EFI_SUCCESS) {
|
||||
PageSize = SmmPageSize2M;
|
||||
NumOfPages = 1;
|
||||
PageAttribute = 0;
|
||||
}
|
||||
if (PageSize >= MaxSmmPageSizeType) {
|
||||
PageSize = SmmPageSize2M;
|
||||
}
|
||||
if (NumOfPages > 512) {
|
||||
NumOfPages = 512;
|
||||
}
|
||||
|
||||
switch (PageSize) {
|
||||
case SmmPageSize4K:
|
||||
//
|
||||
// BIT12 to BIT20 is Page Table index
|
||||
//
|
||||
EndBit = 12;
|
||||
break;
|
||||
case SmmPageSize2M:
|
||||
//
|
||||
// BIT21 to BIT29 is Page Directory index
|
||||
//
|
||||
EndBit = 21;
|
||||
PageAttribute |= (UINTN)IA32_PG_PS;
|
||||
break;
|
||||
case SmmPageSize1G:
|
||||
if (!m1GPageTableSupport) {
|
||||
DEBUG ((EFI_D_ERROR, "1-GByte pages is not supported!"));
|
||||
ASSERT (FALSE);
|
||||
}
|
||||
//
|
||||
// BIT30 to BIT38 is Page Directory Pointer Table index
|
||||
//
|
||||
EndBit = 30;
|
||||
PageAttribute |= (UINTN)IA32_PG_PS;
|
||||
break;
|
||||
default:
|
||||
ASSERT (FALSE);
|
||||
}
|
||||
|
||||
//
|
||||
// If execute-disable is enabled, set NX bit
|
||||
//
|
||||
if (mXdEnabled) {
|
||||
PageAttribute |= IA32_PG_NX;
|
||||
}
|
||||
|
||||
for (Index = 0; Index < NumOfPages; Index++) {
|
||||
PageTable = Pml4;
|
||||
UpperEntry = NULL;
|
||||
for (StartBit = 39; StartBit > EndBit; StartBit -= 9) {
|
||||
PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);
|
||||
if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
|
||||
//
|
||||
// If the entry is not present, allocate one page from page pool for it
|
||||
//
|
||||
PageTable[PTIndex] = AllocPage () | IA32_PG_RW | IA32_PG_P;
|
||||
} else {
|
||||
//
|
||||
// Save the upper entry address
|
||||
//
|
||||
UpperEntry = PageTable + PTIndex;
|
||||
}
|
||||
//
|
||||
// BIT9 to BIT11 of entry is used to save access record,
|
||||
// initialize value is 7
|
||||
//
|
||||
PageTable[PTIndex] |= (UINT64)IA32_PG_A;
|
||||
SetAccNum (PageTable + PTIndex, 7);
|
||||
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);
|
||||
}
|
||||
|
||||
PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);
|
||||
if ((PageTable[PTIndex] & IA32_PG_P) != 0) {
|
||||
//
|
||||
// Check if the entry has already existed, this issue may occur when the different
|
||||
// size page entries created under the same entry
|
||||
//
|
||||
DEBUG ((EFI_D_ERROR, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable, PTIndex, PageTable[PTIndex]));
|
||||
DEBUG ((EFI_D_ERROR, "New page table overlapped with old page table!\n"));
|
||||
ASSERT (FALSE);
|
||||
}
|
||||
//
|
||||
// Fill the new entry
|
||||
//
|
||||
PageTable[PTIndex] = (PFAddress & gPhyMask & ~((1ull << EndBit) - 1)) |
|
||||
PageAttribute | IA32_PG_A | IA32_PG_RW | IA32_PG_P;
|
||||
if (UpperEntry != NULL) {
|
||||
SetSubEntriesNum (UpperEntry, GetSubEntriesNum (UpperEntry) + 1);
|
||||
}
|
||||
//
|
||||
// Get the next page address if we need to create more page tables
|
||||
//
|
||||
PFAddress += (1ull << EndBit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
ThePage Fault handler wrapper for SMM use.
|
||||
|
||||
@param InterruptType Defines the type of interrupt or exception that
|
||||
occurred on the processor.This parameter is processor architecture specific.
|
||||
@param SystemContext A pointer to the processor context when
|
||||
the interrupt occurred on the processor.
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
SmiPFHandler (
|
||||
IN EFI_EXCEPTION_TYPE InterruptType,
|
||||
IN EFI_SYSTEM_CONTEXT SystemContext
|
||||
)
|
||||
{
|
||||
UINTN PFAddress;
|
||||
|
||||
ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
|
||||
|
||||
AcquireSpinLock (&mPFLock);
|
||||
|
||||
PFAddress = AsmReadCr2 ();
|
||||
|
||||
//
|
||||
// If a page fault occurs in SMRAM range, it should be in a SMM stack guard page.
|
||||
//
|
||||
if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
|
||||
(PFAddress >= mCpuHotPlugData.SmrrBase) &&
|
||||
(PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {
|
||||
DEBUG ((EFI_D_ERROR, "SMM stack overflow!\n"));
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
|
||||
//
|
||||
// If a page fault occurs in SMM range
|
||||
//
|
||||
if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
|
||||
(PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
|
||||
if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {
|
||||
DEBUG ((EFI_D_ERROR, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress));
|
||||
DEBUG_CODE (
|
||||
DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);
|
||||
);
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
}
|
||||
|
||||
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
|
||||
SmmProfilePFHandler (
|
||||
SystemContext.SystemContextX64->Rip,
|
||||
SystemContext.SystemContextX64->ExceptionData
|
||||
);
|
||||
} else {
|
||||
SmiDefaultPFHandler ();
|
||||
}
|
||||
|
||||
ReleaseSpinLock (&mPFLock);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/** @file
|
||||
Semaphore mechanism to indicate to the BSP that an AP has exited SMM
|
||||
after SMBASE relocation.
|
||||
|
||||
Copyright (c) 2009 - 2015, 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.
|
||||
|
||||
**/
|
||||
|
||||
#include "PiSmmCpuDxeSmm.h"
|
||||
|
||||
extern UINT32 mSmmRelocationOriginalAddressPtr32;
|
||||
extern UINT32 mRebasedFlagAddr32;
|
||||
|
||||
UINTN mSmmRelocationOriginalAddress;
|
||||
volatile BOOLEAN *mRebasedFlag;
|
||||
|
||||
/**
|
||||
AP Semaphore operation in 32-bit mode while BSP runs in 64-bit mode.
|
||||
**/
|
||||
VOID
|
||||
SmmRelocationSemaphoreComplete32 (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Hook return address of SMM Save State so that semaphore code
|
||||
can be executed immediately after AP exits SMM to indicate to
|
||||
the BSP that an AP has exited SMM after SMBASE relocation.
|
||||
|
||||
@param[in] CpuIndex The processor index.
|
||||
@param[in] RebasedFlag A pointer to a flag that is set to TRUE
|
||||
immediately after AP exits SMM.
|
||||
|
||||
**/
|
||||
VOID
|
||||
SemaphoreHook (
|
||||
IN UINTN CpuIndex,
|
||||
IN volatile BOOLEAN *RebasedFlag
|
||||
)
|
||||
{
|
||||
SMRAM_SAVE_STATE_MAP *CpuState;
|
||||
UINTN TempValue;
|
||||
|
||||
mRebasedFlag = RebasedFlag;
|
||||
mRebasedFlagAddr32 = (UINT32)(UINTN)mRebasedFlag;
|
||||
|
||||
CpuState = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET);
|
||||
mSmmRelocationOriginalAddress = HookReturnFromSmm (
|
||||
CpuIndex,
|
||||
CpuState,
|
||||
(UINT64)(UINTN)&SmmRelocationSemaphoreComplete32,
|
||||
(UINT64)(UINTN)&SmmRelocationSemaphoreComplete
|
||||
);
|
||||
|
||||
//
|
||||
// Use temp value to fix ICC complier warning
|
||||
//
|
||||
TempValue = (UINTN)&mSmmRelocationOriginalAddress;
|
||||
mSmmRelocationOriginalAddressPtr32 = (UINT32)TempValue;
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (c) 2009 - 2015, 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:
|
||||
#
|
||||
# SmiEntry.S
|
||||
#
|
||||
# Abstract:
|
||||
#
|
||||
# Code template of the SMI handler for a particular processor
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
ASM_GLOBAL ASM_PFX(gcSmiHandlerTemplate)
|
||||
ASM_GLOBAL ASM_PFX(gcSmiHandlerSize)
|
||||
ASM_GLOBAL ASM_PFX(gSmiCr3)
|
||||
ASM_GLOBAL ASM_PFX(gSmiStack)
|
||||
ASM_GLOBAL ASM_PFX(gSmbase)
|
||||
ASM_GLOBAL ASM_PFX(FeaturePcdGet (PcdCpuSmmDebug))
|
||||
ASM_GLOBAL ASM_PFX(gSmiHandlerIdtr)
|
||||
|
||||
#
|
||||
# Constants relating to PROCESSOR_SMM_DESCRIPTOR
|
||||
#
|
||||
.equ DSC_OFFSET, 0xfb00
|
||||
.equ DSC_GDTPTR, 0x30
|
||||
.equ DSC_GDTSIZ, 0x38
|
||||
.equ DSC_CS, 14
|
||||
.equ DSC_DS, 16
|
||||
.equ DSC_SS, 18
|
||||
.equ DSC_OTHERSEG, 20
|
||||
#
|
||||
# Constants relating to CPU State Save Area
|
||||
#
|
||||
.equ SSM_DR6, 0xffd0
|
||||
.equ SSM_DR7, 0xffc8
|
||||
|
||||
.equ PROTECT_MODE_CS, 0x08
|
||||
.equ PROTECT_MODE_DS, 0x20
|
||||
.equ LONG_MODE_CS, 0x38
|
||||
.equ TSS_SEGMENT, 0x40
|
||||
.equ GDT_SIZE, 0x50
|
||||
|
||||
.text
|
||||
|
||||
ASM_PFX(gcSmiHandlerTemplate):
|
||||
|
||||
_SmiEntryPoint:
|
||||
#
|
||||
# The encoding of BX in 16-bit addressing mode is the same as of RDI in 64-
|
||||
# bit addressing mode. And that coincidence has been used in the following
|
||||
# "64-bit like" 16-bit code. Be aware that once RDI is referenced as a
|
||||
# base address register, it is actually BX that is referenced.
|
||||
#
|
||||
.byte 0xbb # mov bx, imm16
|
||||
.word _GdtDesc - _SmiEntryPoint + 0x8000
|
||||
#
|
||||
# fix GDT descriptor
|
||||
#
|
||||
.byte 0x2e,0xa1 # mov ax, cs:[offset16]
|
||||
.word DSC_OFFSET + DSC_GDTSIZ
|
||||
.byte 0x48 # dec ax
|
||||
.byte 0x2e
|
||||
movl %eax, (%rdi) # mov cs:[bx], ax
|
||||
.byte 0x66,0x2e,0xa1 # mov eax, cs:[offset16]
|
||||
.word DSC_OFFSET + DSC_GDTPTR
|
||||
.byte 0x2e
|
||||
movw %ax, 2(%rdi)
|
||||
.byte 0x66,0x2e
|
||||
lgdt (%rdi)
|
||||
#
|
||||
# Patch ProtectedMode Segment
|
||||
#
|
||||
.byte 0xb8
|
||||
.word PROTECT_MODE_CS
|
||||
.byte 0x2e
|
||||
movl %eax, -2(%rdi)
|
||||
#
|
||||
# Patch ProtectedMode entry
|
||||
#
|
||||
.byte 0x66, 0xbf # mov edi, SMBASE
|
||||
ASM_PFX(gSmbase): .space 4
|
||||
lea ((ProtectedMode - _SmiEntryPoint) + 0x8000)(%edi), %ax
|
||||
.byte 0x2e
|
||||
movw %ax, -6(%rdi)
|
||||
#
|
||||
# Switch into ProtectedMode
|
||||
#
|
||||
movq %cr0, %rbx
|
||||
.byte 0x66
|
||||
andl $0x9ffafff3, %ebx
|
||||
.byte 0x66
|
||||
orl $0x00000023, %ebx
|
||||
|
||||
movq %rbx, %cr0
|
||||
.byte 0x66, 0xea
|
||||
.space 6
|
||||
|
||||
_GdtDesc: .space 6
|
||||
|
||||
ProtectedMode:
|
||||
movw $PROTECT_MODE_DS, %ax
|
||||
movl %eax, %ds
|
||||
movl %eax, %es
|
||||
movl %eax, %fs
|
||||
movl %eax, %gs
|
||||
movl %eax, %ss
|
||||
.byte 0xbc # mov esp, imm32
|
||||
ASM_PFX(gSmiStack): .space 4
|
||||
jmp ProtFlatMode
|
||||
|
||||
ProtFlatMode:
|
||||
.byte 0xb8
|
||||
ASM_PFX(gSmiCr3): .space 4
|
||||
movq %rax, %cr3
|
||||
movl $0x668,%eax # as cr4.PGE is not set here, refresh cr3
|
||||
movq %rax, %cr4 # in PreModifyMtrrs() to flush TLB.
|
||||
# Load TSS
|
||||
subl $8, %esp # reserve room in stack
|
||||
sgdt (%rsp)
|
||||
movl 2(%rsp), %eax # eax = GDT base
|
||||
addl $8, %esp
|
||||
movl %eax, %edx
|
||||
addl $GDT_SIZE, %edx
|
||||
movb %dl, (TSS_SEGMENT + 2)(%rax)
|
||||
movb %dh, (TSS_SEGMENT + 3)(%rax)
|
||||
.byte 0xc1, 0xea, 0x10 # shr edx, 16
|
||||
movb %dl, (TSS_SEGMENT + 4)(%rax)
|
||||
movb %dh, (TSS_SEGMENT + 7)(%rax)
|
||||
movl %eax, %edx
|
||||
movb $0x89, %dl
|
||||
movb %dl, (TSS_SEGMENT + 5)(%rax) # clear busy flag
|
||||
movl $TSS_SEGMENT, %eax
|
||||
ltr %ax
|
||||
|
||||
#
|
||||
# Switch to LongMode
|
||||
#
|
||||
pushq $LONG_MODE_CS # push cs hardcore here
|
||||
call Base # push return address for retf later
|
||||
Base:
|
||||
addl $(LongMode - Base), (%rsp) # offset for far retf, seg is the 1st arg
|
||||
movl $0xc0000080, %ecx
|
||||
rdmsr
|
||||
orb $1,%ah
|
||||
wrmsr
|
||||
movq %cr0, %rbx
|
||||
btsl $31, %ebx
|
||||
movq %rbx, %cr0
|
||||
retf
|
||||
LongMode: # long mode (64-bit code) starts here
|
||||
movabsq $ASM_PFX(gSmiHandlerIdtr), %rax
|
||||
lidt (%rax)
|
||||
lea (DSC_OFFSET)(%rdi), %ebx
|
||||
movw DSC_DS(%rbx), %ax
|
||||
movl %eax,%ds
|
||||
movw DSC_OTHERSEG(%rbx), %ax
|
||||
movl %eax,%es
|
||||
movl %eax,%fs
|
||||
movl %eax,%gs
|
||||
movw DSC_SS(%rbx), %ax
|
||||
movl %eax,%ss
|
||||
# jmp _SmiHandler ; instruction is not needed
|
||||
|
||||
_SmiHandler:
|
||||
movabsq $ASM_PFX(FeaturePcdGet (PcdCpuSmmDebug)), %rax
|
||||
cmpb $0, (%rax)
|
||||
jz L1
|
||||
|
||||
.byte 0x48, 0x8b, 0x0d # mov rcx, [rip + disp32]
|
||||
.long SSM_DR6 - (. + 4 - _SmiEntryPoint + 0x8000)
|
||||
.byte 0x48, 0x8b, 0x15 # mov rdx, [rip + disp32]
|
||||
.long SSM_DR7 - (. + 4 - _SmiEntryPoint + 0x8000)
|
||||
movq %rcx, %dr6
|
||||
movq %rdx, %dr7
|
||||
L1:
|
||||
|
||||
movabsq $ASM_PFX(SmiRendezvous), %rax
|
||||
movq (%rsp), %rcx
|
||||
# Save FP registers
|
||||
|
||||
subq $0x208, %rsp
|
||||
.byte 0x48 # FXSAVE64
|
||||
fxsave (%rsp)
|
||||
|
||||
addq $-0x20, %rsp
|
||||
call *%rax
|
||||
addq $0x20, %rsp
|
||||
|
||||
#
|
||||
# Restore FP registers
|
||||
#
|
||||
.byte 0x48 # FXRSTOR64
|
||||
fxrstor (%rsp)
|
||||
|
||||
movabsq $ASM_PFX(FeaturePcdGet (PcdCpuSmmDebug)), %rax
|
||||
cmpb $0, (%rax)
|
||||
jz L2
|
||||
|
||||
movq %dr7, %rdx
|
||||
movq %dr6, %rcx
|
||||
.byte 0x48, 0x89, 0x15 # mov [rip + disp32], rdx
|
||||
.long SSM_DR7 - (. + 4 - _SmiEntryPoint + 0x8000)
|
||||
.byte 0x48, 0x89, 0x0d # mov [rip + disp32], rcx
|
||||
.long SSM_DR6 - (. + 4 - _SmiEntryPoint + 0x8000)
|
||||
L2:
|
||||
rsm
|
||||
|
||||
ASM_PFX(gcSmiHandlerSize): .word . - _SmiEntryPoint
|
|
@ -0,0 +1,221 @@
|
|||
;------------------------------------------------------------------------------ ;
|
||||
; Copyright (c) 2009 - 2015, 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:
|
||||
;
|
||||
; SmiEntry.asm
|
||||
;
|
||||
; Abstract:
|
||||
;
|
||||
; Code template of the SMI handler for a particular processor
|
||||
;
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
;
|
||||
; Variables referenced by C code
|
||||
;
|
||||
EXTERNDEF SmiRendezvous:PROC
|
||||
EXTERNDEF gcSmiHandlerTemplate:BYTE
|
||||
EXTERNDEF gcSmiHandlerSize:WORD
|
||||
EXTERNDEF gSmiCr3:DWORD
|
||||
EXTERNDEF gSmiStack:DWORD
|
||||
EXTERNDEF gSmbase:DWORD
|
||||
EXTERNDEF FeaturePcdGet (PcdCpuSmmDebug):BYTE
|
||||
EXTERNDEF gSmiHandlerIdtr:FWORD
|
||||
|
||||
|
||||
;
|
||||
; Constants relating to PROCESSOR_SMM_DESCRIPTOR
|
||||
;
|
||||
DSC_OFFSET EQU 0fb00h
|
||||
DSC_GDTPTR EQU 30h
|
||||
DSC_GDTSIZ EQU 38h
|
||||
DSC_CS EQU 14
|
||||
DSC_DS EQU 16
|
||||
DSC_SS EQU 18
|
||||
DSC_OTHERSEG EQU 20
|
||||
;
|
||||
; Constants relating to CPU State Save Area
|
||||
;
|
||||
SSM_DR6 EQU 0ffd0h
|
||||
SSM_DR7 EQU 0ffc8h
|
||||
|
||||
PROTECT_MODE_CS EQU 08h
|
||||
PROTECT_MODE_DS EQU 20h
|
||||
LONG_MODE_CS EQU 38h
|
||||
TSS_SEGMENT EQU 40h
|
||||
GDT_SIZE EQU 50h
|
||||
|
||||
.code
|
||||
|
||||
gcSmiHandlerTemplate LABEL BYTE
|
||||
|
||||
_SmiEntryPoint:
|
||||
;
|
||||
; The encoding of BX in 16-bit addressing mode is the same as of RDI in 64-
|
||||
; bit addressing mode. And that coincidence has been used in the following
|
||||
; "64-bit like" 16-bit code. Be aware that once RDI is referenced as a
|
||||
; base address register, it is actually BX that is referenced.
|
||||
;
|
||||
DB 0bbh ; mov bx, imm16
|
||||
DW offset _GdtDesc - _SmiEntryPoint + 8000h ; bx = GdtDesc offset
|
||||
; fix GDT descriptor
|
||||
DB 2eh, 0a1h ; mov ax, cs:[offset16]
|
||||
DW DSC_OFFSET + DSC_GDTSIZ
|
||||
DB 48h ; dec ax
|
||||
DB 2eh
|
||||
mov [rdi], eax ; mov cs:[bx], ax
|
||||
DB 66h, 2eh, 0a1h ; mov eax, cs:[offset16]
|
||||
DW DSC_OFFSET + DSC_GDTPTR
|
||||
DB 2eh
|
||||
mov [rdi + 2], ax ; mov cs:[bx + 2], eax
|
||||
DB 66h, 2eh
|
||||
lgdt fword ptr [rdi] ; lgdt fword ptr cs:[bx]
|
||||
; Patch ProtectedMode Segment
|
||||
DB 0b8h ; mov ax, imm16
|
||||
DW PROTECT_MODE_CS ; set AX for segment directly
|
||||
DB 2eh
|
||||
mov [rdi - 2], eax ; mov cs:[bx - 2], ax
|
||||
; Patch ProtectedMode entry
|
||||
DB 66h, 0bfh ; mov edi, SMBASE
|
||||
gSmbase DD ?
|
||||
lea ax, [edi + (@ProtectedMode - _SmiEntryPoint) + 8000h]
|
||||
DB 2eh
|
||||
mov [rdi - 6], ax ; mov cs:[bx - 6], eax
|
||||
; Switch into @ProtectedMode
|
||||
mov rbx, cr0
|
||||
DB 66h
|
||||
and ebx, 9ffafff3h
|
||||
DB 66h
|
||||
or ebx, 00000023h
|
||||
|
||||
mov cr0, rbx
|
||||
DB 66h, 0eah
|
||||
DD ?
|
||||
DW ?
|
||||
|
||||
_GdtDesc FWORD ?
|
||||
@ProtectedMode:
|
||||
mov ax, PROTECT_MODE_DS
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
DB 0bch ; mov esp, imm32
|
||||
gSmiStack DD ?
|
||||
jmp ProtFlatMode
|
||||
|
||||
ProtFlatMode:
|
||||
DB 0b8h ; mov eax, offset gSmiCr3
|
||||
gSmiCr3 DD ?
|
||||
mov cr3, rax
|
||||
mov eax, 668h ; as cr4.PGE is not set here, refresh cr3
|
||||
mov cr4, rax ; in PreModifyMtrrs() to flush TLB.
|
||||
; Load TSS
|
||||
sub esp, 8 ; reserve room in stack
|
||||
sgdt fword ptr [rsp]
|
||||
mov eax, [rsp + 2] ; eax = GDT base
|
||||
add esp, 8
|
||||
mov edx, eax
|
||||
add edx, GDT_SIZE
|
||||
mov [rax + TSS_SEGMENT + 2], dl
|
||||
mov [rax + TSS_SEGMENT + 3], dh
|
||||
DB 0c1h, 0eah, 10h ; shr edx, 16
|
||||
mov [rax + TSS_SEGMENT + 4], dl
|
||||
mov [rax + TSS_SEGMENT + 7], dh
|
||||
mov edx, eax
|
||||
mov dl, 89h
|
||||
mov [rax + TSS_SEGMENT + 5], dl ; clear busy flag
|
||||
mov eax, TSS_SEGMENT
|
||||
ltr ax
|
||||
|
||||
; Switch into @LongMode
|
||||
push LONG_MODE_CS ; push cs hardcore here
|
||||
call Base ; push return address for retf later
|
||||
Base:
|
||||
add dword ptr [rsp], @LongMode - Base; offset for far retf, seg is the 1st arg
|
||||
mov ecx, 0c0000080h
|
||||
rdmsr
|
||||
or ah, 1
|
||||
wrmsr
|
||||
mov rbx, cr0
|
||||
bts ebx, 31
|
||||
mov cr0, rbx
|
||||
retf
|
||||
@LongMode: ; long mode (64-bit code) starts here
|
||||
mov rax, offset gSmiHandlerIdtr
|
||||
lidt fword ptr [rax]
|
||||
lea ebx, [rdi + DSC_OFFSET]
|
||||
mov ax, [rbx + DSC_DS]
|
||||
mov ds, eax
|
||||
mov ax, [rbx + DSC_OTHERSEG]
|
||||
mov es, eax
|
||||
mov fs, eax
|
||||
mov gs, eax
|
||||
mov ax, [rbx + DSC_SS]
|
||||
mov ss, eax
|
||||
; jmp _SmiHandler ; instruction is not needed
|
||||
|
||||
_SmiHandler:
|
||||
;
|
||||
; The following lines restore DR6 & DR7 before running C code. They are useful
|
||||
; when you want to enable hardware breakpoints in SMM.
|
||||
;
|
||||
; NOTE: These lines might not be appreciated in runtime since they might
|
||||
; conflict with OS debugging facilities. Turn them off in RELEASE.
|
||||
;
|
||||
mov rax, offset FeaturePcdGet (PcdCpuSmmDebug) ;Get absolute address. Avoid RIP relative addressing
|
||||
cmp byte ptr [rax], 0
|
||||
jz @1
|
||||
|
||||
DB 48h, 8bh, 0dh ; mov rcx, [rip + disp32]
|
||||
DD SSM_DR6 - ($ + 4 - _SmiEntryPoint + 8000h)
|
||||
DB 48h, 8bh, 15h ; mov rdx, [rip + disp32]
|
||||
DD SSM_DR7 - ($ + 4 - _SmiEntryPoint + 8000h)
|
||||
mov dr6, rcx
|
||||
mov dr7, rdx
|
||||
@1:
|
||||
mov rcx, [rsp] ; rcx <- CpuIndex
|
||||
mov rax, SmiRendezvous ; rax <- absolute addr of SmiRedezvous
|
||||
|
||||
;
|
||||
; Save FP registers
|
||||
;
|
||||
sub rsp, 208h
|
||||
DB 48h ; FXSAVE64
|
||||
fxsave [rsp]
|
||||
|
||||
add rsp, -20h
|
||||
call rax
|
||||
add rsp, 20h
|
||||
|
||||
;
|
||||
; Restore FP registers
|
||||
;
|
||||
DB 48h ; FXRSTOR64
|
||||
fxrstor [rsp]
|
||||
|
||||
mov rax, offset FeaturePcdGet (PcdCpuSmmDebug) ;Get absolute address. Avoid RIP relative addressing
|
||||
cmp byte ptr [rax], 0
|
||||
jz @2
|
||||
|
||||
mov rdx, dr7
|
||||
mov rcx, dr6
|
||||
DB 48h, 89h, 15h ; mov [rip + disp32], rdx
|
||||
DD SSM_DR7 - ($ + 4 - _SmiEntryPoint + 8000h)
|
||||
DB 48h, 89h, 0dh ; mov [rip + disp32], rcx
|
||||
DD SSM_DR6 - ($ + 4 - _SmiEntryPoint + 8000h)
|
||||
@2:
|
||||
rsm
|
||||
|
||||
gcSmiHandlerSize DW $ - _SmiEntryPoint
|
||||
|
||||
END
|
|
@ -0,0 +1,610 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (c) 2009 - 2015, 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:
|
||||
#
|
||||
# SmiException.S
|
||||
#
|
||||
# Abstract:
|
||||
#
|
||||
# Exception handlers used in SM mode
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
ASM_GLOBAL ASM_PFX(SmiPFHandler)
|
||||
ASM_GLOBAL ASM_PFX(gSmiMtrrs)
|
||||
ASM_GLOBAL ASM_PFX(gcSmiIdtr)
|
||||
ASM_GLOBAL ASM_PFX(gcSmiGdtr)
|
||||
ASM_GLOBAL ASM_PFX(gcPsd)
|
||||
|
||||
.data
|
||||
|
||||
NullSeg: .quad 0 # reserved by architecture
|
||||
CodeSeg32:
|
||||
.word -1 # LimitLow
|
||||
.word 0 # BaseLow
|
||||
.byte 0 # BaseMid
|
||||
.byte 0x9b
|
||||
.byte 0xcf # LimitHigh
|
||||
.byte 0 # BaseHigh
|
||||
ProtModeCodeSeg32:
|
||||
.word -1 # LimitLow
|
||||
.word 0 # BaseLow
|
||||
.byte 0 # BaseMid
|
||||
.byte 0x9b
|
||||
.byte 0xcf # LimitHigh
|
||||
.byte 0 # BaseHigh
|
||||
ProtModeSsSeg32:
|
||||
.word -1 # LimitLow
|
||||
.word 0 # BaseLow
|
||||
.byte 0 # BaseMid
|
||||
.byte 0x93
|
||||
.byte 0xcf # LimitHigh
|
||||
.byte 0 # BaseHigh
|
||||
DataSeg32:
|
||||
.word -1 # LimitLow
|
||||
.word 0 # BaseLow
|
||||
.byte 0 # BaseMid
|
||||
.byte 0x93
|
||||
.byte 0xcf # LimitHigh
|
||||
.byte 0 # BaseHigh
|
||||
CodeSeg16:
|
||||
.word -1
|
||||
.word 0
|
||||
.byte 0
|
||||
.byte 0x9b
|
||||
.byte 0x8f
|
||||
.byte 0
|
||||
DataSeg16:
|
||||
.word -1
|
||||
.word 0
|
||||
.byte 0
|
||||
.byte 0x93
|
||||
.byte 0x8f
|
||||
.byte 0
|
||||
CodeSeg64:
|
||||
.word -1 # LimitLow
|
||||
.word 0 # BaseLow
|
||||
.byte 0 # BaseMid
|
||||
.byte 0x9b
|
||||
.byte 0xaf # LimitHigh
|
||||
.byte 0 # BaseHigh
|
||||
# TSS Segment for X64 specially
|
||||
TssSeg:
|
||||
.word TSS_DESC_SIZE # LimitLow
|
||||
.word 0 # BaseLow
|
||||
.byte 0 # BaseMid
|
||||
.byte 0x89
|
||||
.byte 0xDB # LimitHigh
|
||||
.byte 0 # BaseHigh
|
||||
.long 0 # BaseUpper
|
||||
.long 0 # Reserved
|
||||
.equ GDT_SIZE, .- NullSeg
|
||||
|
||||
TssDescriptor:
|
||||
.space 104, 0
|
||||
.equ TSS_DESC_SIZE, .- TssDescriptor
|
||||
|
||||
#
|
||||
# This structure serves as a template for all processors.
|
||||
#
|
||||
ASM_PFX(gcPsd):
|
||||
.ascii "PSDSIG "
|
||||
.word PSD_SIZE
|
||||
.word 2
|
||||
.word 1 << 2
|
||||
.word CODE_SEL
|
||||
.word DATA_SEL
|
||||
.word DATA_SEL
|
||||
.word DATA_SEL
|
||||
.word 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0 # fixed in InitializeMpServiceData()
|
||||
.quad NullSeg
|
||||
.long GDT_SIZE
|
||||
.long 0
|
||||
.space 24, 0
|
||||
.quad ASM_PFX(gSmiMtrrs)
|
||||
.equ PSD_SIZE, . - ASM_PFX(gcPsd)
|
||||
|
||||
#
|
||||
# CODE & DATA segments for SMM runtime
|
||||
#
|
||||
.equ CODE_SEL, CodeSeg64 - NullSeg
|
||||
.equ DATA_SEL, DataSeg32 - NullSeg
|
||||
.equ CODE32_SEL, CodeSeg32 - NullSeg
|
||||
|
||||
ASM_PFX(gcSmiGdtr):
|
||||
.word GDT_SIZE - 1
|
||||
.quad NullSeg
|
||||
|
||||
ASM_PFX(gcSmiIdtr):
|
||||
.word IDT_SIZE - 1
|
||||
.quad _SmiIDT
|
||||
|
||||
|
||||
#
|
||||
# Here is the IDT. There are 32 (not 255) entries in it since only processor
|
||||
# generated exceptions will be handled.
|
||||
#
|
||||
_SmiIDT:
|
||||
# The following segment repeats 32 times:
|
||||
# No. 1
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 2
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 3
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 4
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 5
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 6
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 7
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 8
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 9
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 10
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 11
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 12
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 13
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 14
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 15
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 16
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 17
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 18
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 19
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 20
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 21
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 22
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 23
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 24
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 25
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 26
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 27
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 28
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 29
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 30
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 31
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
# No. 32
|
||||
.word 0 # Offset 0:15
|
||||
.word CODE_SEL
|
||||
.byte 0 # Unused
|
||||
.byte 0x8e # Interrupt Gate, Present
|
||||
.word 0 # Offset 16:31
|
||||
.quad 0 # Offset 32:63
|
||||
|
||||
_SmiIDTEnd:
|
||||
|
||||
.equ IDT_SIZE, (_SmiIDTEnd - _SmiIDT)
|
||||
|
||||
.text
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# _SmiExceptionEntryPoints is the collection of exception entry points followed
|
||||
# by a common exception handler.
|
||||
#
|
||||
# Stack frame would be as follows as specified in IA32 manuals:
|
||||
# +---------------------+ <-- 16-byte aligned ensured by processor
|
||||
# + Old SS +
|
||||
# +---------------------+
|
||||
# + Old RSP +
|
||||
# +---------------------+
|
||||
# + RFlags +
|
||||
# +---------------------+
|
||||
# + CS +
|
||||
# +---------------------+
|
||||
# + RIP +
|
||||
# +---------------------+
|
||||
# + Error Code +
|
||||
# +---------------------+
|
||||
# + Vector Number +
|
||||
# +---------------------+
|
||||
# + RBP +
|
||||
# +---------------------+ <-- RBP, 16-byte aligned
|
||||
#
|
||||
# RSP set to odd multiple of 8 at @CommonEntryPoint means ErrCode PRESENT
|
||||
#------------------------------------------------------------------------------
|
||||
ASM_GLOBAL ASM_PFX(PageFaultIdtHandlerSmmProfile)
|
||||
ASM_PFX(PageFaultIdtHandlerSmmProfile):
|
||||
pushq $0x0e # Page Fault
|
||||
.byte 0x40, 0xf6, 0xc4, 0x08 #test spl, 8
|
||||
jnz L1
|
||||
pushq (%rsp)
|
||||
movq $0, 8(%rsp)
|
||||
L1:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
|
||||
#
|
||||
# Since here the stack pointer is 16-byte aligned, so
|
||||
# EFI_FX_SAVE_STATE_X64 of EFI_SYSTEM_CONTEXT_x64
|
||||
# is 16-byte aligned
|
||||
#
|
||||
|
||||
## UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
|
||||
## UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %r11
|
||||
pushq %r10
|
||||
pushq %r9
|
||||
pushq %r8
|
||||
pushq %rax
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
pushq %rbx
|
||||
pushq 48(%rbp) # RSP
|
||||
pushq (%rbp) # RBP
|
||||
pushq %rsi
|
||||
pushq %rdi
|
||||
|
||||
## UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero
|
||||
movzwq 56(%rbp), %rax
|
||||
pushq %rax # for ss
|
||||
movzwq 32(%rbp), %rax
|
||||
pushq %rax # for cs
|
||||
movq %ds, %rax
|
||||
pushq %rax
|
||||
movq %es, %rax
|
||||
pushq %rax
|
||||
movq %fs, %rax
|
||||
pushq %rax
|
||||
movq %gs, %rax
|
||||
pushq %rax
|
||||
|
||||
## UINT64 Rip;
|
||||
pushq 24(%rbp)
|
||||
|
||||
## UINT64 Gdtr[2], Idtr[2];
|
||||
subq $16, %rsp
|
||||
sidt (%rsp)
|
||||
subq $16, %rsp
|
||||
sgdt (%rsp)
|
||||
|
||||
## UINT64 Ldtr, Tr;
|
||||
xorq %rax, %rax
|
||||
strw %ax
|
||||
pushq %rax
|
||||
sldtw %ax
|
||||
pushq %rax
|
||||
|
||||
## UINT64 RFlags;
|
||||
pushq 40(%rbp)
|
||||
|
||||
## UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
|
||||
movq %cr8, %rax
|
||||
pushq %rax
|
||||
movq %cr4, %rax
|
||||
orq $0x208, %rax
|
||||
movq %rax, %cr4
|
||||
pushq %rax
|
||||
movq %cr3, %rax
|
||||
pushq %rax
|
||||
movq %cr2, %rax
|
||||
pushq %rax
|
||||
xorq %rax, %rax
|
||||
pushq %rax
|
||||
movq %cr0, %rax
|
||||
pushq %rax
|
||||
|
||||
## UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
|
||||
movq %dr7, %rax
|
||||
pushq %rax
|
||||
movq %dr6, %rax
|
||||
pushq %rax
|
||||
movq %dr3, %rax
|
||||
pushq %rax
|
||||
movq %dr2, %rax
|
||||
pushq %rax
|
||||
movq %dr1, %rax
|
||||
pushq %rax
|
||||
movq %dr0, %rax
|
||||
pushq %rax
|
||||
|
||||
## FX_SAVE_STATE_X64 FxSaveState;
|
||||
|
||||
subq $512, %rsp
|
||||
movq %rsp, %rdi
|
||||
.byte 0xf, 0xae, 0x7 # fxsave [rdi]
|
||||
|
||||
# UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear
|
||||
cld
|
||||
|
||||
## UINT32 ExceptionData;
|
||||
pushq 16(%rbp)
|
||||
|
||||
## call into exception handler
|
||||
movq 8(%rbp), %rcx
|
||||
movabsq $ASM_PFX(SmiPFHandler), %rax
|
||||
|
||||
## Prepare parameter and call
|
||||
movq %rsp, %rdx
|
||||
#
|
||||
# Per X64 calling convention, allocate maximum parameter stack space
|
||||
# and make sure RSP is 16-byte aligned
|
||||
#
|
||||
subq $4 * 8 + 8, %rsp
|
||||
call *%rax
|
||||
addq $4 * 8 + 8, %rsp
|
||||
jmp L5
|
||||
|
||||
L5:
|
||||
## UINT64 ExceptionData;
|
||||
addq $8, %rsp
|
||||
|
||||
## FX_SAVE_STATE_X64 FxSaveState;
|
||||
|
||||
movq %rsp, %rsi
|
||||
.byte 0xf, 0xae, 0xe # fxrstor [rsi]
|
||||
addq $512, %rsp
|
||||
|
||||
## UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
|
||||
## Skip restoration of DRx registers to support debuggers
|
||||
## that set breakpoints in interrupt/exception context
|
||||
addq $8 * 6, %rsp
|
||||
|
||||
## UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
|
||||
popq %rax
|
||||
movq %rax, %cr0
|
||||
addq $8, %rsp # not for Cr1
|
||||
popq %rax
|
||||
movq %rax, %cr2
|
||||
popq %rax
|
||||
movq %rax, %cr3
|
||||
popq %rax
|
||||
movq %rax, %cr4
|
||||
popq %rax
|
||||
movq %rax, %cr8
|
||||
|
||||
## UINT64 RFlags;
|
||||
popq 40(%rbp)
|
||||
|
||||
## UINT64 Ldtr, Tr;
|
||||
## UINT64 Gdtr[2], Idtr[2];
|
||||
## Best not let anyone mess with these particular registers...
|
||||
addq $48, %rsp
|
||||
|
||||
## UINT64 Rip;
|
||||
popq 24(%rbp)
|
||||
|
||||
## UINT64 Gs, Fs, Es, Ds, Cs, Ss;
|
||||
popq %rax
|
||||
# mov gs, rax ; not for gs
|
||||
popq %rax
|
||||
# mov fs, rax ; not for fs
|
||||
# (X64 will not use fs and gs, so we do not restore it)
|
||||
popq %rax
|
||||
movq %rax, %es
|
||||
popq %rax
|
||||
movq %rax, %ds
|
||||
popq 32(%rbp) # for cs
|
||||
popq 56(%rbp) # for ss
|
||||
|
||||
## UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
|
||||
## UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
popq %rdi
|
||||
popq %rsi
|
||||
addq $8, %rsp # not for rbp
|
||||
popq 48(%rbp) # for rsp
|
||||
popq %rbx
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
popq %rax
|
||||
popq %r8
|
||||
popq %r9
|
||||
popq %r10
|
||||
popq %r11
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
|
||||
movq %rbp, %rsp
|
||||
|
||||
# Enable TF bit after page fault handler runs
|
||||
btsl $8, 40(%rsp) #RFLAGS
|
||||
|
||||
popq %rbp
|
||||
addq $16, %rsp # skip INT# & ErrCode
|
||||
iretq
|
||||
|
||||
ASM_GLOBAL ASM_PFX(InitializeIDTSmmStackGuard)
|
||||
ASM_PFX(InitializeIDTSmmStackGuard):
|
||||
# If SMM Stack Guard feature is enabled, set the IST field of
|
||||
# the interrupt gate for Page Fault Exception to be 1
|
||||
#
|
||||
movabsq $_SmiIDT + 14 * 16, %rax
|
||||
movb $1, 4(%rax)
|
||||
ret
|
|
@ -0,0 +1,413 @@
|
|||
;------------------------------------------------------------------------------ ;
|
||||
; Copyright (c) 2009 - 2015, 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:
|
||||
;
|
||||
; SmiException.asm
|
||||
;
|
||||
; Abstract:
|
||||
;
|
||||
; Exception handlers used in SM mode
|
||||
;
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
EXTERNDEF SmiPFHandler:PROC
|
||||
EXTERNDEF gSmiMtrrs:QWORD
|
||||
EXTERNDEF gcSmiIdtr:FWORD
|
||||
EXTERNDEF gcSmiGdtr:FWORD
|
||||
EXTERNDEF gcPsd:BYTE
|
||||
|
||||
.const
|
||||
|
||||
NullSeg DQ 0 ; reserved by architecture
|
||||
CodeSeg32 LABEL QWORD
|
||||
DW -1 ; LimitLow
|
||||
DW 0 ; BaseLow
|
||||
DB 0 ; BaseMid
|
||||
DB 9bh
|
||||
DB 0cfh ; LimitHigh
|
||||
DB 0 ; BaseHigh
|
||||
ProtModeCodeSeg32 LABEL QWORD
|
||||
DW -1 ; LimitLow
|
||||
DW 0 ; BaseLow
|
||||
DB 0 ; BaseMid
|
||||
DB 9bh
|
||||
DB 0cfh ; LimitHigh
|
||||
DB 0 ; BaseHigh
|
||||
ProtModeSsSeg32 LABEL QWORD
|
||||
DW -1 ; LimitLow
|
||||
DW 0 ; BaseLow
|
||||
DB 0 ; BaseMid
|
||||
DB 93h
|
||||
DB 0cfh ; LimitHigh
|
||||
DB 0 ; BaseHigh
|
||||
DataSeg32 LABEL QWORD
|
||||
DW -1 ; LimitLow
|
||||
DW 0 ; BaseLow
|
||||
DB 0 ; BaseMid
|
||||
DB 93h
|
||||
DB 0cfh ; LimitHigh
|
||||
DB 0 ; BaseHigh
|
||||
CodeSeg16 LABEL QWORD
|
||||
DW -1
|
||||
DW 0
|
||||
DB 0
|
||||
DB 9bh
|
||||
DB 8fh
|
||||
DB 0
|
||||
DataSeg16 LABEL QWORD
|
||||
DW -1
|
||||
DW 0
|
||||
DB 0
|
||||
DB 93h
|
||||
DB 8fh
|
||||
DB 0
|
||||
CodeSeg64 LABEL QWORD
|
||||
DW -1 ; LimitLow
|
||||
DW 0 ; BaseLow
|
||||
DB 0 ; BaseMid
|
||||
DB 9bh
|
||||
DB 0afh ; LimitHigh
|
||||
DB 0 ; BaseHigh
|
||||
; TSS Segment for X64 specially
|
||||
TssSeg LABEL QWORD
|
||||
DW TSS_DESC_SIZE ; LimitLow
|
||||
DW 0 ; BaseLow
|
||||
DB 0 ; BaseMid
|
||||
DB 89h
|
||||
DB 080h ; LimitHigh
|
||||
DB 0 ; BaseHigh
|
||||
DD 0 ; BaseUpper
|
||||
DD 0 ; Reserved
|
||||
GDT_SIZE = $ - offset NullSeg
|
||||
|
||||
; Create TSS Descriptor just after GDT
|
||||
TssDescriptor LABEL BYTE
|
||||
DD 0 ; Reserved
|
||||
DQ 0 ; RSP0
|
||||
DQ 0 ; RSP1
|
||||
DQ 0 ; RSP2
|
||||
DD 0 ; Reserved
|
||||
DD 0 ; Reserved
|
||||
DQ 0 ; IST1
|
||||
DQ 0 ; IST2
|
||||
DQ 0 ; IST3
|
||||
DQ 0 ; IST4
|
||||
DQ 0 ; IST5
|
||||
DQ 0 ; IST6
|
||||
DQ 0 ; IST7
|
||||
DD 0 ; Reserved
|
||||
DD 0 ; Reserved
|
||||
DW 0 ; Reserved
|
||||
DW 0 ; I/O Map Base Address
|
||||
TSS_DESC_SIZE = $ - offset TssDescriptor
|
||||
|
||||
;
|
||||
; This structure serves as a template for all processors.
|
||||
;
|
||||
gcPsd LABEL BYTE
|
||||
DB 'PSDSIG '
|
||||
DW PSD_SIZE
|
||||
DW 2
|
||||
DW 1 SHL 2
|
||||
DW CODE_SEL
|
||||
DW DATA_SEL
|
||||
DW DATA_SEL
|
||||
DW DATA_SEL
|
||||
DW 0
|
||||
DQ 0
|
||||
DQ 0
|
||||
DQ 0 ; fixed in InitializeMpServiceData()
|
||||
DQ offset NullSeg
|
||||
DD GDT_SIZE
|
||||
DD 0
|
||||
DB 24 dup (0)
|
||||
DQ offset gSmiMtrrs
|
||||
PSD_SIZE = $ - offset gcPsd
|
||||
|
||||
;
|
||||
; CODE & DATA segments for SMM runtime
|
||||
;
|
||||
CODE_SEL = offset CodeSeg64 - offset NullSeg
|
||||
DATA_SEL = offset DataSeg32 - offset NullSeg
|
||||
CODE32_SEL = offset CodeSeg32 - offset NullSeg
|
||||
|
||||
gcSmiGdtr LABEL FWORD
|
||||
DW GDT_SIZE - 1
|
||||
DQ offset NullSeg
|
||||
|
||||
gcSmiIdtr LABEL FWORD
|
||||
DW IDT_SIZE - 1
|
||||
DQ offset _SmiIDT
|
||||
|
||||
.data
|
||||
|
||||
;
|
||||
; Here is the IDT. There are 32 (not 255) entries in it since only processor
|
||||
; generated exceptions will be handled.
|
||||
;
|
||||
_SmiIDT:
|
||||
REPEAT 32
|
||||
DW 0 ; Offset 0:15
|
||||
DW CODE_SEL ; Segment selector
|
||||
DB 0 ; Unused
|
||||
DB 8eh ; Interrupt Gate, Present
|
||||
DW 0 ; Offset 16:31
|
||||
DQ 0 ; Offset 32:63
|
||||
ENDM
|
||||
_SmiIDTEnd:
|
||||
|
||||
IDT_SIZE = (offset _SmiIDTEnd - offset _SmiIDT)
|
||||
|
||||
.code
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; _SmiExceptionEntryPoints is the collection of exception entry points followed
|
||||
; by a common exception handler.
|
||||
;
|
||||
; Stack frame would be as follows as specified in IA32 manuals:
|
||||
;
|
||||
; +---------------------+ <-- 16-byte aligned ensured by processor
|
||||
; + Old SS +
|
||||
; +---------------------+
|
||||
; + Old RSP +
|
||||
; +---------------------+
|
||||
; + RFlags +
|
||||
; +---------------------+
|
||||
; + CS +
|
||||
; +---------------------+
|
||||
; + RIP +
|
||||
; +---------------------+
|
||||
; + Error Code +
|
||||
; +---------------------+
|
||||
; + Vector Number +
|
||||
; +---------------------+
|
||||
; + RBP +
|
||||
; +---------------------+ <-- RBP, 16-byte aligned
|
||||
;
|
||||
; RSP set to odd multiple of 8 at @CommonEntryPoint means ErrCode PRESENT
|
||||
;------------------------------------------------------------------------------
|
||||
PageFaultIdtHandlerSmmProfile PROC
|
||||
push 0eh ; Page Fault
|
||||
test spl, 8 ; odd multiple of 8 => ErrCode present
|
||||
jnz @F
|
||||
push [rsp] ; duplicate INT# if no ErrCode
|
||||
mov qword ptr [rsp + 8], 0
|
||||
@@:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
;
|
||||
; Since here the stack pointer is 16-byte aligned, so
|
||||
; EFI_FX_SAVE_STATE_X64 of EFI_SYSTEM_CONTEXT_x64
|
||||
; is 16-byte aligned
|
||||
;
|
||||
|
||||
;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
|
||||
;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
push r15
|
||||
push r14
|
||||
push r13
|
||||
push r12
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push rax
|
||||
push rcx
|
||||
push rdx
|
||||
push rbx
|
||||
push qword ptr [rbp + 48] ; RSP
|
||||
push qword ptr [rbp] ; RBP
|
||||
push rsi
|
||||
push rdi
|
||||
|
||||
;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero
|
||||
movzx rax, word ptr [rbp + 56]
|
||||
push rax ; for ss
|
||||
movzx rax, word ptr [rbp + 32]
|
||||
push rax ; for cs
|
||||
mov rax, ds
|
||||
push rax
|
||||
mov rax, es
|
||||
push rax
|
||||
mov rax, fs
|
||||
push rax
|
||||
mov rax, gs
|
||||
push rax
|
||||
|
||||
;; UINT64 Rip;
|
||||
push qword ptr [rbp + 24]
|
||||
|
||||
;; UINT64 Gdtr[2], Idtr[2];
|
||||
sub rsp, 16
|
||||
sidt fword ptr [rsp]
|
||||
sub rsp, 16
|
||||
sgdt fword ptr [rsp]
|
||||
|
||||
;; UINT64 Ldtr, Tr;
|
||||
xor rax, rax
|
||||
str ax
|
||||
push rax
|
||||
sldt ax
|
||||
push rax
|
||||
|
||||
;; UINT64 RFlags;
|
||||
push qword ptr [rbp + 40]
|
||||
|
||||
;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
|
||||
mov rax, cr8
|
||||
push rax
|
||||
mov rax, cr4
|
||||
or rax, 208h
|
||||
mov cr4, rax
|
||||
push rax
|
||||
mov rax, cr3
|
||||
push rax
|
||||
mov rax, cr2
|
||||
push rax
|
||||
xor rax, rax
|
||||
push rax
|
||||
mov rax, cr0
|
||||
push rax
|
||||
|
||||
;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
|
||||
mov rax, dr7
|
||||
push rax
|
||||
mov rax, dr6
|
||||
push rax
|
||||
mov rax, dr3
|
||||
push rax
|
||||
mov rax, dr2
|
||||
push rax
|
||||
mov rax, dr1
|
||||
push rax
|
||||
mov rax, dr0
|
||||
push rax
|
||||
|
||||
;; FX_SAVE_STATE_X64 FxSaveState;
|
||||
|
||||
sub rsp, 512
|
||||
mov rdi, rsp
|
||||
db 0fh, 0aeh, 00000111y ;fxsave [rdi]
|
||||
|
||||
; UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear
|
||||
cld
|
||||
|
||||
;; UINT32 ExceptionData;
|
||||
push qword ptr [rbp + 16]
|
||||
|
||||
;; call into exception handler
|
||||
mov rcx, [rbp + 8]
|
||||
mov rax, SmiPFHandler
|
||||
|
||||
;; Prepare parameter and call
|
||||
mov rdx, rsp
|
||||
;
|
||||
; Per X64 calling convention, allocate maximum parameter stack space
|
||||
; and make sure RSP is 16-byte aligned
|
||||
;
|
||||
sub rsp, 4 * 8 + 8
|
||||
call rax
|
||||
add rsp, 4 * 8 + 8
|
||||
jmp @F
|
||||
|
||||
@@:
|
||||
;; UINT64 ExceptionData;
|
||||
add rsp, 8
|
||||
|
||||
;; FX_SAVE_STATE_X64 FxSaveState;
|
||||
|
||||
mov rsi, rsp
|
||||
db 0fh, 0aeh, 00001110y ; fxrstor [rsi]
|
||||
add rsp, 512
|
||||
|
||||
;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
|
||||
;; Skip restoration of DRx registers to support debuggers
|
||||
;; that set breakpoints in interrupt/exception context
|
||||
add rsp, 8 * 6
|
||||
|
||||
;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
|
||||
pop rax
|
||||
mov cr0, rax
|
||||
add rsp, 8 ; not for Cr1
|
||||
pop rax
|
||||
mov cr2, rax
|
||||
pop rax
|
||||
mov cr3, rax
|
||||
pop rax
|
||||
mov cr4, rax
|
||||
pop rax
|
||||
mov cr8, rax
|
||||
|
||||
;; UINT64 RFlags;
|
||||
pop qword ptr [rbp + 40]
|
||||
|
||||
;; UINT64 Ldtr, Tr;
|
||||
;; UINT64 Gdtr[2], Idtr[2];
|
||||
;; Best not let anyone mess with these particular registers...
|
||||
add rsp, 48
|
||||
|
||||
;; UINT64 Rip;
|
||||
pop qword ptr [rbp + 24]
|
||||
|
||||
;; UINT64 Gs, Fs, Es, Ds, Cs, Ss;
|
||||
pop rax
|
||||
; mov gs, rax ; not for gs
|
||||
pop rax
|
||||
; mov fs, rax ; not for fs
|
||||
; (X64 will not use fs and gs, so we do not restore it)
|
||||
pop rax
|
||||
mov es, rax
|
||||
pop rax
|
||||
mov ds, rax
|
||||
pop qword ptr [rbp + 32] ; for cs
|
||||
pop qword ptr [rbp + 56] ; for ss
|
||||
|
||||
;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
|
||||
;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
pop rdi
|
||||
pop rsi
|
||||
add rsp, 8 ; not for rbp
|
||||
pop qword ptr [rbp + 48] ; for rsp
|
||||
pop rbx
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
pop r12
|
||||
pop r13
|
||||
pop r14
|
||||
pop r15
|
||||
|
||||
mov rsp, rbp
|
||||
|
||||
; Enable TF bit after page fault handler runs
|
||||
bts dword ptr [rsp + 40], 8 ;RFLAGS
|
||||
|
||||
pop rbp
|
||||
add rsp, 16 ; skip INT# & ErrCode
|
||||
iretq
|
||||
PageFaultIdtHandlerSmmProfile ENDP
|
||||
|
||||
InitializeIDTSmmStackGuard PROC
|
||||
;
|
||||
; If SMM Stack Guard feature is enabled, set the IST field of
|
||||
; the interrupt gate for Page Fault Exception to be 1
|
||||
;
|
||||
lea rax, _SmiIDT + 14 * 16
|
||||
mov byte ptr [rax + 4], 1
|
||||
ret
|
||||
InitializeIDTSmmStackGuard ENDP
|
||||
|
||||
END
|
|
@ -0,0 +1,141 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (c) 2009 - 2015, 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:
|
||||
#
|
||||
# SmmInit.S
|
||||
#
|
||||
# Abstract:
|
||||
#
|
||||
# Functions for relocating SMBASE's for all processors
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
ASM_GLOBAL ASM_PFX(gSmmCr0)
|
||||
ASM_GLOBAL ASM_PFX(gSmmCr3)
|
||||
ASM_GLOBAL ASM_PFX(gSmmCr4)
|
||||
ASM_GLOBAL ASM_PFX(gSmmJmpAddr)
|
||||
ASM_GLOBAL ASM_PFX(gcSmmInitTemplate)
|
||||
ASM_GLOBAL ASM_PFX(gcSmmInitSize)
|
||||
ASM_GLOBAL ASM_PFX(mRebasedFlagAddr32)
|
||||
ASM_GLOBAL ASM_PFX(SmmRelocationSemaphoreComplete)
|
||||
ASM_GLOBAL ASM_PFX(SmmRelocationSemaphoreComplete32)
|
||||
ASM_GLOBAL ASM_PFX(mSmmRelocationOriginalAddressPtr32)
|
||||
ASM_GLOBAL ASM_PFX(gSmmInitStack)
|
||||
ASM_GLOBAL ASM_PFX(gcSmiInitGdtr)
|
||||
|
||||
|
||||
.text
|
||||
|
||||
ASM_PFX(gcSmiInitGdtr):
|
||||
.word 0
|
||||
.quad 0
|
||||
|
||||
SmmStartup:
|
||||
.byte 0x66,0xb8 # mov eax, imm32
|
||||
ASM_PFX(gSmmCr3): .space 4
|
||||
movq %rax, %cr3
|
||||
.byte 0x66,0x2e
|
||||
lgdt (ASM_PFX(gcSmiInitGdtr) - SmmStartup)(%ebp)
|
||||
.byte 0x66,0xb8 # mov eax, imm32
|
||||
ASM_PFX(gSmmCr4): .space 4
|
||||
orb $2, %ah # enable XMM registers access
|
||||
movq %rax, %cr4
|
||||
.byte 0x66
|
||||
movl $0xc0000080,%ecx # IA32_EFER MSR
|
||||
rdmsr
|
||||
orb $1,%ah # set LME bit
|
||||
wrmsr
|
||||
.byte 0x66,0xb8 # mov eax, imm32
|
||||
ASM_PFX(gSmmCr0): .space 4
|
||||
movq %rax, %cr0
|
||||
.byte 0x66,0xea # far jmp to long mode
|
||||
ASM_PFX(gSmmJmpAddr): .quad LongMode
|
||||
LongMode: # long-mode starts here
|
||||
.byte 0x48,0xbc # mov rsp, imm64
|
||||
ASM_PFX(gSmmInitStack): .space 8
|
||||
andw $0xfff0, %sp # make sure RSP is 16-byte aligned
|
||||
#
|
||||
# Accoring to X64 calling convention, XMM0~5 are volatile, we need to save
|
||||
# them before calling C-function.
|
||||
#
|
||||
subq $0x60, %rsp
|
||||
movdqa %xmm0, 0x0(%rsp)
|
||||
movdqa %xmm1, 0x10(%rsp)
|
||||
movdqa %xmm2, 0x20(%rsp)
|
||||
movdqa %xmm3, 0x30(%rsp)
|
||||
movdqa %xmm4, 0x40(%rsp)
|
||||
movdqa %xmm5, 0x50(%rsp)
|
||||
|
||||
|
||||
addq $-0x20, %rsp
|
||||
call ASM_PFX(SmmInitHandler)
|
||||
addq $0x20, %rsp
|
||||
#
|
||||
# Restore XMM0~5 after calling C-function.
|
||||
#
|
||||
movdqa 0x0(%rsp), %xmm0
|
||||
movdqa 0x10(%rsp), %xmm1
|
||||
movdqa 0x20(%rsp), %xmm2
|
||||
movdqa 0x30(%rsp), %xmm3
|
||||
movdqa 0x40(%rsp), %xmm4
|
||||
movdqa 0x50(%rsp), %xmm5
|
||||
|
||||
rsm
|
||||
|
||||
ASM_PFX(gcSmmInitTemplate):
|
||||
|
||||
_SmmInitTemplate:
|
||||
.byte 0x66,0x2e,0x8b,0x2e # mov ebp, cs:[@F]
|
||||
.word L1 - _SmmInitTemplate + 0x8000
|
||||
.byte 0x66, 0x81, 0xed, 0, 0, 3, 0 # sub ebp, 0x30000
|
||||
jmp *%bp # jmp ebp actually
|
||||
L1:
|
||||
.quad SmmStartup
|
||||
|
||||
ASM_PFX(gcSmmInitSize): .word . - ASM_PFX(gcSmmInitTemplate)
|
||||
|
||||
ASM_PFX(SmmRelocationSemaphoreComplete):
|
||||
# Create a simple stack frame to store RAX and the original RSM location
|
||||
pushq %rax # Used to store return address
|
||||
pushq %rax
|
||||
|
||||
# Load the original RSM location onto stack
|
||||
movabsq $ASM_PFX(mSmmRelocationOriginalAddress), %rax
|
||||
movq (%rax), %rax
|
||||
movq %rax, 0x08(%rsp)
|
||||
|
||||
# Update rebase flag
|
||||
movabsq $ASM_PFX(mRebasedFlag), %rax
|
||||
movq (%rax), %rax
|
||||
movb $1, (%rax)
|
||||
|
||||
#restore RAX and return to original RSM location
|
||||
popq %rax
|
||||
retq
|
||||
|
||||
#
|
||||
# Semaphore code running in 32-bit mode
|
||||
#
|
||||
ASM_PFX(SmmRelocationSemaphoreComplete32):
|
||||
#
|
||||
# movb $1, ()
|
||||
#
|
||||
.byte 0xc6, 0x05
|
||||
ASM_PFX(mRebasedFlagAddr32):
|
||||
.long 0
|
||||
.byte 1
|
||||
#
|
||||
# jmpd ()
|
||||
#
|
||||
.byte 0xff, 0x25
|
||||
ASM_PFX(mSmmRelocationOriginalAddressPtr32):
|
||||
.long 0
|
|
@ -0,0 +1,132 @@
|
|||
;------------------------------------------------------------------------------ ;
|
||||
; Copyright (c) 2009 - 2015, 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:
|
||||
;
|
||||
; SmmInit.Asm
|
||||
;
|
||||
; Abstract:
|
||||
;
|
||||
; Functions for relocating SMBASE's for all processors
|
||||
;
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
EXTERNDEF SmmInitHandler:PROC
|
||||
EXTERNDEF gSmmCr0:DWORD
|
||||
EXTERNDEF gSmmCr3:DWORD
|
||||
EXTERNDEF gSmmCr4:DWORD
|
||||
EXTERNDEF gSmmJmpAddr:QWORD
|
||||
EXTERNDEF gcSmmInitTemplate:BYTE
|
||||
EXTERNDEF gcSmmInitSize:WORD
|
||||
EXTERNDEF mRebasedFlag:PTR BYTE
|
||||
EXTERNDEF mSmmRelocationOriginalAddress:QWORD
|
||||
EXTERNDEF mRebasedFlagAddr32:DWORD
|
||||
EXTERNDEF mSmmRelocationOriginalAddressPtr32:DWORD
|
||||
EXTERNDEF gSmmInitStack:QWORD
|
||||
EXTERNDEF gcSmiInitGdtr:FWORD
|
||||
|
||||
.code
|
||||
|
||||
gcSmiInitGdtr LABEL FWORD
|
||||
DW 0
|
||||
DQ 0
|
||||
|
||||
SmmStartup PROC
|
||||
DB 66h, 0b8h ; mov eax, imm32
|
||||
gSmmCr3 DD ?
|
||||
mov cr3, rax
|
||||
DB 66h, 2eh
|
||||
lgdt fword ptr [ebp + (offset gcSmiInitGdtr - SmmStartup)]
|
||||
DB 66h, 0b8h ; mov eax, imm32
|
||||
gSmmCr4 DD ?
|
||||
or ah, 2 ; enable XMM registers access
|
||||
mov cr4, rax
|
||||
DB 66h
|
||||
mov ecx, 0c0000080h ; IA32_EFER MSR
|
||||
rdmsr
|
||||
or ah, 1 ; set LME bit
|
||||
wrmsr
|
||||
DB 66h, 0b8h ; mov eax, imm32
|
||||
gSmmCr0 DD ?
|
||||
mov cr0, rax ; enable protected mode & paging
|
||||
DB 66h, 0eah ; far jmp to long mode
|
||||
gSmmJmpAddr DQ @LongMode
|
||||
@LongMode: ; long-mode starts here
|
||||
DB 48h, 0bch ; mov rsp, imm64
|
||||
gSmmInitStack DQ ?
|
||||
and sp, 0fff0h ; make sure RSP is 16-byte aligned
|
||||
;
|
||||
; Accoring to X64 calling convention, XMM0~5 are volatile, we need to save
|
||||
; them before calling C-function.
|
||||
;
|
||||
sub rsp, 60h
|
||||
movdqa [rsp], xmm0
|
||||
movdqa [rsp + 10h], xmm1
|
||||
movdqa [rsp + 20h], xmm2
|
||||
movdqa [rsp + 30h], xmm3
|
||||
movdqa [rsp + 40h], xmm4
|
||||
movdqa [rsp + 50h], xmm5
|
||||
|
||||
add rsp, -20h
|
||||
call SmmInitHandler
|
||||
add rsp, 20h
|
||||
|
||||
;
|
||||
; Restore XMM0~5 after calling C-function.
|
||||
;
|
||||
movdqa xmm0, [rsp]
|
||||
movdqa xmm1, [rsp + 10h]
|
||||
movdqa xmm2, [rsp + 20h]
|
||||
movdqa xmm3, [rsp + 30h]
|
||||
movdqa xmm4, [rsp + 40h]
|
||||
movdqa xmm5, [rsp + 50h]
|
||||
|
||||
rsm
|
||||
SmmStartup ENDP
|
||||
|
||||
gcSmmInitTemplate LABEL BYTE
|
||||
|
||||
_SmmInitTemplate PROC
|
||||
DB 66h, 2eh, 8bh, 2eh ; mov ebp, cs:[@F]
|
||||
DW @L1 - _SmmInitTemplate + 8000h
|
||||
DB 66h, 81h, 0edh, 00h, 00h, 03h, 00 ; sub ebp, 30000h
|
||||
jmp bp ; jmp ebp actually
|
||||
@L1:
|
||||
DQ SmmStartup
|
||||
_SmmInitTemplate ENDP
|
||||
|
||||
gcSmmInitSize DW $ - gcSmmInitTemplate
|
||||
|
||||
SmmRelocationSemaphoreComplete PROC
|
||||
push rax
|
||||
mov rax, mRebasedFlag
|
||||
mov byte ptr [rax], 1
|
||||
pop rax
|
||||
jmp [mSmmRelocationOriginalAddress]
|
||||
SmmRelocationSemaphoreComplete ENDP
|
||||
|
||||
;
|
||||
; Semaphore code running in 32-bit mode
|
||||
;
|
||||
SmmRelocationSemaphoreComplete32 PROC
|
||||
;
|
||||
; mov byte ptr [], 1
|
||||
;
|
||||
db 0c6h, 05h
|
||||
mRebasedFlagAddr32 dd 0
|
||||
db 1
|
||||
;
|
||||
; jmp dword ptr []
|
||||
;
|
||||
db 0ffh, 25h
|
||||
mSmmRelocationOriginalAddressPtr32 dd 0
|
||||
SmmRelocationSemaphoreComplete32 ENDP
|
||||
|
||||
END
|
|
@ -0,0 +1,316 @@
|
|||
/** @file
|
||||
X64 processor specific functions to enable SMM profile.
|
||||
|
||||
Copyright (c) 2012 - 2015, 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.
|
||||
|
||||
**/
|
||||
|
||||
#include "PiSmmCpuDxeSmm.h"
|
||||
#include "SmmProfileInternal.h"
|
||||
|
||||
//
|
||||
// Current page index.
|
||||
//
|
||||
UINTN mPFPageIndex;
|
||||
|
||||
//
|
||||
// Pool for dynamically creating page table in page fault handler.
|
||||
//
|
||||
UINT64 mPFPageBuffer;
|
||||
|
||||
//
|
||||
// Store the uplink information for each page being used.
|
||||
//
|
||||
UINT64 *mPFPageUplink[MAX_PF_PAGE_COUNT];
|
||||
|
||||
/**
|
||||
Create SMM page table for S3 path.
|
||||
|
||||
**/
|
||||
VOID
|
||||
InitSmmS3Cr3 (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
EFI_PHYSICAL_ADDRESS Pages;
|
||||
UINT64 *PTEntry;
|
||||
|
||||
//
|
||||
// Generate PAE page table for the first 4GB memory space
|
||||
//
|
||||
Pages = Gen4GPageTable (1);
|
||||
|
||||
//
|
||||
// Fill Page-Table-Level4 (PML4) entry
|
||||
//
|
||||
PTEntry = (UINT64*)(UINTN)(Pages - EFI_PAGES_TO_SIZE (1));
|
||||
*PTEntry = Pages + IA32_PG_P;
|
||||
ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry));
|
||||
|
||||
//
|
||||
// Return the address of PML4 (to set CR3)
|
||||
//
|
||||
mSmmS3ResumeState->SmmS3Cr3 = (UINT32)(UINTN)PTEntry;
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
/**
|
||||
Allocate pages for creating 4KB-page based on 2MB-page when page fault happens.
|
||||
|
||||
**/
|
||||
VOID
|
||||
InitPagesForPFHandler (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
VOID *Address;
|
||||
|
||||
//
|
||||
// Pre-Allocate memory for page fault handler
|
||||
//
|
||||
Address = NULL;
|
||||
Address = AllocatePages (MAX_PF_PAGE_COUNT);
|
||||
ASSERT_EFI_ERROR (Address != NULL);
|
||||
|
||||
mPFPageBuffer = (UINT64)(UINTN) Address;
|
||||
mPFPageIndex = 0;
|
||||
ZeroMem ((VOID *) (UINTN) mPFPageBuffer, EFI_PAGE_SIZE * MAX_PF_PAGE_COUNT);
|
||||
ZeroMem (mPFPageUplink, sizeof (mPFPageUplink));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
Allocate one page for creating 4KB-page based on 2MB-page.
|
||||
|
||||
@param Uplink The address of Page-Directory entry.
|
||||
|
||||
**/
|
||||
VOID
|
||||
AcquirePage (
|
||||
UINT64 *Uplink
|
||||
)
|
||||
{
|
||||
UINT64 Address;
|
||||
|
||||
//
|
||||
// Get the buffer
|
||||
//
|
||||
Address = mPFPageBuffer + EFI_PAGES_TO_SIZE (mPFPageIndex);
|
||||
ZeroMem ((VOID *) (UINTN) Address, EFI_PAGE_SIZE);
|
||||
|
||||
//
|
||||
// Cut the previous uplink if it exists and wasn't overwritten
|
||||
//
|
||||
if ((mPFPageUplink[mPFPageIndex] != NULL) && ((*mPFPageUplink[mPFPageIndex] & PHYSICAL_ADDRESS_MASK) == Address)) {
|
||||
*mPFPageUplink[mPFPageIndex] = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Link & Record the current uplink
|
||||
//
|
||||
*Uplink = Address | IA32_PG_P | IA32_PG_RW;
|
||||
mPFPageUplink[mPFPageIndex] = Uplink;
|
||||
|
||||
mPFPageIndex = (mPFPageIndex + 1) % MAX_PF_PAGE_COUNT;
|
||||
}
|
||||
|
||||
/**
|
||||
Update page table to map the memory correctly in order to make the instruction
|
||||
which caused page fault execute successfully. And it also save the original page
|
||||
table to be restored in single-step exception.
|
||||
|
||||
@param PageTable PageTable Address.
|
||||
@param PFAddress The memory address which caused page fault exception.
|
||||
@param CpuIndex The index of the processor.
|
||||
@param ErrorCode The Error code of exception.
|
||||
@param IsValidPFAddress The flag indicates if SMM profile data need be added.
|
||||
|
||||
**/
|
||||
VOID
|
||||
RestorePageTableAbove4G (
|
||||
UINT64 *PageTable,
|
||||
UINT64 PFAddress,
|
||||
UINTN CpuIndex,
|
||||
UINTN ErrorCode,
|
||||
BOOLEAN *IsValidPFAddress
|
||||
)
|
||||
{
|
||||
UINTN PTIndex;
|
||||
UINT64 Address;
|
||||
BOOLEAN Nx;
|
||||
BOOLEAN Existed;
|
||||
UINTN Index;
|
||||
UINTN PFIndex;
|
||||
|
||||
ASSERT ((PageTable != NULL) && (IsValidPFAddress != NULL));
|
||||
|
||||
//
|
||||
// If page fault address is 4GB above.
|
||||
//
|
||||
|
||||
//
|
||||
// Check if page fault address has existed in page table.
|
||||
// If it exists in page table but page fault is generated,
|
||||
// there are 2 possible reasons: 1. present flag is set to 0; 2. instruction fetch in protected memory range.
|
||||
//
|
||||
Existed = FALSE;
|
||||
PageTable = (UINT64*)(AsmReadCr3 () & PHYSICAL_ADDRESS_MASK);
|
||||
PTIndex = BitFieldRead64 (PFAddress, 39, 47);
|
||||
if ((PageTable[PTIndex] & IA32_PG_P) != 0) {
|
||||
// PML4E
|
||||
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
PTIndex = BitFieldRead64 (PFAddress, 30, 38);
|
||||
if ((PageTable[PTIndex] & IA32_PG_P) != 0) {
|
||||
// PDPTE
|
||||
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
PTIndex = BitFieldRead64 (PFAddress, 21, 29);
|
||||
// PD
|
||||
if ((PageTable[PTIndex] & IA32_PG_PS) != 0) {
|
||||
//
|
||||
// 2MB page
|
||||
//
|
||||
Address = (UINT64)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
if ((Address & PHYSICAL_ADDRESS_MASK & ~((1ull << 21) - 1)) == ((PFAddress & PHYSICAL_ADDRESS_MASK & ~((1ull << 21) - 1)))) {
|
||||
Existed = TRUE;
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// 4KB page
|
||||
//
|
||||
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
if (PageTable != 0) {
|
||||
//
|
||||
// When there is a valid entry to map to 4KB page, need not create a new entry to map 2MB.
|
||||
//
|
||||
PTIndex = BitFieldRead64 (PFAddress, 12, 20);
|
||||
Address = (UINT64)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
if ((Address & PHYSICAL_ADDRESS_MASK & ~((1ull << 12) - 1)) == (PFAddress & PHYSICAL_ADDRESS_MASK & ~((1ull << 12) - 1))) {
|
||||
Existed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If page entry does not existed in page table at all, create a new entry.
|
||||
//
|
||||
if (!Existed) {
|
||||
|
||||
if (IsAddressValid (PFAddress, &Nx)) {
|
||||
//
|
||||
// If page fault address above 4GB is in protected range but it causes a page fault exception,
|
||||
// Will create a page entry for this page fault address, make page table entry as present/rw and execution-disable.
|
||||
// this access is not saved into SMM profile data.
|
||||
//
|
||||
*IsValidPFAddress = TRUE;
|
||||
}
|
||||
|
||||
//
|
||||
// Create one entry in page table for page fault address.
|
||||
//
|
||||
SmiDefaultPFHandler ();
|
||||
//
|
||||
// Find the page table entry created just now.
|
||||
//
|
||||
PageTable = (UINT64*)(AsmReadCr3 () & PHYSICAL_ADDRESS_MASK);
|
||||
PFAddress = AsmReadCr2 ();
|
||||
// PML4E
|
||||
PTIndex = BitFieldRead64 (PFAddress, 39, 47);
|
||||
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
// PDPTE
|
||||
PTIndex = BitFieldRead64 (PFAddress, 30, 38);
|
||||
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
// PD
|
||||
PTIndex = BitFieldRead64 (PFAddress, 21, 29);
|
||||
Address = PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK;
|
||||
//
|
||||
// Check if 2MB-page entry need be changed to 4KB-page entry.
|
||||
//
|
||||
if (IsAddressSplit (Address)) {
|
||||
AcquirePage (&PageTable[PTIndex]);
|
||||
|
||||
// PTE
|
||||
PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
|
||||
for (Index = 0; Index < 512; Index++) {
|
||||
PageTable[Index] = Address | IA32_PG_RW | IA32_PG_P;
|
||||
if (!IsAddressValid (Address, &Nx)) {
|
||||
PageTable[Index] = PageTable[Index] & (INTN)(INT32)(~(IA32_PG_RW | IA32_PG_P));
|
||||
}
|
||||
if (Nx && mXdSupported) {
|
||||
PageTable[Index] = PageTable[Index] | IA32_PG_NX;
|
||||
}
|
||||
if (Address == (PFAddress & PHYSICAL_ADDRESS_MASK & ~((1ull << 12) - 1))) {
|
||||
PTIndex = Index;
|
||||
}
|
||||
Address += SIZE_4KB;
|
||||
} // end for PT
|
||||
} else {
|
||||
//
|
||||
// Update 2MB page entry.
|
||||
//
|
||||
if (!IsAddressValid (Address, &Nx)) {
|
||||
//
|
||||
// Patch to remove present flag and rw flag.
|
||||
//
|
||||
PageTable[PTIndex] = PageTable[PTIndex] & (INTN)(INT32)(~(IA32_PG_RW | IA32_PG_P));
|
||||
}
|
||||
//
|
||||
// Set XD bit to 1
|
||||
//
|
||||
if (Nx && mXdSupported) {
|
||||
PageTable[PTIndex] = PageTable[PTIndex] | IA32_PG_NX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Record old entries with non-present status
|
||||
// Old entries include the memory which instruction is at and the memory which instruction access.
|
||||
//
|
||||
//
|
||||
ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT);
|
||||
if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) {
|
||||
PFIndex = mPFEntryCount[CpuIndex];
|
||||
mLastPFEntryValue[CpuIndex][PFIndex] = PageTable[PTIndex];
|
||||
mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex];
|
||||
mPFEntryCount[CpuIndex]++;
|
||||
}
|
||||
|
||||
//
|
||||
// Add present flag or clear XD flag to make page fault handler succeed.
|
||||
//
|
||||
PageTable[PTIndex] |= (UINT64)(IA32_PG_RW | IA32_PG_P);
|
||||
if ((ErrorCode & IA32_PF_EC_ID) != 0) {
|
||||
//
|
||||
// If page fault is caused by instruction fetch, clear XD bit in the entry.
|
||||
//
|
||||
PageTable[PTIndex] &= ~IA32_PG_NX;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
Clear TF in FLAGS.
|
||||
|
||||
@param SystemContext A pointer to the processor context when
|
||||
the interrupt occurred on the processor.
|
||||
|
||||
**/
|
||||
VOID
|
||||
ClearTrapFlag (
|
||||
IN OUT EFI_SYSTEM_CONTEXT SystemContext
|
||||
)
|
||||
{
|
||||
SystemContext.SystemContextX64->Rflags &= (UINTN) ~BIT8;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/** @file
|
||||
X64 processor specific header file to enable SMM profile.
|
||||
|
||||
Copyright (c) 2012 - 2015, 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.
|
||||
|
||||
**/
|
||||
|
||||
#ifndef _SMM_PROFILE_ARCH_H_
|
||||
#define _SMM_PROFILE_ARCH_H_
|
||||
|
||||
#pragma pack (1)
|
||||
|
||||
typedef struct _MSR_DS_AREA_STRUCT {
|
||||
UINT64 BTSBufferBase;
|
||||
UINT64 BTSIndex;
|
||||
UINT64 BTSAbsoluteMaximum;
|
||||
UINT64 BTSInterruptThreshold;
|
||||
UINT64 PEBSBufferBase;
|
||||
UINT64 PEBSIndex;
|
||||
UINT64 PEBSAbsoluteMaximum;
|
||||
UINT64 PEBSInterruptThreshold;
|
||||
UINT64 PEBSCounterReset[2];
|
||||
UINT64 Reserved;
|
||||
} MSR_DS_AREA_STRUCT;
|
||||
|
||||
typedef struct _BRANCH_TRACE_RECORD {
|
||||
UINT64 LastBranchFrom;
|
||||
UINT64 LastBranchTo;
|
||||
UINT64 Rsvd0 : 4;
|
||||
UINT64 BranchPredicted : 1;
|
||||
UINT64 Rsvd1 : 59;
|
||||
} BRANCH_TRACE_RECORD;
|
||||
|
||||
typedef struct _PEBS_RECORD {
|
||||
UINT64 Rflags;
|
||||
UINT64 LinearIP;
|
||||
UINT64 Rax;
|
||||
UINT64 Rbx;
|
||||
UINT64 Rcx;
|
||||
UINT64 Rdx;
|
||||
UINT64 Rsi;
|
||||
UINT64 Rdi;
|
||||
UINT64 Rbp;
|
||||
UINT64 Rsp;
|
||||
UINT64 R8;
|
||||
UINT64 R9;
|
||||
UINT64 R10;
|
||||
UINT64 R11;
|
||||
UINT64 R12;
|
||||
UINT64 R13;
|
||||
UINT64 R14;
|
||||
UINT64 R15;
|
||||
} PEBS_RECORD;
|
||||
|
||||
#pragma pack ()
|
||||
|
||||
#define PHYSICAL_ADDRESS_MASK ((1ull << 52) - SIZE_4KB)
|
||||
|
||||
/**
|
||||
Update page table to map the memory correctly in order to make the instruction
|
||||
which caused page fault execute successfully. And it also save the original page
|
||||
table to be restored in single-step exception.
|
||||
|
||||
@param PageTable PageTable Address.
|
||||
@param PFAddress The memory address which caused page fault exception.
|
||||
@param CpuIndex The index of the processor.
|
||||
@param ErrorCode The Error code of exception.
|
||||
@param IsValidPFAddress The flag indicates if SMM profile data need be added.
|
||||
|
||||
**/
|
||||
VOID
|
||||
RestorePageTableAbove4G (
|
||||
UINT64 *PageTable,
|
||||
UINT64 PFAddress,
|
||||
UINTN CpuIndex,
|
||||
UINTN ErrorCode,
|
||||
BOOLEAN *IsValidPFAddress
|
||||
);
|
||||
|
||||
/**
|
||||
Create SMM page table for S3 path.
|
||||
|
||||
**/
|
||||
VOID
|
||||
InitSmmS3Cr3 (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Allocate pages for creating 4KB-page based on 2MB-page when page fault happens.
|
||||
|
||||
**/
|
||||
VOID
|
||||
InitPagesForPFHandler (
|
||||
VOID
|
||||
);
|
||||
|
||||
#endif // _SMM_PROFILE_ARCH_H_
|
Loading…
Reference in New Issue