AmdSev.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /** @file
  2. AMD SEV helper function.
  3. Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "MpLib.h"
  7. #include <Library/CcExitLib.h>
  8. #include <Register/Amd/Fam17Msr.h>
  9. #include <Register/Amd/Ghcb.h>
  10. /**
  11. Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.
  12. @param[in] CpuMpData Pointer to CPU MP Data
  13. @param[in] CpuData Pointer to CPU AP Data
  14. @param[in] ApicId APIC ID of the vCPU
  15. **/
  16. VOID
  17. SevSnpCreateSaveArea (
  18. IN CPU_MP_DATA *CpuMpData,
  19. IN CPU_AP_DATA *CpuData,
  20. UINT32 ApicId
  21. )
  22. {
  23. SEV_ES_SAVE_AREA *SaveArea;
  24. IA32_CR0 ApCr0;
  25. IA32_CR0 ResetCr0;
  26. IA32_CR4 ApCr4;
  27. IA32_CR4 ResetCr4;
  28. UINTN StartIp;
  29. UINT8 SipiVector;
  30. UINT32 RmpAdjustStatus;
  31. UINT64 VmgExitStatus;
  32. MSR_SEV_ES_GHCB_REGISTER Msr;
  33. GHCB *Ghcb;
  34. BOOLEAN InterruptState;
  35. UINT64 ExitInfo1;
  36. UINT64 ExitInfo2;
  37. //
  38. // Allocate a single page for the SEV-ES Save Area and initialize it.
  39. //
  40. SaveArea = AllocateReservedPages (1);
  41. if (!SaveArea) {
  42. return;
  43. }
  44. ZeroMem (SaveArea, EFI_PAGE_SIZE);
  45. //
  46. // Propogate the CR0.NW and CR0.CD setting to the AP
  47. //
  48. ResetCr0.UintN = 0x00000010;
  49. ApCr0.UintN = CpuData->VolatileRegisters.Cr0;
  50. if (ApCr0.Bits.NW) {
  51. ResetCr0.Bits.NW = 1;
  52. }
  53. if (ApCr0.Bits.CD) {
  54. ResetCr0.Bits.CD = 1;
  55. }
  56. //
  57. // Propagate the CR4.MCE setting to the AP
  58. //
  59. ResetCr4.UintN = 0;
  60. ApCr4.UintN = CpuData->VolatileRegisters.Cr4;
  61. if (ApCr4.Bits.MCE) {
  62. ResetCr4.Bits.MCE = 1;
  63. }
  64. //
  65. // Convert the start IP into a SIPI Vector
  66. //
  67. StartIp = CpuMpData->MpCpuExchangeInfo->BufferStart;
  68. SipiVector = (UINT8)(StartIp >> 12);
  69. //
  70. // Set the CS:RIP value based on the start IP
  71. //
  72. SaveArea->Cs.Base = SipiVector << 12;
  73. SaveArea->Cs.Selector = SipiVector << 8;
  74. SaveArea->Cs.Limit = 0xFFFF;
  75. SaveArea->Cs.Attributes.Bits.Present = 1;
  76. SaveArea->Cs.Attributes.Bits.Sbit = 1;
  77. SaveArea->Cs.Attributes.Bits.Type = SEV_ES_RESET_CODE_SEGMENT_TYPE;
  78. SaveArea->Rip = StartIp & 0xFFF;
  79. //
  80. // Set the remaining values as defined in APM for INIT
  81. //
  82. SaveArea->Ds.Limit = 0xFFFF;
  83. SaveArea->Ds.Attributes.Bits.Present = 1;
  84. SaveArea->Ds.Attributes.Bits.Sbit = 1;
  85. SaveArea->Ds.Attributes.Bits.Type = SEV_ES_RESET_DATA_SEGMENT_TYPE;
  86. SaveArea->Es = SaveArea->Ds;
  87. SaveArea->Fs = SaveArea->Ds;
  88. SaveArea->Gs = SaveArea->Ds;
  89. SaveArea->Ss = SaveArea->Ds;
  90. SaveArea->Gdtr.Limit = 0xFFFF;
  91. SaveArea->Ldtr.Limit = 0xFFFF;
  92. SaveArea->Ldtr.Attributes.Bits.Present = 1;
  93. SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_LDT_TYPE;
  94. SaveArea->Idtr.Limit = 0xFFFF;
  95. SaveArea->Tr.Limit = 0xFFFF;
  96. SaveArea->Ldtr.Attributes.Bits.Present = 1;
  97. SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_TSS_TYPE;
  98. SaveArea->Efer = 0x1000;
  99. SaveArea->Cr4 = ResetCr4.UintN;
  100. SaveArea->Cr0 = ResetCr0.UintN;
  101. SaveArea->Dr7 = 0x0400;
  102. SaveArea->Dr6 = 0xFFFF0FF0;
  103. SaveArea->Rflags = 0x0002;
  104. SaveArea->GPat = 0x0007040600070406ULL;
  105. SaveArea->XCr0 = 0x0001;
  106. SaveArea->Mxcsr = 0x1F80;
  107. SaveArea->X87Ftw = 0x5555;
  108. SaveArea->X87Fcw = 0x0040;
  109. //
  110. // Set the SEV-SNP specific fields for the save area:
  111. // VMPL - always VMPL0
  112. // SEV_FEATURES - equivalent to the SEV_STATUS MSR right shifted 2 bits
  113. //
  114. SaveArea->Vmpl = 0;
  115. SaveArea->SevFeatures = AsmReadMsr64 (MSR_SEV_STATUS) >> 2;
  116. //
  117. // To turn the page into a recognized VMSA page, issue RMPADJUST:
  118. // Target VMPL but numerically higher than current VMPL
  119. // Target PermissionMask is not used
  120. //
  121. RmpAdjustStatus = SevSnpRmpAdjust (
  122. (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,
  123. TRUE
  124. );
  125. ASSERT (RmpAdjustStatus == 0);
  126. ExitInfo1 = (UINT64)ApicId << 32;
  127. ExitInfo1 |= SVM_VMGEXIT_SNP_AP_CREATE;
  128. ExitInfo2 = (UINT64)(UINTN)SaveArea;
  129. Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
  130. Ghcb = Msr.Ghcb;
  131. CcExitVmgInit (Ghcb, &InterruptState);
  132. Ghcb->SaveArea.Rax = SaveArea->SevFeatures;
  133. CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
  134. VmgExitStatus = CcExitVmgExit (
  135. Ghcb,
  136. SVM_EXIT_SNP_AP_CREATION,
  137. ExitInfo1,
  138. ExitInfo2
  139. );
  140. CcExitVmgDone (Ghcb, InterruptState);
  141. ASSERT (VmgExitStatus == 0);
  142. if (VmgExitStatus != 0) {
  143. RmpAdjustStatus = SevSnpRmpAdjust (
  144. (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,
  145. FALSE
  146. );
  147. if (RmpAdjustStatus == 0) {
  148. FreePages (SaveArea, 1);
  149. } else {
  150. DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));
  151. }
  152. SaveArea = NULL;
  153. }
  154. if (CpuData->SevEsSaveArea) {
  155. RmpAdjustStatus = SevSnpRmpAdjust (
  156. (EFI_PHYSICAL_ADDRESS)(UINTN)CpuData->SevEsSaveArea,
  157. FALSE
  158. );
  159. if (RmpAdjustStatus == 0) {
  160. FreePages (CpuData->SevEsSaveArea, 1);
  161. } else {
  162. DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));
  163. }
  164. }
  165. CpuData->SevEsSaveArea = SaveArea;
  166. }
  167. /**
  168. Create SEV-SNP APs.
  169. @param[in] CpuMpData Pointer to CPU MP Data
  170. @param[in] ProcessorNumber The handle number of specified processor
  171. (-1 for all APs)
  172. **/
  173. VOID
  174. SevSnpCreateAP (
  175. IN CPU_MP_DATA *CpuMpData,
  176. IN INTN ProcessorNumber
  177. )
  178. {
  179. CPU_INFO_IN_HOB *CpuInfoInHob;
  180. CPU_AP_DATA *CpuData;
  181. UINTN Index;
  182. UINT32 ApicId;
  183. ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000);
  184. CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;
  185. if (ProcessorNumber < 0) {
  186. for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
  187. if (Index != CpuMpData->BspNumber) {
  188. CpuData = &CpuMpData->CpuData[Index];
  189. ApicId = CpuInfoInHob[Index].ApicId,
  190. SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);
  191. }
  192. }
  193. } else {
  194. Index = (UINTN)ProcessorNumber;
  195. CpuData = &CpuMpData->CpuData[Index];
  196. ApicId = CpuInfoInHob[ProcessorNumber].ApicId,
  197. SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);
  198. }
  199. }
  200. /**
  201. Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.
  202. @param[in] PageAddress
  203. @param[in] VmsaPage
  204. @return RMPADJUST return value
  205. **/
  206. UINT32
  207. SevSnpRmpAdjust (
  208. IN EFI_PHYSICAL_ADDRESS PageAddress,
  209. IN BOOLEAN VmsaPage
  210. )
  211. {
  212. UINT64 Rdx;
  213. //
  214. // The RMPADJUST instruction is used to set or clear the VMSA bit for a
  215. // page. The VMSA change is only made when running at VMPL0 and is ignored
  216. // otherwise. If too low a target VMPL is specified, the instruction can
  217. // succeed without changing the VMSA bit when not running at VMPL0. Using a
  218. // target VMPL level of 1, RMPADJUST will return a FAIL_PERMISSION error if
  219. // not running at VMPL0, thus ensuring that the VMSA bit is set appropriately
  220. // when no error is returned.
  221. //
  222. Rdx = 1;
  223. if (VmsaPage) {
  224. Rdx |= RMPADJUST_VMSA_PAGE_BIT;
  225. }
  226. return AsmRmpAdjust ((UINT64)PageAddress, 0, Rdx);
  227. }