| /** @file | |
| Realm Aperture Management Protocol Dxe | |
| Copyright (c) 2022 - 2025, Arm Limited. All rights reserved. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| @par Glossary: | |
| - IPA - Intermediate Physical Address | |
| - RAMP - Realm Aperture Management Protocol | |
| - RIPAS - Realm IPA state | |
| - RSI - Realm Service Interface | |
| **/ | |
| #include <Base.h> | |
| #include <Library/ArmCcaLib.h> | |
| #include <Library/ArmCcaRsiLib.h> | |
| #include <Library/ArmMmuLib.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/DxeServicesTableLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <Protocol/RealmApertureManagementProtocol.h> | |
| #include <Protocol/ResetNotification.h> | |
| /** | |
| A macro defining the signature for the aperture information structure. | |
| */ | |
| #define APERTURE_INFO_SIG SIGNATURE_64 ('A', 'P', 'E', 'R', 'T', 'U', 'R', 'E') | |
| /** | |
| A structure describing the aperture. | |
| */ | |
| typedef struct { | |
| /// Signature for identifying this structure. | |
| UINT64 Signature; | |
| /// The linked list entry. | |
| LIST_ENTRY Link; | |
| /// The base address for the start of the aperture. | |
| EFI_PHYSICAL_ADDRESS BaseAddress; | |
| /// The number of pages covered by the aperture. | |
| UINTN Pages; | |
| /// The bit mask of attributes for the memory region. The | |
| /// bit mask of available attributes is defined in GetMemoryMap(). | |
| UINT64 MemoryAttributes; | |
| /// The RIPAS for the aperture. | |
| ARM_CCA_RIPAS Ripas; | |
| } APERTURE_INFO; | |
| /** | |
| List of the APERTURE_INFO structures that have been set up by OpenAperture() | |
| and not yet torn down by CloseAperture(). The list represents the full set | |
| of open apertures currently in effect. | |
| */ | |
| STATIC | |
| LIST_ENTRY mApertureInfos = INITIALIZE_LIST_HEAD_VARIABLE (mApertureInfos); | |
| /** Checks if an open aperture is overlapping the memory region. | |
| @param [in] MemStart Pointer to the page start address. | |
| @param [in] Pages Number of pages to share. | |
| @retval TRUE If memory region overlaps an open aperture. | |
| @retval FALSE Memory region does not overlap any open apertures. | |
| **/ | |
| STATIC | |
| BOOLEAN | |
| EFIAPI | |
| IsApertureOverlapping ( | |
| IN CONST EFI_PHYSICAL_ADDRESS MemStart, | |
| IN CONST UINTN Pages | |
| ) | |
| { | |
| LIST_ENTRY *Node; | |
| LIST_ENTRY *NextNode; | |
| APERTURE_INFO *ApertureInfo; | |
| EFI_PHYSICAL_ADDRESS MemEnd; | |
| EFI_PHYSICAL_ADDRESS ApertureStart; | |
| EFI_PHYSICAL_ADDRESS ApertureEnd; | |
| MemEnd = MemStart + EFI_PAGES_TO_SIZE (Pages) - 1; | |
| for ( | |
| Node = GetFirstNode (&mApertureInfos); | |
| Node != &mApertureInfos; | |
| Node = NextNode | |
| ) | |
| { | |
| NextNode = GetNextNode (&mApertureInfos, Node); | |
| ApertureInfo = CR (Node, APERTURE_INFO, Link, APERTURE_INFO_SIG); | |
| ApertureStart = ApertureInfo->BaseAddress; | |
| ApertureEnd = ApertureStart + EFI_PAGES_TO_SIZE (ApertureInfo->Pages) - 1; | |
| if (((ApertureStart >= MemStart) && (ApertureStart <= MemEnd)) || | |
| ((ApertureEnd >= MemStart) && (ApertureEnd <= MemEnd)) || | |
| ((MemStart >= ApertureStart) && (MemStart <= ApertureEnd)) || | |
| ((MemEnd >= ApertureStart) && (MemEnd <= ApertureEnd))) | |
| { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** Enables sharing of the memory buffers with the host. | |
| @param [in] Memory Pointer to the page start address. | |
| @param [in] Pages Number of pages to share. | |
| @param [out] ApertureReference Reference to the opened aperture. | |
| @retval EFI_SUCCESS Success. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_OUT_OF_RESOURCES Memory allocation failed. | |
| @retval EFI_ACCESS_DENIED Aperture already open over memory region. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| RampOpenAperture ( | |
| IN CONST EFI_PHYSICAL_ADDRESS Memory, | |
| IN CONST UINTN Pages, | |
| OUT EFI_HANDLE *CONST ApertureReference | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_STATUS Status1; | |
| APERTURE_INFO *ApertInfo; | |
| EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; | |
| EFI_PHYSICAL_ADDRESS MemRangeAddr; | |
| UINTN Index; | |
| UINT64 Offset; | |
| if ((Memory == 0) || | |
| (Pages == 0) || | |
| (ApertureReference == NULL) || | |
| ((Memory & (EFI_PAGE_SIZE - 1)) != 0)) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // The pages size must be aligned to the Realm Granule size. | |
| STATIC_ASSERT ( | |
| (EFI_PAGE_SIZE & (ARM_CCA_REALM_GRANULE_SIZE - 1)) == 0, | |
| "(EFI_PAGE_SIZE & (ARM_CCA_REALM_GRANULE_SIZE - 1)) == 0" | |
| ); | |
| // Checks if we already have an open aperture that overlaps the | |
| // memory region. If so return the request as invalid. | |
| if (IsApertureOverlapping (Memory, Pages)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| MemRangeAddr = Memory; | |
| Index = 0; | |
| while (Index < Pages) { | |
| Status = gDS->GetMemorySpaceDescriptor (MemRangeAddr, &GcdDescriptor); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // We currently do not have a usecase where we would want to open apertures | |
| // for runtime services memory | |
| if ((GcdDescriptor.Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) { | |
| ASSERT (0); | |
| return EFI_UNSUPPORTED; | |
| } | |
| // The MemRangeAddr could reside in middle of a descriptor, so | |
| // compute the offset from the start and use that to advance | |
| // the Index and MemRangeAddr. | |
| Offset = MemRangeAddr - GcdDescriptor.BaseAddress; | |
| Index += EFI_SIZE_TO_PAGES (GcdDescriptor.Length - Offset); | |
| MemRangeAddr += (GcdDescriptor.Length - Offset); | |
| } // while | |
| Status = ArmCcaSetMemoryProtectionAttribute ( | |
| Memory, | |
| EFI_PAGES_TO_SIZE (Pages), | |
| TRUE | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: Failed to update page tables for Protected EMPTY page mapping, " | |
| "Address = %p, Pages = 0x%lx, Status = %r\n", | |
| Memory, | |
| Pages, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| // Allocate a APERTURE_INFO structure to remember the apertures opened. | |
| ApertInfo = AllocateZeroPool (sizeof (APERTURE_INFO)); | |
| if (ApertInfo == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto error_handler1; | |
| } | |
| InitializeListHead (&ApertInfo->Link); | |
| ApertInfo->Signature = APERTURE_INFO_SIG; | |
| ApertInfo->BaseAddress = Memory; | |
| ApertInfo->Pages = Pages; | |
| ApertInfo->MemoryAttributes = GcdDescriptor.Attributes; | |
| ApertInfo->Ripas = RipasEmpty; | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "%a: ApertRef = 0x%p, Memory = 0x%lx, Pages = 0x%x, " | |
| "MemoryAttributes = 0x%x, Ripas = 0x%x\n", | |
| __func__, | |
| ApertInfo, | |
| ApertInfo->BaseAddress, | |
| ApertInfo->Pages, | |
| ApertInfo->MemoryAttributes, | |
| ApertInfo->Ripas | |
| )); | |
| // Set the Realm IPA state to Empty to open the Aperture | |
| Status = ArmCcaRsiSetIpaState ( | |
| (UINT64 *)Memory, | |
| (Pages * EFI_PAGE_SIZE), | |
| RipasEmpty, | |
| ARM_CCA_RIPAS_CHANGE_FLAGS_RSI_CHANGE_DESTROYED | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: RSI Set IPA State failed, Address = %p, Pages = 0x%lx, " | |
| "Status = %r\n", | |
| Memory, | |
| Pages, | |
| Status | |
| )); | |
| goto error_handler; | |
| } | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "SUCCESS: RSI Set IPA State complete, Address = %p, Pages = 0x%lx, " | |
| "Status = %r\n", | |
| Memory, | |
| Pages, | |
| Status | |
| )); | |
| InsertHeadList (&mApertureInfos, &ApertInfo->Link); | |
| *ApertureReference = (EFI_HANDLE *)&ApertInfo->Link; | |
| return Status; | |
| error_handler: | |
| FreePool (ApertInfo); | |
| error_handler1: | |
| Status1 = ArmCcaSetMemoryProtectionAttribute ( | |
| Memory, | |
| EFI_PAGES_TO_SIZE (Pages), | |
| FALSE | |
| ); | |
| if (RETURN_ERROR (Status1)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: Failed to update page tables to Protected page mapping, " | |
| "Address = %p, Pages = 0x%lx, Status = %r\n", | |
| Memory, | |
| Pages, | |
| Status1 | |
| )); | |
| } | |
| *ApertureReference = NULL; | |
| // return the first error code | |
| return Status; | |
| } | |
| /** Disables the sharing of the buffers. | |
| @param [in] ApertureReference Reference to the aperture for closing. | |
| @param [in] MemoryMapLocked The function is executing on the stack of | |
| gBS->ExitBootServices(); changes to the UEFI | |
| memory map are forbidden. | |
| @retval EFI_SUCCESS The operation completed successfully. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_NOT_FOUND The required buffer information is not found. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| RampCloseAperture ( | |
| IN CONST EFI_HANDLE ApertureReference, | |
| IN BOOLEAN MemoryMapLocked | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| APERTURE_INFO *ApertInfo; | |
| if (ApertureReference == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| ApertInfo = NULL; | |
| ApertInfo = CR (ApertureReference, APERTURE_INFO, Link, APERTURE_INFO_SIG); | |
| if (ApertInfo == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| if (!IsNodeInList (&mApertureInfos, (LIST_ENTRY *)ApertureReference)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "%a: ApertRef = 0x%p, Memory = 0x%lx, Pages = 0x%x, " | |
| "MemoryAttributes = 0x%x, Ripas = 0x%x\n", | |
| __func__, | |
| ApertInfo, | |
| ApertInfo->BaseAddress, | |
| ApertInfo->Pages, | |
| ApertInfo->MemoryAttributes, | |
| ApertInfo->Ripas | |
| )); | |
| // Set the Realm IPA state to RAM to close the Aperture | |
| Status = ArmCcaRsiSetIpaState ( | |
| (UINT64 *)ApertInfo->BaseAddress, | |
| (ApertInfo->Pages * EFI_PAGE_SIZE), | |
| RipasRam, | |
| ARM_CCA_RIPAS_CHANGE_FLAGS_RSI_CHANGE_DESTROYED | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: RSI Set IPA State failed, Address = %p, Pages = 0x%lx, " | |
| "Status = %r\n", | |
| ApertInfo->BaseAddress, | |
| ApertInfo->Pages, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| if (!MemoryMapLocked) { | |
| Status = ArmCcaSetMemoryProtectionAttribute ( | |
| ApertInfo->BaseAddress, | |
| EFI_PAGES_TO_SIZE (ApertInfo->Pages), | |
| FALSE | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: Failed to update page tables for Protected RAM page mapping," | |
| "Address = %p, Pages = 0x%lx, Status = %r\n", | |
| ApertInfo->BaseAddress, | |
| ApertInfo->Pages, | |
| Status | |
| )); | |
| } | |
| } | |
| RemoveEntryList (&ApertInfo->Link); | |
| if (!MemoryMapLocked) { | |
| ZeroMem (ApertInfo, sizeof (APERTURE_INFO)); | |
| FreePool (ApertInfo); | |
| } else { | |
| // Changes to the UEFI memory map are forbidden. | |
| // So just zero out the memory. | |
| ZeroMem (ApertInfo, sizeof (APERTURE_INFO)); | |
| } | |
| return Status; | |
| } | |
| /** Closes all open apertures. | |
| @param [in] MemoryMapLocked The function is executing on the stack of | |
| gBS->ExitBootServices(); changes to the UEFI | |
| memory map are forbidden. | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| RampCloseAllApertures ( | |
| IN BOOLEAN MemoryMapLocked | |
| ) | |
| { | |
| LIST_ENTRY *Node; | |
| LIST_ENTRY *NextNode; | |
| APERTURE_INFO *ApertureInfo; | |
| // All drivers that had opened the apertures have halted their respective | |
| // controllers by now; close all the apertures. | |
| for ( | |
| Node = GetFirstNode (&mApertureInfos); | |
| Node != &mApertureInfos; | |
| Node = NextNode | |
| ) | |
| { | |
| NextNode = GetNextNode (&mApertureInfos, Node); | |
| ApertureInfo = CR (Node, APERTURE_INFO, Link, APERTURE_INFO_SIG); | |
| RampCloseAperture (&ApertureInfo->Link, MemoryMapLocked); | |
| } | |
| } | |
| /** | |
| Notification function that is queued after the notification functions of all | |
| events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. | |
| This function invokes the closing of all open apertures. | |
| @param[in] Event Event whose notification function is being invoked. Event | |
| is permitted to request the queueing of this function | |
| only at TPL_CALLBACK task priority level. | |
| @param[in] Context Ignored. | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| OnRampExitBootServicesEvent ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| RampCloseAllApertures (TRUE); | |
| } | |
| /** | |
| Notification function that is queued when gBS->ExitBootServices() signals the | |
| EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another | |
| event, received as Context, and returns. | |
| Signaling an event in this context is safe. The UEFI spec allows | |
| gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not | |
| listed, hence memory is not allocated. | |
| @param[in] Event Event whose notification function is being invoked. | |
| Event is permitted to request the queueing of this | |
| function at TPL_CALLBACK or TPL_NOTIFY task | |
| priority level. | |
| @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal | |
| is permitted to request the queueing of its | |
| notification function only at TPL_CALLBACK level. | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| RampExitBootServices ( | |
| IN EFI_EVENT Event, | |
| IN VOID *EventToSignal | |
| ) | |
| { | |
| // (1) The NotifyFunctions of all the events in | |
| // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before | |
| // RampExitBootServices() is entered. | |
| // | |
| // (2) RampExitBootServices() is executing minimally at TPL_CALLBACK. | |
| // | |
| // (3) RampExitBootServices() has been queued in unspecified order relative | |
| // to the NotifyFunctions of all the other events in | |
| // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as | |
| // Event's. | |
| // | |
| // Consequences: | |
| // | |
| // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions | |
| // queued at TPL_CALLBACK may be invoked after RampExitBootServices() | |
| // returns. | |
| // | |
| // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions | |
| // queued at TPL_NOTIFY may be invoked after RampExitBootServices() | |
| // returns; plus *all* NotifyFunctions queued at TPL_CALLBACK will be | |
| // invoked strictly after all NotifyFunctions queued at TPL_NOTIFY, | |
| // including RampExitBootServices(), have been invoked. | |
| // | |
| // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we | |
| // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all* | |
| // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES. | |
| gBS->SignalEvent (EventToSignal); | |
| } | |
| /** A structure describing the Realm Aperture Management protocol. | |
| */ | |
| STATIC | |
| CONST | |
| EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL Ramp = { | |
| EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_REVISION, | |
| RampOpenAperture, | |
| RampCloseAperture | |
| }; | |
| /** | |
| This routine is called to close all apertures before system reset. | |
| @param[in] ResetType The type of reset to perform. | |
| @param[in] ResetStatus The status code for the reset. | |
| @param[in] DataSize The size, in bytes, of ResetData. | |
| @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or | |
| EfiResetShutdown the data buffer starts with a Null- | |
| terminated string, optionally followed by additional | |
| binary data. The string is a description that the | |
| caller may use to further indicate the reason for | |
| the system reset. ResetData is only valid if | |
| ResetStatus is something other than EFI_SUCCESS | |
| unless the ResetType is EfiResetPlatformSpecific | |
| where a minimum amount of ResetData is always | |
| required. | |
| For a ResetType of EfiResetPlatformSpecific the data | |
| buffer also starts with a Null-terminated string | |
| that is followed by an EFI_GUID that describes the | |
| specific type of reset to perform. | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| OnResetEvent ( | |
| IN EFI_RESET_TYPE ResetType, | |
| IN EFI_STATUS ResetStatus, | |
| IN UINTN DataSize, | |
| IN VOID *ResetData OPTIONAL | |
| ) | |
| { | |
| RampCloseAllApertures (FALSE); | |
| } | |
| /** | |
| Hook the system reset to close all apertures. | |
| @param[in] Event Event whose notification function is being invoked | |
| @param[in] Context Pointer to the notification function's context | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| OnResetNotificationInstall ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_RESET_NOTIFICATION_PROTOCOL *ResetNotify; | |
| Status = gBS->LocateProtocol ( | |
| &gEfiResetNotificationProtocolGuid, | |
| NULL, | |
| (VOID **)&ResetNotify | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Status = ResetNotify->RegisterResetNotify (ResetNotify, OnResetEvent); | |
| ASSERT_EFI_ERROR (Status); | |
| DEBUG ((DEBUG_VERBOSE, "RAMP: Hook system reset to close all apertures.\n")); | |
| gBS->CloseEvent (Event); | |
| } | |
| } | |
| /** Entry point for Realm Aperture Management Protocol Dxe | |
| @param [in] ImageHandle Handle for this image. | |
| @param [in] SystemTable Pointer to the EFI system table. | |
| @retval EFI_SUCCESS When executing in a Realm the RAMP was | |
| installed successfully. | |
| @retval EFI_REQUEST_UNLOAD_IMAGE When execution context is not a Realm, this | |
| function installs the RAMP absent protocol | |
| to allow other modules to continue and | |
| returns EFI_REQUEST_UNLOAD_IMAGE to unload | |
| the image. | |
| @retval EFI_OUT_OF_RESOURCES There was not enough memory to install the | |
| protocols. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RealmApertureManagementProtocolDxeInitialize ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE Handle; | |
| EFI_EVENT CloseAllAperturesEvent; | |
| EFI_EVENT ExitBootEvent; | |
| VOID *Registration; | |
| // When the execution context is not a Realm, install the Realm Aperture | |
| // Management Absent protocol and return EFI_REQUEST_UNLOAD_IMAGE so the | |
| // image can be unloaded. | |
| if (!ArmCcaIsRealm ()) { | |
| Handle = NULL; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &Handle, | |
| &gEfiRealmApertureManagementAbsentProtocolGuid, | |
| NULL, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| return EFI_REQUEST_UNLOAD_IMAGE; | |
| } | |
| /* | |
| Create the "late" event whose notification function will close all | |
| apertures. | |
| */ | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, // Type | |
| TPL_CALLBACK, // NotifyTpl | |
| OnRampExitBootServicesEvent, // NotifyFunction | |
| NULL, // NotifyContext | |
| &CloseAllAperturesEvent // Event | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| /* | |
| Create the event whose notification function will be queued by | |
| gBS->ExitBootServices() and will signal the event created above. | |
| */ | |
| Status = gBS->CreateEvent ( | |
| EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type | |
| TPL_CALLBACK, // NotifyTpl | |
| RampExitBootServices, // NotifyFunction | |
| CloseAllAperturesEvent, // NotifyContext | |
| &ExitBootEvent // Event | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto error_handler1; | |
| } | |
| Handle = NULL; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &Handle, | |
| &gEfiRealmApertureManagementProtocolGuid, | |
| &Ramp, | |
| NULL | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // RAMP Protocol installed successfully | |
| // Hook the system reset to close all apertures. | |
| EfiCreateProtocolNotifyEvent ( | |
| &gEfiResetNotificationProtocolGuid, | |
| TPL_CALLBACK, | |
| OnResetNotificationInstall, | |
| NULL, | |
| &Registration | |
| ); | |
| return Status; | |
| } | |
| // cleanup on error | |
| gBS->CloseEvent (ExitBootEvent); | |
| error_handler1: | |
| gBS->CloseEvent (CloseAllAperturesEvent); | |
| return Status; | |
| } |