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/VmgExitLib.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
;
47 SnpPageStateFailureTerminate (
51 MSR_SEV_ES_GHCB_REGISTER Msr
;
54 // Use the GHCB MSR Protocol to request termination by the hypervisor
56 Msr
.GhcbPhysicalAddress
= 0;
57 Msr
.GhcbTerminate
.Function
= GHCB_INFO_TERMINATE_REQUEST
;
58 Msr
.GhcbTerminate
.ReasonCodeSet
= GHCB_TERMINATE_GHCB
;
59 Msr
.GhcbTerminate
.ReasonCode
= GHCB_TERMINATE_GHCB_GENERAL
;
60 AsmWriteMsr64 (MSR_SEV_ES_GHCB
, Msr
.GhcbPhysicalAddress
);
69 This function issues the PVALIDATE instruction to validate or invalidate the memory
70 range specified. If PVALIDATE returns size mismatch then it retry validating with
77 IN SNP_PAGE_STATE_CHANGE_INFO
*Info
,
83 UINTN Address
, RmpPageSize
, Ret
, i
;
85 for ( ; StartIndex
<= EndIndex
; StartIndex
++) {
87 // Get the address and the page size from the Info.
89 Address
= Info
->Entry
[StartIndex
].GuestFrameNumber
<< EFI_PAGE_SHIFT
;
90 RmpPageSize
= Info
->Entry
[StartIndex
].PageSize
;
92 Ret
= AsmPvalidate (RmpPageSize
, Validate
, Address
);
95 // If we fail to validate due to size mismatch then try with the
96 // smaller page size. This senario will occur if the backing page in
97 // the RMP entry is 4K and we are validating it as a 2MB.
99 if ((Ret
== PVALIDATE_RET_SIZE_MISMATCH
) && (RmpPageSize
== PvalidatePageSize2MB
)) {
100 for (i
= 0; i
< PAGES_PER_LARGE_ENTRY
; i
++) {
101 Ret
= AsmPvalidate (PvalidatePageSize4K
, Validate
, Address
);
106 Address
= Address
+ EFI_PAGE_SIZE
;
111 // If validation failed then do not continue.
116 "%a:%a: Failed to %a address 0x%Lx Error code %d\n",
119 Validate
? "Validate" : "Invalidate",
123 SnpPageStateFailureTerminate ();
130 BuildPageStateBuffer (
131 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
132 IN EFI_PHYSICAL_ADDRESS EndAddress
,
133 IN SEV_SNP_PAGE_STATE State
,
134 IN BOOLEAN UseLargeEntry
,
135 IN SNP_PAGE_STATE_CHANGE_INFO
*Info
138 EFI_PHYSICAL_ADDRESS NextAddress
;
139 UINTN i
, RmpPageSize
;
141 // Clear the page state structure
142 SetMem (Info
, sizeof (*Info
), 0);
145 NextAddress
= EndAddress
;
148 // Populate the page state entry structure
150 while ((BaseAddress
< EndAddress
) && (i
< SNP_PAGE_STATE_MAX_ENTRY
)) {
152 // Is this a 2MB aligned page? Check if we can use the Large RMP entry.
154 if (UseLargeEntry
&& IS_ALIGNED (BaseAddress
, SIZE_2MB
) &&
155 ((EndAddress
- BaseAddress
) >= SIZE_2MB
))
157 RmpPageSize
= PvalidatePageSize2MB
;
158 NextAddress
= BaseAddress
+ SIZE_2MB
;
160 RmpPageSize
= PvalidatePageSize4K
;
161 NextAddress
= BaseAddress
+ EFI_PAGE_SIZE
;
164 Info
->Entry
[i
].GuestFrameNumber
= BaseAddress
>> EFI_PAGE_SHIFT
;
165 Info
->Entry
[i
].PageSize
= RmpPageSize
;
166 Info
->Entry
[i
].Operation
= MemoryStateToGhcbOp (State
);
167 Info
->Entry
[i
].CurrentPage
= 0;
168 Info
->Header
.EndEntry
= (UINT16
)i
;
170 BaseAddress
= NextAddress
;
179 PageStateChangeVmgExit (
181 IN SNP_PAGE_STATE_CHANGE_INFO
*Info
187 // As per the GHCB specification, the hypervisor can resume the guest before
188 // processing all the entries. Checks whether all the entries are processed.
190 // The stragtegy here is to wait for the hypervisor to change the page
191 // state in the RMP table before guest access the memory pages. If the
192 // page state was not successful, then later memory access will result
195 while (Info
->Header
.CurrentEntry
<= Info
->Header
.EndEntry
) {
196 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
197 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
199 Status
= VmgExit (Ghcb
, SVM_EXIT_SNP_PAGE_STATE_CHANGE
, 0, 0);
202 // The Page State Change VMGEXIT can pass the failure through the
203 // ExitInfo2. Lets check both the return value as well as ExitInfo2.
205 if ((Status
!= 0) || (Ghcb
->SaveArea
.SwExitInfo2
)) {
206 SnpPageStateFailureTerminate ();
212 The function is used to set the page state when SEV-SNP is active. The page state
213 transition consist of changing the page ownership in the RMP table, and using the
214 PVALIDATE instruction to update the Validated bit in RMP table.
216 When the UseLargeEntry is set to TRUE, then function will try to use the large RMP
217 entry (whevever possible).
220 InternalSetPageState (
221 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
223 IN SEV_SNP_PAGE_STATE State
,
224 IN BOOLEAN UseLargeEntry
228 EFI_PHYSICAL_ADDRESS NextAddress
, EndAddress
;
229 MSR_SEV_ES_GHCB_REGISTER Msr
;
230 BOOLEAN InterruptState
;
231 SNP_PAGE_STATE_CHANGE_INFO
*Info
;
233 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
236 EndAddress
= BaseAddress
+ EFI_PAGES_TO_SIZE (NumPages
);
240 "%a:%a Address 0x%Lx - 0x%Lx State = %a LargeEntry = %d\n",
245 State
== SevSnpPageShared
? "Shared" : "Private",
249 while (BaseAddress
< EndAddress
) {
250 UINTN CurrentEntry
, EndEntry
;
253 // Initialize the GHCB
255 VmgInit (Ghcb
, &InterruptState
);
258 // Build the page state structure
260 Info
= (SNP_PAGE_STATE_CHANGE_INFO
*)Ghcb
->SharedBuffer
;
261 NextAddress
= BuildPageStateBuffer (
270 // Save the current and end entry from the page state structure. We need
273 CurrentEntry
= Info
->Header
.CurrentEntry
;
274 EndEntry
= Info
->Header
.EndEntry
;
277 // If the caller requested to change the page state to shared then
278 // invalidate the pages before making the page shared in the RMP table.
280 if (State
== SevSnpPageShared
) {
281 PvalidateRange (Info
, CurrentEntry
, EndEntry
, FALSE
);
285 // Invoke the page state change VMGEXIT.
287 PageStateChangeVmgExit (Ghcb
, Info
);
290 // If the caller requested to change the page state to private then
291 // validate the pages after it has been added in the RMP table.
293 if (State
== SevSnpPagePrivate
) {
294 PvalidateRange (Info
, CurrentEntry
, EndEntry
, TRUE
);
297 VmgDone (Ghcb
, InterruptState
);
299 BaseAddress
= NextAddress
;