mirror of https://github.com/acidanthera/audk.git
155 lines
4.0 KiB
NASM
155 lines
4.0 KiB
NASM
|
;------------------------------------------------------------------------------
|
||
|
; @file
|
||
|
; Relocate the SMBASE on a hot-added CPU when it services its first SMI.
|
||
|
;
|
||
|
; Copyright (c) 2020, Red Hat, Inc.
|
||
|
;
|
||
|
; SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
;
|
||
|
; The routine runs on the hot-added CPU in the following "big real mode",
|
||
|
; 16-bit environment; per "SMI HANDLER EXECUTION ENVIRONMENT" in the Intel SDM
|
||
|
; (table "Processor Register Initialization in SMM"):
|
||
|
;
|
||
|
; - CS selector: 0x3000 (most significant 16 bits of SMM_DEFAULT_SMBASE).
|
||
|
;
|
||
|
; - CS limit: 0xFFFF_FFFF.
|
||
|
;
|
||
|
; - CS base: SMM_DEFAULT_SMBASE (0x3_0000).
|
||
|
;
|
||
|
; - IP: SMM_HANDLER_OFFSET (0x8000).
|
||
|
;
|
||
|
; - ES, SS, DS, FS, GS selectors: 0.
|
||
|
;
|
||
|
; - ES, SS, DS, FS, GS limits: 0xFFFF_FFFF.
|
||
|
;
|
||
|
; - ES, SS, DS, FS, GS bases: 0.
|
||
|
;
|
||
|
; - Operand-size and address-size override prefixes can be used to access the
|
||
|
; address space beyond 1MB.
|
||
|
;------------------------------------------------------------------------------
|
||
|
|
||
|
SECTION .data
|
||
|
BITS 16
|
||
|
|
||
|
;
|
||
|
; Bring in SMM_DEFAULT_SMBASE from
|
||
|
; "MdePkg/Include/Register/Intel/SmramSaveStateMap.h".
|
||
|
;
|
||
|
SMM_DEFAULT_SMBASE: equ 0x3_0000
|
||
|
|
||
|
;
|
||
|
; Field offsets in FIRST_SMI_HANDLER_CONTEXT, which resides at
|
||
|
; SMM_DEFAULT_SMBASE.
|
||
|
;
|
||
|
ApicIdGate: equ 0 ; UINT64
|
||
|
NewSmbase: equ 8 ; UINT32
|
||
|
AboutToLeaveSmm: equ 12 ; UINT8
|
||
|
|
||
|
;
|
||
|
; SMRAM Save State Map field offsets, per the AMD (not Intel) layout that QEMU
|
||
|
; implements. Relative to SMM_DEFAULT_SMBASE.
|
||
|
;
|
||
|
SaveStateRevId: equ 0xFEFC ; UINT32
|
||
|
SaveStateSmbase: equ 0xFEF8 ; UINT32
|
||
|
SaveStateSmbase64: equ 0xFF00 ; UINT32
|
||
|
|
||
|
;
|
||
|
; CPUID constants, from "MdePkg/Include/Register/Intel/Cpuid.h".
|
||
|
;
|
||
|
CPUID_SIGNATURE: equ 0x00
|
||
|
CPUID_EXTENDED_TOPOLOGY: equ 0x0B
|
||
|
CPUID_VERSION_INFO: equ 0x01
|
||
|
|
||
|
GLOBAL ASM_PFX (mFirstSmiHandler) ; UINT8[]
|
||
|
GLOBAL ASM_PFX (mFirstSmiHandlerSize) ; UINT16
|
||
|
|
||
|
ASM_PFX (mFirstSmiHandler):
|
||
|
;
|
||
|
; Get our own APIC ID first, so we can contend for ApicIdGate.
|
||
|
;
|
||
|
; This basically reimplements GetInitialApicId() from
|
||
|
; "UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c".
|
||
|
;
|
||
|
mov eax, CPUID_SIGNATURE
|
||
|
cpuid
|
||
|
cmp eax, CPUID_EXTENDED_TOPOLOGY
|
||
|
jb GetApicIdFromVersionInfo
|
||
|
|
||
|
mov eax, CPUID_EXTENDED_TOPOLOGY
|
||
|
mov ecx, 0
|
||
|
cpuid
|
||
|
test ebx, 0xFFFF
|
||
|
jz GetApicIdFromVersionInfo
|
||
|
|
||
|
;
|
||
|
; EDX has the APIC ID, save it to ESI.
|
||
|
;
|
||
|
mov esi, edx
|
||
|
jmp KnockOnGate
|
||
|
|
||
|
GetApicIdFromVersionInfo:
|
||
|
mov eax, CPUID_VERSION_INFO
|
||
|
cpuid
|
||
|
shr ebx, 24
|
||
|
;
|
||
|
; EBX has the APIC ID, save it to ESI.
|
||
|
;
|
||
|
mov esi, ebx
|
||
|
|
||
|
KnockOnGate:
|
||
|
;
|
||
|
; See if ApicIdGate shows our own APIC ID. If so, swap it to MAX_UINT64
|
||
|
; (close the gate), and advance. Otherwise, keep knocking.
|
||
|
;
|
||
|
; InterlockedCompareExchange64():
|
||
|
; - Value := &FIRST_SMI_HANDLER_CONTEXT.ApicIdGate
|
||
|
; - CompareValue (EDX:EAX) := APIC ID (from ESI)
|
||
|
; - ExchangeValue (ECX:EBX) := MAX_UINT64
|
||
|
;
|
||
|
mov edx, 0
|
||
|
mov eax, esi
|
||
|
mov ecx, 0xFFFF_FFFF
|
||
|
mov ebx, 0xFFFF_FFFF
|
||
|
lock cmpxchg8b [ds : dword (SMM_DEFAULT_SMBASE + ApicIdGate)]
|
||
|
jz ApicIdMatch
|
||
|
pause
|
||
|
jmp KnockOnGate
|
||
|
|
||
|
ApicIdMatch:
|
||
|
;
|
||
|
; Update the SMBASE field in the SMRAM Save State Map.
|
||
|
;
|
||
|
; First, calculate the address of the SMBASE field, based on the SMM Revision
|
||
|
; ID; store the result in EBX.
|
||
|
;
|
||
|
mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + SaveStateRevId)]
|
||
|
test eax, 0xFFFF
|
||
|
jz LegacySaveStateMap
|
||
|
|
||
|
mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase64
|
||
|
jmp UpdateSmbase
|
||
|
|
||
|
LegacySaveStateMap:
|
||
|
mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase
|
||
|
|
||
|
UpdateSmbase:
|
||
|
;
|
||
|
; Load the new SMBASE value into EAX.
|
||
|
;
|
||
|
mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + NewSmbase)]
|
||
|
;
|
||
|
; Save it to the SMBASE field whose address we calculated in EBX.
|
||
|
;
|
||
|
mov dword [ds : dword ebx], eax
|
||
|
;
|
||
|
; Set AboutToLeaveSmm.
|
||
|
;
|
||
|
mov byte [ds : dword (SMM_DEFAULT_SMBASE + AboutToLeaveSmm)], 1
|
||
|
;
|
||
|
; We're done; leave SMM and continue to the pen.
|
||
|
;
|
||
|
rsm
|
||
|
|
||
|
ASM_PFX (mFirstSmiHandlerSize):
|
||
|
dw $ - ASM_PFX (mFirstSmiHandler)
|