Implement the First SMI Handler for hot-added CPUs, in NASM. Add the interfacing C-language function that the SMM Monarch calls. This function launches and coordinates SMBASE relocation for a hot-added CPU. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Igor Mammedov <imammedo@redhat.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200226221156.29589-13-lersek@redhat.com> Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Tested-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
		
			
				
	
	
		
			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)
 |