3 SEV-SNP Page Validation functions.
5 Copyright (c) 2021 AMD Incorporated. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Uefi/UefiBaseType.h>
12 #include <Library/BaseLib.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/MemEncryptSevLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/CcExitLib.h>
18 #include <Register/Amd/Ghcb.h>
19 #include <Register/Amd/Msr.h>
21 #include "SnpPageStateChange.h"
23 #define IS_ALIGNED(x, y) ((((x) & (y - 1)) == 0))
24 #define PAGES_PER_LARGE_ENTRY 512
29 IN SEV_SNP_PAGE_STATE State
35 case SevSnpPageShared
: Cmd
= SNP_PAGE_STATE_SHARED
;
37 case SevSnpPagePrivate
: Cmd
= SNP_PAGE_STATE_PRIVATE
;
46 SnpPageStateFailureTerminate (
50 MSR_SEV_ES_GHCB_REGISTER Msr
;
53 // Use the GHCB MSR Protocol to request termination by the hypervisor
55 Msr
.GhcbPhysicalAddress
= 0;
56 Msr
.GhcbTerminate
.Function
= GHCB_INFO_TERMINATE_REQUEST
;
57 Msr
.GhcbTerminate
.ReasonCodeSet
= GHCB_TERMINATE_GHCB
;
58 Msr
.GhcbTerminate
.ReasonCode
= GHCB_TERMINATE_GHCB_GENERAL
;
59 AsmWriteMsr64 (MSR_SEV_ES_GHCB
, Msr
.GhcbPhysicalAddress
);
68 This function issues the PVALIDATE instruction to validate or invalidate the memory
69 range specified. If PVALIDATE returns size mismatch then it retry validating with
76 IN SNP_PAGE_STATE_CHANGE_INFO
*Info
,
82 UINTN Address
, RmpPageSize
, Ret
, i
;
84 for ( ; StartIndex
<= EndIndex
; StartIndex
++) {
86 // Get the address and the page size from the Info.
88 Address
= Info
->Entry
[StartIndex
].GuestFrameNumber
<< EFI_PAGE_SHIFT
;
89 RmpPageSize
= Info
->Entry
[StartIndex
].PageSize
;
91 Ret
= AsmPvalidate (RmpPageSize
, Validate
, Address
);
94 // If we fail to validate due to size mismatch then try with the
95 // smaller page size. This senario will occur if the backing page in
96 // the RMP entry is 4K and we are validating it as a 2MB.
98 if ((Ret
== PVALIDATE_RET_SIZE_MISMATCH
) && (RmpPageSize
== PvalidatePageSize2MB
)) {
99 for (i
= 0; i
< PAGES_PER_LARGE_ENTRY
; i
++) {
100 Ret
= AsmPvalidate (PvalidatePageSize4K
, Validate
, Address
);
105 Address
= Address
+ EFI_PAGE_SIZE
;
110 // If validation failed then do not continue.
115 "%a:%a: Failed to %a address 0x%Lx Error code %d\n",
118 Validate
? "Validate" : "Invalidate",
122 SnpPageStateFailureTerminate ();
129 BuildPageStateBuffer (
130 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
131 IN EFI_PHYSICAL_ADDRESS EndAddress
,
132 IN SEV_SNP_PAGE_STATE State
,
133 IN BOOLEAN UseLargeEntry
,
134 IN SNP_PAGE_STATE_CHANGE_INFO
*Info
137 EFI_PHYSICAL_ADDRESS NextAddress
;
138 UINTN i
, RmpPageSize
;
140 // Clear the page state structure
141 SetMem (Info
, sizeof (*Info
), 0);
144 NextAddress
= EndAddress
;
147 // Populate the page state entry structure
149 while ((BaseAddress
< EndAddress
) && (i
< SNP_PAGE_STATE_MAX_ENTRY
)) {
151 // Is this a 2MB aligned page? Check if we can use the Large RMP entry.
153 if (UseLargeEntry
&& IS_ALIGNED (BaseAddress
, SIZE_2MB
) &&
154 ((EndAddress
- BaseAddress
) >= SIZE_2MB
))
156 RmpPageSize
= PvalidatePageSize2MB
;
157 NextAddress
= BaseAddress
+ SIZE_2MB
;
159 RmpPageSize
= PvalidatePageSize4K
;
160 NextAddress
= BaseAddress
+ EFI_PAGE_SIZE
;
163 Info
->Entry
[i
].GuestFrameNumber
= BaseAddress
>> EFI_PAGE_SHIFT
;
164 Info
->Entry
[i
].PageSize
= RmpPageSize
;
165 Info
->Entry
[i
].Operation
= MemoryStateToGhcbOp (State
);
166 Info
->Entry
[i
].CurrentPage
= 0;
167 Info
->Header
.EndEntry
= (UINT16
)i
;
169 BaseAddress
= NextAddress
;
178 PageStateChangeVmgExit (
180 IN SNP_PAGE_STATE_CHANGE_INFO
*Info
186 // As per the GHCB specification, the hypervisor can resume the guest before
187 // processing all the entries. Checks whether all the entries are processed.
189 // The stragtegy here is to wait for the hypervisor to change the page
190 // state in the RMP table before guest access the memory pages. If the
191 // page state was not successful, then later memory access will result
194 while (Info
->Header
.CurrentEntry
<= Info
->Header
.EndEntry
) {
195 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
196 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
198 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_SNP_PAGE_STATE_CHANGE
, 0, 0);
201 // The Page State Change VMGEXIT can pass the failure through the
202 // ExitInfo2. Lets check both the return value as well as ExitInfo2.
204 if ((Status
!= 0) || (Ghcb
->SaveArea
.SwExitInfo2
)) {
205 SnpPageStateFailureTerminate ();
211 The function is used to set the page state when SEV-SNP is active. The page state
212 transition consist of changing the page ownership in the RMP table, and using the
213 PVALIDATE instruction to update the Validated bit in RMP table.
215 When the UseLargeEntry is set to TRUE, then function will try to use the large RMP
216 entry (whevever possible).
219 InternalSetPageState (
220 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
222 IN SEV_SNP_PAGE_STATE State
,
223 IN BOOLEAN UseLargeEntry
227 EFI_PHYSICAL_ADDRESS NextAddress
, EndAddress
;
228 MSR_SEV_ES_GHCB_REGISTER Msr
;
229 BOOLEAN InterruptState
;
230 SNP_PAGE_STATE_CHANGE_INFO
*Info
;
232 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
235 EndAddress
= BaseAddress
+ EFI_PAGES_TO_SIZE (NumPages
);
239 "%a:%a Address 0x%Lx - 0x%Lx State = %a LargeEntry = %d\n",
244 State
== SevSnpPageShared
? "Shared" : "Private",
248 while (BaseAddress
< EndAddress
) {
249 UINTN CurrentEntry
, EndEntry
;
252 // Initialize the GHCB
254 CcExitVmgInit (Ghcb
, &InterruptState
);
257 // Build the page state structure
259 Info
= (SNP_PAGE_STATE_CHANGE_INFO
*)Ghcb
->SharedBuffer
;
260 NextAddress
= BuildPageStateBuffer (
269 // Save the current and end entry from the page state structure. We need
272 CurrentEntry
= Info
->Header
.CurrentEntry
;
273 EndEntry
= Info
->Header
.EndEntry
;
276 // If the caller requested to change the page state to shared then
277 // invalidate the pages before making the page shared in the RMP table.
279 if (State
== SevSnpPageShared
) {
280 PvalidateRange (Info
, CurrentEntry
, EndEntry
, FALSE
);
284 // Invoke the page state change VMGEXIT.
286 PageStateChangeVmgExit (Ghcb
, Info
);
289 // If the caller requested to change the page state to private then
290 // validate the pages after it has been added in the RMP table.
292 if (State
== SevSnpPagePrivate
) {
293 PvalidateRange (Info
, CurrentEntry
, EndEntry
, TRUE
);
296 CcExitVmgDone (Ghcb
, InterruptState
);
298 BaseAddress
= NextAddress
;