AmdSev.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /** @file
  2. File defines the Sec routines for the AMD SEV
  3. Copyright (c) 2021, Advanced Micro Devices, Inc. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include <Library/BaseLib.h>
  7. #include <Library/DebugLib.h>
  8. #include <Library/MemEncryptSevLib.h>
  9. #include <Library/BaseMemoryLib.h>
  10. #include <Register/Amd/Ghcb.h>
  11. #include <Register/Amd/Msr.h>
  12. #include "AmdSev.h"
  13. /**
  14. Handle an SEV-ES/GHCB protocol check failure.
  15. Notify the hypervisor using the VMGEXIT instruction that the SEV-ES guest
  16. wishes to be terminated.
  17. @param[in] ReasonCode Reason code to provide to the hypervisor for the
  18. termination request.
  19. **/
  20. VOID
  21. SevEsProtocolFailure (
  22. IN UINT8 ReasonCode
  23. )
  24. {
  25. MSR_SEV_ES_GHCB_REGISTER Msr;
  26. //
  27. // Use the GHCB MSR Protocol to request termination by the hypervisor
  28. //
  29. Msr.GhcbPhysicalAddress = 0;
  30. Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
  31. Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
  32. Msr.GhcbTerminate.ReasonCode = ReasonCode;
  33. AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
  34. AsmVmgExit ();
  35. ASSERT (FALSE);
  36. CpuDeadLoop ();
  37. }
  38. /**
  39. Determine if SEV-SNP is active.
  40. @retval TRUE SEV-SNP is enabled
  41. @retval FALSE SEV-SNP is not enabled
  42. **/
  43. BOOLEAN
  44. SevSnpIsEnabled (
  45. VOID
  46. )
  47. {
  48. MSR_SEV_STATUS_REGISTER Msr;
  49. //
  50. // Read the SEV_STATUS MSR to determine whether SEV-SNP is active.
  51. //
  52. Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
  53. //
  54. // Check MSR_0xC0010131 Bit 2 (Sev-Snp Enabled)
  55. //
  56. if (Msr.Bits.SevSnpBit) {
  57. return TRUE;
  58. }
  59. return FALSE;
  60. }
  61. /**
  62. Register the GHCB GPA
  63. */
  64. STATIC
  65. VOID
  66. SevSnpGhcbRegister (
  67. EFI_PHYSICAL_ADDRESS Address
  68. )
  69. {
  70. MSR_SEV_ES_GHCB_REGISTER Msr;
  71. //
  72. // Use the GHCB MSR Protocol to request to register the GPA.
  73. //
  74. Msr.GhcbPhysicalAddress = Address & ~EFI_PAGE_MASK;
  75. Msr.GhcbGpaRegister.Function = GHCB_INFO_GHCB_GPA_REGISTER_REQUEST;
  76. AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
  77. AsmVmgExit ();
  78. Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
  79. //
  80. // If hypervisor responded with a different GPA than requested then fail.
  81. //
  82. if ((Msr.GhcbGpaRegister.Function != GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE) ||
  83. ((Msr.GhcbPhysicalAddress & ~EFI_PAGE_MASK) != Address))
  84. {
  85. SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
  86. }
  87. }
  88. /**
  89. Verify that Hypervisor supports the SNP feature.
  90. */
  91. STATIC
  92. BOOLEAN
  93. HypervisorSnpFeatureCheck (
  94. VOID
  95. )
  96. {
  97. MSR_SEV_ES_GHCB_REGISTER Msr;
  98. UINT64 Features;
  99. //
  100. // Use the GHCB MSR Protocol to query the hypervisor capabilities
  101. //
  102. Msr.GhcbPhysicalAddress = 0;
  103. Msr.GhcbHypervisorFeatures.Function = GHCB_HYPERVISOR_FEATURES_REQUEST;
  104. AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
  105. AsmVmgExit ();
  106. Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
  107. Features = RShiftU64 (Msr.GhcbPhysicalAddress, 12);
  108. if ((Msr.GhcbHypervisorFeatures.Function != GHCB_HYPERVISOR_FEATURES_RESPONSE) ||
  109. (!(Features & GHCB_HV_FEATURES_SNP)))
  110. {
  111. return FALSE;
  112. }
  113. return TRUE;
  114. }
  115. /**
  116. Validate the SEV-ES/GHCB protocol level.
  117. Verify that the level of SEV-ES/GHCB protocol supported by the hypervisor
  118. and the guest intersect. If they don't intersect, request termination.
  119. **/
  120. VOID
  121. SevEsProtocolCheck (
  122. VOID
  123. )
  124. {
  125. MSR_SEV_ES_GHCB_REGISTER Msr;
  126. GHCB *Ghcb;
  127. //
  128. // Use the GHCB MSR Protocol to obtain the GHCB SEV-ES Information for
  129. // protocol checking
  130. //
  131. Msr.GhcbPhysicalAddress = 0;
  132. Msr.GhcbInfo.Function = GHCB_INFO_SEV_INFO_GET;
  133. AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
  134. AsmVmgExit ();
  135. Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
  136. if (Msr.GhcbInfo.Function != GHCB_INFO_SEV_INFO) {
  137. SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
  138. }
  139. if (Msr.GhcbProtocol.SevEsProtocolMin > Msr.GhcbProtocol.SevEsProtocolMax) {
  140. SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
  141. }
  142. if ((Msr.GhcbProtocol.SevEsProtocolMin > GHCB_VERSION_MAX) ||
  143. (Msr.GhcbProtocol.SevEsProtocolMax < GHCB_VERSION_MIN))
  144. {
  145. SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
  146. }
  147. //
  148. // We cannot use the MemEncryptSevSnpIsEnabled () because the
  149. // ProcessLibraryConstructorList () is not called yet.
  150. //
  151. if (SevSnpIsEnabled ()) {
  152. //
  153. // Check if hypervisor supports the SNP feature
  154. //
  155. if (!HypervisorSnpFeatureCheck ()) {
  156. SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
  157. }
  158. //
  159. // Unlike the SEV-ES guest, the SNP requires that GHCB GPA must be
  160. // registered with the Hypervisor before the use. This can be done
  161. // using the new VMGEXIT defined in the GHCB v2. Register the GPA
  162. // before it is used.
  163. //
  164. SevSnpGhcbRegister ((EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase));
  165. }
  166. //
  167. // SEV-ES protocol checking succeeded, set the initial GHCB address
  168. //
  169. Msr.GhcbPhysicalAddress = FixedPcdGet32 (PcdOvmfSecGhcbBase);
  170. AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
  171. Ghcb = Msr.Ghcb;
  172. SetMem (Ghcb, FixedPcdGet32 (PcdOvmfSecGhcbSize), 0);
  173. //
  174. // Set the version to the maximum that can be supported
  175. //
  176. Ghcb->ProtocolVersion = MIN (Msr.GhcbProtocol.SevEsProtocolMax, GHCB_VERSION_MAX);
  177. Ghcb->GhcbUsage = GHCB_STANDARD_USAGE;
  178. }
  179. /**
  180. Determine if the SEV is active.
  181. During the early booting, GuestType is set in the work area. Verify that it
  182. is an SEV guest.
  183. @retval TRUE SEV is enabled
  184. @retval FALSE SEV is not enabled
  185. **/
  186. BOOLEAN
  187. IsSevGuest (
  188. VOID
  189. )
  190. {
  191. OVMF_WORK_AREA *WorkArea;
  192. //
  193. // Ensure that the size of the Confidential Computing work area header
  194. // is same as what is provided through a fixed PCD.
  195. //
  196. ASSERT (
  197. (UINTN)FixedPcdGet32 (PcdOvmfConfidentialComputingWorkAreaHeader) ==
  198. sizeof (CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER)
  199. );
  200. WorkArea = (OVMF_WORK_AREA *)FixedPcdGet32 (PcdOvmfWorkAreaBase);
  201. return ((WorkArea != NULL) && (WorkArea->Header.GuestType == CcGuestTypeAmdSev));
  202. }
  203. /**
  204. Determine if SEV-ES is active.
  205. During early booting, SEV-ES support code will set a flag to indicate that
  206. SEV-ES is enabled. Return the value of this flag as an indicator that SEV-ES
  207. is enabled.
  208. @retval TRUE SEV-ES is enabled
  209. @retval FALSE SEV-ES is not enabled
  210. **/
  211. BOOLEAN
  212. SevEsIsEnabled (
  213. VOID
  214. )
  215. {
  216. SEC_SEV_ES_WORK_AREA *SevEsWorkArea;
  217. if (!IsSevGuest ()) {
  218. return FALSE;
  219. }
  220. SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *)FixedPcdGet32 (PcdSevEsWorkAreaBase);
  221. return ((SevEsWorkArea->SevStatusMsrValue & BIT1) != 0);
  222. }
  223. /**
  224. Validate System RAM used for decompressing the PEI and DXE firmware volumes
  225. when SEV-SNP is active. The PCDs SecValidatedStart and SecValidatedEnd are
  226. set in OvmfPkg/Include/Fdf/FvmainCompactScratchEnd.fdf.inc.
  227. **/
  228. VOID
  229. SecValidateSystemRam (
  230. VOID
  231. )
  232. {
  233. PHYSICAL_ADDRESS Start, End;
  234. if (IsSevGuest () && SevSnpIsEnabled ()) {
  235. Start = (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecValidatedStart);
  236. End = (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecValidatedEnd);
  237. MemEncryptSevSnpPreValidateSystemRam (Start, EFI_SIZE_TO_PAGES ((UINTN)(End - Start)));
  238. }
  239. }