AmdSev.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /** @file
  2. CPU MP Initialize helper function for AMD SEV.
  3. Copyright (c) 2021, AMD Inc. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "MpLib.h"
  7. #include <Library/CcExitLib.h>
  8. /**
  9. Get Protected mode code segment with 16-bit default addressing
  10. from current GDT table.
  11. @return Protected mode 16-bit code segment value.
  12. **/
  13. STATIC
  14. UINT16
  15. GetProtectedMode16CS (
  16. VOID
  17. )
  18. {
  19. IA32_DESCRIPTOR GdtrDesc;
  20. IA32_SEGMENT_DESCRIPTOR *GdtEntry;
  21. UINTN GdtEntryCount;
  22. UINT16 Index;
  23. Index = (UINT16)-1;
  24. AsmReadGdtr (&GdtrDesc);
  25. GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
  26. GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;
  27. for (Index = 0; Index < GdtEntryCount; Index++) {
  28. if ((GdtEntry->Bits.L == 0) &&
  29. (GdtEntry->Bits.DB == 0) &&
  30. (GdtEntry->Bits.Type > 8))
  31. {
  32. break;
  33. }
  34. GdtEntry++;
  35. }
  36. ASSERT (Index != GdtEntryCount);
  37. return Index * 8;
  38. }
  39. /**
  40. Get Protected mode code segment with 32-bit default addressing
  41. from current GDT table.
  42. @return Protected mode 32-bit code segment value.
  43. **/
  44. STATIC
  45. UINT16
  46. GetProtectedMode32CS (
  47. VOID
  48. )
  49. {
  50. IA32_DESCRIPTOR GdtrDesc;
  51. IA32_SEGMENT_DESCRIPTOR *GdtEntry;
  52. UINTN GdtEntryCount;
  53. UINT16 Index;
  54. Index = (UINT16)-1;
  55. AsmReadGdtr (&GdtrDesc);
  56. GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
  57. GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;
  58. for (Index = 0; Index < GdtEntryCount; Index++) {
  59. if ((GdtEntry->Bits.L == 0) &&
  60. (GdtEntry->Bits.DB == 1) &&
  61. (GdtEntry->Bits.Type > 8))
  62. {
  63. break;
  64. }
  65. GdtEntry++;
  66. }
  67. ASSERT (Index != GdtEntryCount);
  68. return Index * 8;
  69. }
  70. /**
  71. Reset an AP when in SEV-ES mode.
  72. If successful, this function never returns.
  73. @param[in] Ghcb Pointer to the GHCB
  74. @param[in] CpuMpData Pointer to CPU MP Data
  75. **/
  76. VOID
  77. MpInitLibSevEsAPReset (
  78. IN GHCB *Ghcb,
  79. IN CPU_MP_DATA *CpuMpData
  80. )
  81. {
  82. EFI_STATUS Status;
  83. UINTN ProcessorNumber;
  84. UINT16 Code16, Code32;
  85. AP_RESET *APResetFn;
  86. UINTN BufferStart;
  87. UINTN StackStart;
  88. Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);
  89. ASSERT_EFI_ERROR (Status);
  90. Code16 = GetProtectedMode16CS ();
  91. Code32 = GetProtectedMode32CS ();
  92. APResetFn = (AP_RESET *)(CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
  93. BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
  94. StackStart = CpuMpData->SevEsAPResetStackStart -
  95. (AP_RESET_STACK_SIZE * ProcessorNumber);
  96. //
  97. // This call never returns.
  98. //
  99. APResetFn (BufferStart, Code16, Code32, StackStart);
  100. }
  101. /**
  102. Allocate the SEV-ES AP jump table buffer.
  103. @param[in, out] CpuMpData The pointer to CPU MP Data structure.
  104. **/
  105. VOID
  106. AllocateSevEsAPMemory (
  107. IN OUT CPU_MP_DATA *CpuMpData
  108. )
  109. {
  110. if (CpuMpData->SevEsAPBuffer == (UINTN)-1) {
  111. CpuMpData->SevEsAPBuffer =
  112. CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
  113. }
  114. }
  115. /**
  116. Program the SEV-ES AP jump table buffer.
  117. @param[in] SipiVector The SIPI vector used for the AP Reset
  118. **/
  119. VOID
  120. SetSevEsJumpTable (
  121. IN UINTN SipiVector
  122. )
  123. {
  124. SEV_ES_AP_JMP_FAR *JmpFar;
  125. UINT32 Offset, InsnByte;
  126. UINT8 LoNib, HiNib;
  127. JmpFar = (SEV_ES_AP_JMP_FAR *)(UINTN)FixedPcdGet32 (PcdSevEsWorkAreaBase);
  128. ASSERT (JmpFar != NULL);
  129. //
  130. // Obtain the address of the Segment/Rip location in the workarea.
  131. // This will be set to a value derived from the SIPI vector and will
  132. // be the memory address used for the far jump below.
  133. //
  134. Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
  135. Offset += sizeof (JmpFar->InsnBuffer);
  136. LoNib = (UINT8)Offset;
  137. HiNib = (UINT8)(Offset >> 8);
  138. //
  139. // Program the workarea (which is the initial AP boot address) with
  140. // far jump to the SIPI vector (where XX and YY represent the
  141. // address of where the SIPI vector is stored.
  142. //
  143. // JMP FAR [CS:XXYY] => 2E FF 2E YY XX
  144. //
  145. InsnByte = 0;
  146. JmpFar->InsnBuffer[InsnByte++] = 0x2E; // CS override prefix
  147. JmpFar->InsnBuffer[InsnByte++] = 0xFF; // JMP (FAR)
  148. JmpFar->InsnBuffer[InsnByte++] = 0x2E; // ModRM (JMP memory location)
  149. JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
  150. JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
  151. //
  152. // Program the Segment/Rip based on the SIPI vector (always at least
  153. // 16-byte aligned, so Rip is set to 0).
  154. //
  155. JmpFar->Rip = 0;
  156. JmpFar->Segment = (UINT16)(SipiVector >> 4);
  157. }
  158. /**
  159. The function puts the AP in halt loop.
  160. @param[in] CpuMpData The pointer to CPU MP Data structure.
  161. **/
  162. VOID
  163. SevEsPlaceApHlt (
  164. CPU_MP_DATA *CpuMpData
  165. )
  166. {
  167. MSR_SEV_ES_GHCB_REGISTER Msr;
  168. GHCB *Ghcb;
  169. UINT64 Status;
  170. BOOLEAN DoDecrement;
  171. BOOLEAN InterruptState;
  172. DoDecrement = (BOOLEAN)(CpuMpData->InitFlag == ApInitConfig);
  173. while (TRUE) {
  174. Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
  175. Ghcb = Msr.Ghcb;
  176. VmgInit (Ghcb, &InterruptState);
  177. if (DoDecrement) {
  178. DoDecrement = FALSE;
  179. //
  180. // Perform the delayed decrement just before issuing the first
  181. // VMGEXIT with AP_RESET_HOLD.
  182. //
  183. InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
  184. }
  185. Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
  186. if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
  187. VmgDone (Ghcb, InterruptState);
  188. break;
  189. }
  190. VmgDone (Ghcb, InterruptState);
  191. }
  192. //
  193. // Awakened in a new phase? Use the new CpuMpData
  194. //
  195. if (CpuMpData->NewCpuMpData != NULL) {
  196. CpuMpData = CpuMpData->NewCpuMpData;
  197. }
  198. MpInitLibSevEsAPReset (Ghcb, CpuMpData);
  199. }
  200. /**
  201. The function fills the exchange data for the AP.
  202. @param[in] ExchangeInfo The pointer to CPU Exchange Data structure
  203. **/
  204. VOID
  205. FillExchangeInfoDataSevEs (
  206. IN volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo
  207. )
  208. {
  209. UINT32 StdRangeMax;
  210. AsmCpuid (CPUID_SIGNATURE, &StdRangeMax, NULL, NULL, NULL);
  211. if (StdRangeMax >= CPUID_EXTENDED_TOPOLOGY) {
  212. CPUID_EXTENDED_TOPOLOGY_EBX ExtTopoEbx;
  213. AsmCpuid (CPUID_EXTENDED_TOPOLOGY, NULL, &ExtTopoEbx.Uint32, NULL, NULL);
  214. ExchangeInfo->ExtTopoAvail = !!ExtTopoEbx.Bits.LogicalProcessors;
  215. }
  216. }