/** @file
Support functions for UEFI protocol notification infrastructure.
Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "DxeMain.h"
#include "Handle.h"
#include "Event.h"
/**
Signal event for every protocol in protocol entry.
@param ProtEntry Protocol entry
**/
VOID
CoreNotifyProtocolEntry (
IN PROTOCOL_ENTRY *ProtEntry
)
{
PROTOCOL_NOTIFY *ProtNotify;
LIST_ENTRY *Link;
ASSERT_LOCKED (&gProtocolDatabaseLock);
for (Link = ProtEntry->Notify.ForwardLink; Link != &ProtEntry->Notify; Link = Link->ForwardLink) {
ProtNotify = CR (Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE);
CoreSignalEvent (ProtNotify->Event);
}
}
/**
Removes Protocol from the protocol list (but not the handle list).
@param Handle The handle to remove protocol on.
@param Protocol GUID of the protocol to be moved
@param Interface The interface of the protocol
@return Protocol Entry
**/
PROTOCOL_INTERFACE *
CoreRemoveInterfaceFromProtocol (
IN IHANDLE *Handle,
IN EFI_GUID *Protocol,
IN VOID *Interface
)
{
PROTOCOL_INTERFACE *Prot;
PROTOCOL_NOTIFY *ProtNotify;
PROTOCOL_ENTRY *ProtEntry;
LIST_ENTRY *Link;
ASSERT_LOCKED (&gProtocolDatabaseLock);
Prot = CoreFindProtocolInterface (Handle, Protocol, Interface);
if (Prot != NULL) {
ProtEntry = Prot->Protocol;
//
// If there's a protocol notify location pointing to this entry, back it up one
//
for (Link = ProtEntry->Notify.ForwardLink; Link != &ProtEntry->Notify; Link = Link->ForwardLink) {
ProtNotify = CR (Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE);
if (ProtNotify->Position == &Prot->ByProtocol) {
ProtNotify->Position = Prot->ByProtocol.BackLink;
}
}
//
// Remove the protocol interface entry
//
RemoveEntryList (&Prot->ByProtocol);
}
return Prot;
}
/**
Add a new protocol notification record for the request protocol.
@param Protocol The requested protocol to add the notify
registration
@param Event The event to signal
@param Registration Returns the registration record
@retval EFI_INVALID_PARAMETER Invalid parameter
@retval EFI_SUCCESS Successfully returned the registration record
that has been added
**/
EFI_STATUS
EFIAPI
CoreRegisterProtocolNotify (
IN EFI_GUID *Protocol,
IN EFI_EVENT Event,
OUT VOID **Registration
)
{
PROTOCOL_ENTRY *ProtEntry;
PROTOCOL_NOTIFY *ProtNotify;
EFI_STATUS Status;
if ((Protocol == NULL) || (Event == NULL) || (Registration == NULL)) {
return EFI_INVALID_PARAMETER;
}
CoreAcquireProtocolLock ();
ProtNotify = NULL;
//
// Get the protocol entry to add the notification too
//
ProtEntry = CoreFindProtocolEntry (Protocol, TRUE);
if (ProtEntry != NULL) {
//
// Allocate a new notification record
//
ProtNotify = AllocatePool (sizeof (PROTOCOL_NOTIFY));
if (ProtNotify != NULL) {
((IEVENT *)Event)->ExFlag |= EVT_EXFLAG_EVENT_PROTOCOL_NOTIFICATION;
ProtNotify->Signature = PROTOCOL_NOTIFY_SIGNATURE;
ProtNotify->Protocol = ProtEntry;
ProtNotify->Event = Event;
//
// start at the begining
//
ProtNotify->Position = &ProtEntry->Protocols;
InsertTailList (&ProtEntry->Notify, &ProtNotify->Link);
}
}
CoreReleaseProtocolLock ();
//
// Done. If we have a protocol notify entry, then return it.
// Otherwise, we must have run out of resources trying to add one
//
Status = EFI_OUT_OF_RESOURCES;
if (ProtNotify != NULL) {
*Registration = ProtNotify;
Status = EFI_SUCCESS;
}
return Status;
}
/**
Reinstall a protocol interface on a device handle. The OldInterface for Protocol is replaced by the NewInterface.
@param UserHandle Handle on which the interface is to be
reinstalled
@param Protocol The numeric ID of the interface
@param OldInterface A pointer to the old interface
@param NewInterface A pointer to the new interface
@retval EFI_SUCCESS The protocol interface was installed
@retval EFI_NOT_FOUND The OldInterface on the handle was not found
@retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
**/
EFI_STATUS
EFIAPI
CoreReinstallProtocolInterface (
IN EFI_HANDLE UserHandle,
IN EFI_GUID *Protocol,
IN VOID *OldInterface,
IN VOID *NewInterface
)
{
EFI_STATUS Status;
IHANDLE *Handle;
PROTOCOL_INTERFACE *Prot;
PROTOCOL_ENTRY *ProtEntry;
if (Protocol == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Lock the protocol database
//
CoreAcquireProtocolLock ();
Status = CoreValidateHandle (UserHandle);
if (EFI_ERROR (Status)) {
goto Done;
}
Handle = (IHANDLE *)UserHandle;
//
// Check that Protocol exists on UserHandle, and Interface matches the interface in the database
//
Prot = CoreFindProtocolInterface (UserHandle, Protocol, OldInterface);
if (Prot == NULL) {
Status = EFI_NOT_FOUND;
goto Done;
}
//
// Attempt to disconnect all drivers that are using the protocol interface that is about to be reinstalled
//
Status = CoreDisconnectControllersUsingProtocolInterface (
UserHandle,
Prot
);
if (EFI_ERROR (Status)) {
//
// One or more drivers refused to release, so return the error
//
goto Done;
}
//
// Remove the protocol interface from the protocol
//
Prot = CoreRemoveInterfaceFromProtocol (Handle, Protocol, OldInterface);
if (Prot == NULL) {
Status = EFI_NOT_FOUND;
goto Done;
}
ProtEntry = Prot->Protocol;
//
// Update the interface on the protocol
//
Prot->Interface = NewInterface;
//
// Add this protocol interface to the tail of the
// protocol entry
//
InsertTailList (&ProtEntry->Protocols, &Prot->ByProtocol);
//
// Update the Key to show that the handle has been created/modified
//
gHandleDatabaseKey++;
Handle->Key = gHandleDatabaseKey;
//
// Release the lock and connect all drivers to UserHandle
//
CoreReleaseProtocolLock ();
//
// Return code is ignored on purpose.
//
CoreConnectController (
UserHandle,
NULL,
NULL,
TRUE
);
CoreAcquireProtocolLock ();
//
// Notify the notification list for this protocol
//
CoreNotifyProtocolEntry (ProtEntry);
Status = EFI_SUCCESS;
Done:
CoreReleaseProtocolLock ();
return Status;
}