]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariablePolicySmmDxe.c
1 /** @file -- VariablePolicySmmDxe.c
2 This protocol allows communication with Variable Policy Engine.
3
4 Copyright (c) Microsoft Corporation.
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include <Library/BaseLib.h>
10 #include <Library/UefiLib.h>
11 #include <Library/DebugLib.h>
12 #include <Library/SafeIntLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/MemoryAllocationLib.h>
16
17 #include <Protocol/VariablePolicy.h>
18 #include <Protocol/MmCommunication2.h>
19
20 #include <Guid/VarCheckPolicyMmi.h>
21
22 #include "Variable.h"
23
24 EDKII_VARIABLE_POLICY_PROTOCOL mVariablePolicyProtocol;
25 EFI_MM_COMMUNICATION2_PROTOCOL *mMmCommunication;
26
27 VOID *mMmCommunicationBuffer;
28 UINTN mMmCommunicationBufferSize;
29 EFI_LOCK mMmCommunicationLock;
30
31 /**
32 Internal helper function to consolidate communication method.
33
34 @param[in,out] CommBuffer
35 @param[in,out] CommSize Size of the CommBuffer.
36
37 @retval EFI_STATUS Result from communication method.
38
39 **/
40 STATIC
41 EFI_STATUS
42 InternalMmCommunicate (
43 IN OUT VOID *CommBuffer,
44 IN OUT UINTN *CommSize
45 )
46 {
47 EFI_STATUS Status;
48
49 if ((CommBuffer == NULL) || (CommSize == NULL)) {
50 return EFI_INVALID_PARAMETER;
51 }
52
53 Status = mMmCommunication->Communicate (mMmCommunication, CommBuffer, CommBuffer, CommSize);
54 return Status;
55 }
56
57 /**
58 This API function disables the variable policy enforcement. If it's
59 already been called once, will return EFI_ALREADY_STARTED.
60
61 @retval EFI_SUCCESS
62 @retval EFI_ALREADY_STARTED Has already been called once this boot.
63 @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
64 @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
65
66 **/
67 STATIC
68 EFI_STATUS
69 EFIAPI
70 ProtocolDisableVariablePolicy (
71 VOID
72 )
73 {
74 EFI_STATUS Status;
75 EFI_MM_COMMUNICATE_HEADER *CommHeader;
76 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
77 UINTN BufferSize;
78
79 // Check the PCD for convenience.
80 // This would also be rejected by the lib, but why go to MM if we don't have to?
81 if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {
82 return EFI_WRITE_PROTECTED;
83 }
84
85 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
86
87 // Set up the MM communication.
88 BufferSize = mMmCommunicationBufferSize;
89 CommHeader = mMmCommunicationBuffer;
90 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER *)&CommHeader->Data;
91 CopyGuid (&CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid);
92 CommHeader->MessageLength = BufferSize - OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data);
93 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
94 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
95 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_DISABLE;
96
97 Status = InternalMmCommunicate (CommHeader, &BufferSize);
98 DEBUG ((DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status));
99
100 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
101
102 return (EFI_ERROR (Status)) ? Status : PolicyHeader->Result;
103 }
104
105 /**
106 This API function returns whether or not the policy engine is
107 currently being enforced.
108
109 @param[out] State Pointer to a return value for whether the policy enforcement
110 is currently enabled.
111
112 @retval EFI_SUCCESS
113 @retval Others An error has prevented this command from completing.
114
115 **/
116 STATIC
117 EFI_STATUS
118 EFIAPI
119 ProtocolIsVariablePolicyEnabled (
120 OUT BOOLEAN *State
121 )
122 {
123 EFI_STATUS Status;
124 EFI_MM_COMMUNICATE_HEADER *CommHeader;
125 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
126 VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *CommandParams;
127 UINTN BufferSize;
128
129 if (State == NULL) {
130 return EFI_INVALID_PARAMETER;
131 }
132
133 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
134
135 // Set up the MM communication.
136 BufferSize = mMmCommunicationBufferSize;
137 CommHeader = mMmCommunicationBuffer;
138 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER *)&CommHeader->Data;
139 CommandParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *)(PolicyHeader + 1);
140 CopyGuid (&CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid);
141 CommHeader->MessageLength = BufferSize - OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data);
142 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
143 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
144 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_IS_ENABLED;
145
146 Status = InternalMmCommunicate (CommHeader, &BufferSize);
147 DEBUG ((DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status));
148
149 if (!EFI_ERROR (Status)) {
150 Status = PolicyHeader->Result;
151 *State = CommandParams->State;
152 }
153
154 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
155
156 return Status;
157 }
158
159 /**
160 This API function validates and registers a new policy with
161 the policy enforcement engine.
162
163 @param[in] NewPolicy Pointer to the incoming policy structure.
164
165 @retval EFI_SUCCESS
166 @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
167 @retval EFI_ALREADY_STARTED An identical matching policy already exists.
168 @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
169 @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
170 @retval EFI_ABORTED A calculation error has prevented this function from completing.
171 @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
172
173 **/
174 STATIC
175 EFI_STATUS
176 EFIAPI
177 ProtocolRegisterVariablePolicy (
178 IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
179 )
180 {
181 EFI_STATUS Status;
182 EFI_MM_COMMUNICATE_HEADER *CommHeader;
183 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
184 VOID *PolicyBuffer;
185 UINTN BufferSize;
186 UINTN RequiredSize;
187
188 if (NewPolicy == NULL) {
189 return EFI_INVALID_PARAMETER;
190 }
191
192 // First, make sure that the required size does not exceed the capabilities
193 // of the MmCommunication buffer.
194 RequiredSize = OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data) + sizeof (VAR_CHECK_POLICY_COMM_HEADER);
195 Status = SafeUintnAdd (RequiredSize, NewPolicy->Size, &RequiredSize);
196 if (EFI_ERROR (Status) || (RequiredSize > mMmCommunicationBufferSize)) {
197 DEBUG ((
198 DEBUG_ERROR,
199 "%a - Policy too large for buffer! %r, %d > %d \n",
200 __FUNCTION__,
201 Status,
202 RequiredSize,
203 mMmCommunicationBufferSize
204 ));
205 return EFI_OUT_OF_RESOURCES;
206 }
207
208 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
209
210 // Set up the MM communication.
211 BufferSize = mMmCommunicationBufferSize;
212 CommHeader = mMmCommunicationBuffer;
213 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER *)&CommHeader->Data;
214 PolicyBuffer = (VOID *)(PolicyHeader + 1);
215 CopyGuid (&CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid);
216 CommHeader->MessageLength = BufferSize - OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data);
217 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
218 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
219 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_REGISTER;
220
221 // Copy the policy into place. This copy is safe because we've already tested above.
222 CopyMem (PolicyBuffer, NewPolicy, NewPolicy->Size);
223
224 Status = InternalMmCommunicate (CommHeader, &BufferSize);
225 DEBUG ((DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status));
226
227 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
228
229 return (EFI_ERROR (Status)) ? Status : PolicyHeader->Result;
230 }
231
232 /**
233 This helper function takes care of the overhead of formatting, sending, and interpreting
234 the results for a single DumpVariablePolicy request.
235
236 @param[in] PageRequested The page of the paginated results from MM. 0 for metadata.
237 @param[out] TotalSize The total size of the entire buffer. Returned as part of metadata.
238 @param[out] PageSize The size of the current page being returned. Not valid as part of metadata.
239 @param[out] HasMore A flag indicating whether there are more pages after this one.
240 @param[out] Buffer The start of the current page from MM.
241
242 @retval EFI_SUCCESS Output params have been updated (either metadata or dump page).
243 @retval EFI_INVALID_PARAMETER One of the output params is NULL.
244 @retval Others Response from MM handler.
245
246 **/
247 STATIC
248 EFI_STATUS
249 DumpVariablePolicyHelper (
250 IN UINT32 PageRequested,
251 OUT UINT32 *TotalSize,
252 OUT UINT32 *PageSize,
253 OUT BOOLEAN *HasMore,
254 OUT UINT8 **Buffer
255 )
256 {
257 EFI_STATUS Status;
258 EFI_MM_COMMUNICATE_HEADER *CommHeader;
259 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
260 VAR_CHECK_POLICY_COMM_DUMP_PARAMS *CommandParams;
261 UINTN BufferSize;
262
263 if ((TotalSize == NULL) || (PageSize == NULL) || (HasMore == NULL) || (Buffer == NULL)) {
264 return EFI_INVALID_PARAMETER;
265 }
266
267 // Set up the MM communication.
268 BufferSize = mMmCommunicationBufferSize;
269 CommHeader = mMmCommunicationBuffer;
270 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER *)&CommHeader->Data;
271 CommandParams = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS *)(PolicyHeader + 1);
272 CopyGuid (&CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid);
273 CommHeader->MessageLength = BufferSize - OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data);
274 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
275 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
276 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_DUMP;
277
278 CommandParams->PageRequested = PageRequested;
279
280 Status = InternalMmCommunicate (CommHeader, &BufferSize);
281 DEBUG ((DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status));
282
283 if (!EFI_ERROR (Status)) {
284 Status = PolicyHeader->Result;
285 *TotalSize = CommandParams->TotalSize;
286 *PageSize = CommandParams->PageSize;
287 *HasMore = CommandParams->HasMore;
288 *Buffer = (UINT8 *)(CommandParams + 1);
289 }
290
291 return Status;
292 }
293
294 /**
295 This API function will dump the entire contents of the variable policy table.
296
297 Similar to GetVariable, the first call can be made with a 0 size and it will return
298 the size of the buffer required to hold the entire table.
299
300 @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
301 @param[in,out] Size On input, the size of the output buffer. On output, the size
302 of the data returned.
303
304 @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
305 @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
306 @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
307
308 **/
309 STATIC
310 EFI_STATUS
311 EFIAPI
312 ProtocolDumpVariablePolicy (
313 OUT UINT8 *Policy OPTIONAL,
314 IN OUT UINT32 *Size
315 )
316 {
317 EFI_STATUS Status;
318 UINT8 *Source;
319 UINT8 *Destination;
320 UINT32 PolicySize;
321 UINT32 PageSize;
322 BOOLEAN HasMore;
323 UINT32 PageIndex;
324
325 if ((Size == NULL) || ((*Size > 0) && (Policy == NULL))) {
326 return EFI_INVALID_PARAMETER;
327 }
328
329 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
330
331 // Repeat this whole process until we either have a failure case or get the entire buffer.
332 do {
333 // First, we must check the zero page to determine the buffer size and
334 // reset the internal state.
335 PolicySize = 0;
336 PageSize = 0;
337 HasMore = FALSE;
338 Status = DumpVariablePolicyHelper (0, &PolicySize, &PageSize, &HasMore, &Source);
339 if (EFI_ERROR (Status)) {
340 break;
341 }
342
343 // If we're good, we can at least check the required size now.
344 if (*Size < PolicySize) {
345 *Size = PolicySize;
346 Status = EFI_BUFFER_TOO_SMALL;
347 break;
348 }
349
350 // On further thought, let's update the size either way.
351 *Size = PolicySize;
352 // And get ready to ROCK.
353 Destination = Policy;
354
355 // Keep looping and copying until we're either done or freak out.
356 for (PageIndex = 1; !EFI_ERROR (Status) && HasMore && PageIndex < MAX_UINT32; PageIndex++) {
357 Status = DumpVariablePolicyHelper (PageIndex, &PolicySize, &PageSize, &HasMore, &Source);
358 if (!EFI_ERROR (Status)) {
359 CopyMem (Destination, Source, PageSize);
360 Destination += PageSize;
361 }
362 }
363
364 // Next, we check to see whether
365 } while (Status == EFI_TIMEOUT);
366
367 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
368
369 // There's currently no use for this, but it shouldn't be hard to implement.
370 return Status;
371 }
372
373 /**
374 This API function locks the interface so that no more policy updates
375 can be performed or changes made to the enforcement until the next boot.
376
377 @retval EFI_SUCCESS
378 @retval Others An error has prevented this command from completing.
379
380 **/
381 STATIC
382 EFI_STATUS
383 EFIAPI
384 ProtocolLockVariablePolicy (
385 VOID
386 )
387 {
388 EFI_STATUS Status;
389 EFI_MM_COMMUNICATE_HEADER *CommHeader;
390 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
391 UINTN BufferSize;
392
393 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
394
395 // Set up the MM communication.
396 BufferSize = mMmCommunicationBufferSize;
397 CommHeader = mMmCommunicationBuffer;
398 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER *)&CommHeader->Data;
399 CopyGuid (&CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid);
400 CommHeader->MessageLength = BufferSize - OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data);
401 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
402 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
403 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_LOCK;
404
405 Status = InternalMmCommunicate (CommHeader, &BufferSize);
406 DEBUG ((DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status));
407
408 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
409
410 return (EFI_ERROR (Status)) ? Status : PolicyHeader->Result;
411 }
412
413 /**
414 This helper function locates the shared comm buffer and assigns it to input pointers.
415
416 @param[in,out] BufferSize On input, the minimum buffer size required INCLUDING the MM communicate header.
417 On output, the size of the matching buffer found.
418 @param[out] LocatedBuffer A pointer to the matching buffer.
419
420 @retval EFI_SUCCESS
421 @retval EFI_INVALID_PARAMETER One of the output pointers was NULL.
422 @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate a comm buffer.
423
424 **/
425 STATIC
426 EFI_STATUS
427 InitMmCommonCommBuffer (
428 IN OUT UINTN *BufferSize,
429 OUT VOID **LocatedBuffer
430 )
431 {
432 EFI_STATUS Status;
433
434 Status = EFI_SUCCESS;
435
436 // Make sure that we're working with good pointers.
437 if ((BufferSize == NULL) || (LocatedBuffer == NULL)) {
438 return EFI_INVALID_PARAMETER;
439 }
440
441 // Allocate the runtime memory for the comm buffer.
442 *LocatedBuffer = AllocateRuntimePool (*BufferSize);
443 if (*LocatedBuffer == NULL) {
444 Status = EFI_OUT_OF_RESOURCES;
445 *BufferSize = 0;
446 }
447
448 EfiInitializeLock (&mMmCommunicationLock, TPL_NOTIFY);
449
450 return Status;
451 }
452
453 /**
454 Convert internal pointer addresses to virtual addresses.
455
456 @param[in] Event Event whose notification function is being invoked.
457 @param[in] Context The pointer to the notification function's context, which
458 is implementation-dependent.
459 **/
460 STATIC
461 VOID
462 EFIAPI
463 VariablePolicyVirtualAddressCallback (
464 IN EFI_EVENT Event,
465 IN VOID *Context
466 )
467 {
468 EfiConvertPointer (0, (VOID **)&mMmCommunication);
469 EfiConvertPointer (0, (VOID **)&mMmCommunicationBuffer);
470 }
471
472 /**
473 The driver's entry point.
474
475 @param[in] ImageHandle The firmware allocated handle for the EFI image.
476 @param[in] SystemTable A pointer to the EFI System Table.
477
478 @retval EFI_SUCCESS The entry point executed successfully.
479 @retval other Some error occured when executing this entry point.
480
481 **/
482 EFI_STATUS
483 EFIAPI
484 VariablePolicySmmDxeMain (
485 IN EFI_HANDLE ImageHandle,
486 IN EFI_SYSTEM_TABLE *SystemTable
487 )
488 {
489 EFI_STATUS Status;
490 BOOLEAN ProtocolInstalled;
491 BOOLEAN VirtualAddressChangeRegistered;
492 EFI_EVENT VirtualAddressChangeEvent;
493
494 Status = EFI_SUCCESS;
495 ProtocolInstalled = FALSE;
496 VirtualAddressChangeRegistered = FALSE;
497
498 // Update the minimum buffer size.
499 mMmCommunicationBufferSize = VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE;
500 // Locate the shared comm buffer to use for sending MM commands.
501 Status = InitMmCommonCommBuffer (&mMmCommunicationBufferSize, &mMmCommunicationBuffer);
502 if (EFI_ERROR (Status)) {
503 DEBUG ((DEBUG_ERROR, "%a - Failed to locate a viable MM comm buffer! %r\n", __FUNCTION__, Status));
504 ASSERT_EFI_ERROR (Status);
505 return Status;
506 }
507
508 // Locate the MmCommunication protocol.
509 Status = gBS->LocateProtocol (&gEfiMmCommunication2ProtocolGuid, NULL, (VOID **)&mMmCommunication);
510 if (EFI_ERROR (Status)) {
511 DEBUG ((DEBUG_ERROR, "%a - Failed to locate MmCommunication protocol! %r\n", __FUNCTION__, Status));
512 ASSERT_EFI_ERROR (Status);
513 return Status;
514 }
515
516 // Configure the VariablePolicy protocol structure.
517 mVariablePolicyProtocol.Revision = EDKII_VARIABLE_POLICY_PROTOCOL_REVISION;
518 mVariablePolicyProtocol.DisableVariablePolicy = ProtocolDisableVariablePolicy;
519 mVariablePolicyProtocol.IsVariablePolicyEnabled = ProtocolIsVariablePolicyEnabled;
520 mVariablePolicyProtocol.RegisterVariablePolicy = ProtocolRegisterVariablePolicy;
521 mVariablePolicyProtocol.DumpVariablePolicy = ProtocolDumpVariablePolicy;
522 mVariablePolicyProtocol.LockVariablePolicy = ProtocolLockVariablePolicy;
523
524 // Register all the protocols and return the status.
525 Status = gBS->InstallMultipleProtocolInterfaces (
526 &ImageHandle,
527 &gEdkiiVariablePolicyProtocolGuid,
528 &mVariablePolicyProtocol,
529 NULL
530 );
531 if (EFI_ERROR (Status)) {
532 DEBUG ((DEBUG_ERROR, "%a - Failed to install protocol! %r\n", __FUNCTION__, Status));
533 goto Exit;
534 } else {
535 ProtocolInstalled = TRUE;
536 }
537
538 // Normally, we might want to register a callback
539 // to lock the interface, but this is integrated
540 // into the existing callbacks in VaraiableSmm.c
541 // and VariableDxe.c.
542
543 //
544 // Register a VirtualAddressChange callback for the MmComm protocol and Comm buffer.
545 Status = gBS->CreateEventEx (
546 EVT_NOTIFY_SIGNAL,
547 TPL_NOTIFY,
548 VariablePolicyVirtualAddressCallback,
549 NULL,
550 &gEfiEventVirtualAddressChangeGuid,
551 &VirtualAddressChangeEvent
552 );
553 if (EFI_ERROR (Status)) {
554 DEBUG ((DEBUG_ERROR, "%a - Failed to create VirtualAddressChange event! %r\n", __FUNCTION__, Status));
555 goto Exit;
556 } else {
557 VirtualAddressChangeRegistered = TRUE;
558 }
559
560 Exit:
561 //
562 // If we're about to return a failed status (and unload this driver), we must first undo anything that
563 // has been successfully done.
564 if (EFI_ERROR (Status)) {
565 if (ProtocolInstalled) {
566 gBS->UninstallProtocolInterface (&ImageHandle, &gEdkiiVariablePolicyProtocolGuid, &mVariablePolicyProtocol);
567 }
568
569 if (VirtualAddressChangeRegistered) {
570 gBS->CloseEvent (VirtualAddressChangeEvent);
571 }
572 }
573
574 return Status;
575 }