FirstSmiHandler.nasm 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. ;------------------------------------------------------------------------------
  2. ; @file
  3. ; Relocate the SMBASE on a hot-added CPU when it services its first SMI.
  4. ;
  5. ; Copyright (c) 2020, Red Hat, Inc.
  6. ;
  7. ; SPDX-License-Identifier: BSD-2-Clause-Patent
  8. ;
  9. ; The routine runs on the hot-added CPU in the following "big real mode",
  10. ; 16-bit environment; per "SMI HANDLER EXECUTION ENVIRONMENT" in the Intel SDM
  11. ; (table "Processor Register Initialization in SMM"):
  12. ;
  13. ; - CS selector: 0x3000 (most significant 16 bits of SMM_DEFAULT_SMBASE).
  14. ;
  15. ; - CS limit: 0xFFFF_FFFF.
  16. ;
  17. ; - CS base: SMM_DEFAULT_SMBASE (0x3_0000).
  18. ;
  19. ; - IP: SMM_HANDLER_OFFSET (0x8000).
  20. ;
  21. ; - ES, SS, DS, FS, GS selectors: 0.
  22. ;
  23. ; - ES, SS, DS, FS, GS limits: 0xFFFF_FFFF.
  24. ;
  25. ; - ES, SS, DS, FS, GS bases: 0.
  26. ;
  27. ; - Operand-size and address-size override prefixes can be used to access the
  28. ; address space beyond 1MB.
  29. ;------------------------------------------------------------------------------
  30. SECTION .data
  31. BITS 16
  32. ;
  33. ; Bring in SMM_DEFAULT_SMBASE from
  34. ; "MdePkg/Include/Register/Intel/SmramSaveStateMap.h".
  35. ;
  36. SMM_DEFAULT_SMBASE: equ 0x3_0000
  37. ;
  38. ; Field offsets in FIRST_SMI_HANDLER_CONTEXT, which resides at
  39. ; SMM_DEFAULT_SMBASE.
  40. ;
  41. ApicIdGate: equ 0 ; UINT64
  42. NewSmbase: equ 8 ; UINT32
  43. AboutToLeaveSmm: equ 12 ; UINT8
  44. ;
  45. ; SMRAM Save State Map field offsets, per the AMD (not Intel) layout that QEMU
  46. ; implements. Relative to SMM_DEFAULT_SMBASE.
  47. ;
  48. SaveStateRevId: equ 0xFEFC ; UINT32
  49. SaveStateSmbase: equ 0xFEF8 ; UINT32
  50. SaveStateSmbase64: equ 0xFF00 ; UINT32
  51. ;
  52. ; CPUID constants, from "MdePkg/Include/Register/Intel/Cpuid.h".
  53. ;
  54. CPUID_SIGNATURE: equ 0x00
  55. CPUID_EXTENDED_TOPOLOGY: equ 0x0B
  56. CPUID_VERSION_INFO: equ 0x01
  57. GLOBAL ASM_PFX (mFirstSmiHandler) ; UINT8[]
  58. GLOBAL ASM_PFX (mFirstSmiHandlerSize) ; UINT16
  59. ASM_PFX (mFirstSmiHandler):
  60. ;
  61. ; Get our own APIC ID first, so we can contend for ApicIdGate.
  62. ;
  63. ; This basically reimplements GetInitialApicId() from
  64. ; "UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c".
  65. ;
  66. mov eax, CPUID_SIGNATURE
  67. cpuid
  68. cmp eax, CPUID_EXTENDED_TOPOLOGY
  69. jb GetApicIdFromVersionInfo
  70. mov eax, CPUID_EXTENDED_TOPOLOGY
  71. mov ecx, 0
  72. cpuid
  73. test ebx, 0xFFFF
  74. jz GetApicIdFromVersionInfo
  75. ;
  76. ; EDX has the APIC ID, save it to ESI.
  77. ;
  78. mov esi, edx
  79. jmp KnockOnGate
  80. GetApicIdFromVersionInfo:
  81. mov eax, CPUID_VERSION_INFO
  82. cpuid
  83. shr ebx, 24
  84. ;
  85. ; EBX has the APIC ID, save it to ESI.
  86. ;
  87. mov esi, ebx
  88. KnockOnGate:
  89. ;
  90. ; See if ApicIdGate shows our own APIC ID. If so, swap it to MAX_UINT64
  91. ; (close the gate), and advance. Otherwise, keep knocking.
  92. ;
  93. ; InterlockedCompareExchange64():
  94. ; - Value := &FIRST_SMI_HANDLER_CONTEXT.ApicIdGate
  95. ; - CompareValue (EDX:EAX) := APIC ID (from ESI)
  96. ; - ExchangeValue (ECX:EBX) := MAX_UINT64
  97. ;
  98. mov edx, 0
  99. mov eax, esi
  100. mov ecx, 0xFFFF_FFFF
  101. mov ebx, 0xFFFF_FFFF
  102. lock cmpxchg8b [ds : dword (SMM_DEFAULT_SMBASE + ApicIdGate)]
  103. jz ApicIdMatch
  104. pause
  105. jmp KnockOnGate
  106. ApicIdMatch:
  107. ;
  108. ; Update the SMBASE field in the SMRAM Save State Map.
  109. ;
  110. ; First, calculate the address of the SMBASE field, based on the SMM Revision
  111. ; ID; store the result in EBX.
  112. ;
  113. mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + SaveStateRevId)]
  114. test eax, 0xFFFF
  115. jz LegacySaveStateMap
  116. mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase64
  117. jmp UpdateSmbase
  118. LegacySaveStateMap:
  119. mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase
  120. UpdateSmbase:
  121. ;
  122. ; Load the new SMBASE value into EAX.
  123. ;
  124. mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + NewSmbase)]
  125. ;
  126. ; Save it to the SMBASE field whose address we calculated in EBX.
  127. ;
  128. mov dword [ds : dword ebx], eax
  129. ;
  130. ; Set AboutToLeaveSmm.
  131. ;
  132. mov byte [ds : dword (SMM_DEFAULT_SMBASE + AboutToLeaveSmm)], 1
  133. ;
  134. ; We're done; leave SMM and continue to the pen.
  135. ;
  136. rsm
  137. ASM_PFX (mFirstSmiHandlerSize):
  138. dw $ - ASM_PFX (mFirstSmiHandler)