+++ /dev/null
-/** @file\r
-\r
-Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
-SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-Module Name:\r
-\r
- Dpc.c\r
-\r
-Abstract:\r
-\r
-\r
-**/\r
-\r
-#include "Dpc.h"\r
-\r
-//\r
-// Handle for the EFI_DPC_PROTOCOL instance\r
-//\r
-EFI_HANDLE mDpcHandle = NULL;\r
-\r
-//\r
-// The EFI_DPC_PROTOCOL instances that is installed onto mDpcHandle\r
-//\r
-EFI_DPC_PROTOCOL mDpc = {\r
- DpcQueueDpc,\r
- DpcDispatchDpc\r
-};\r
-\r
-//\r
-// Global variables used to meaasure the DPC Queue Depths\r
-//\r
-UINTN mDpcQueueDepth = 0;\r
-UINTN mMaxDpcQueueDepth = 0;\r
-\r
-//\r
-// Free list of DPC entries. As DPCs are queued, entries are removed from this\r
-// free list. As DPC entries are dispatched, DPC entries are added to the free list.\r
-// If the free list is empty and a DPC is queued, the free list is grown by allocating\r
-// an additional set of DPC entries.\r
-//\r
-LIST_ENTRY mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE(mDpcEntryFreeList);\r
-\r
-//\r
-// An array of DPC queues. A DPC queue is allocated for every leval EFI_TPL value.\r
-// As DPCs are queued, they are added to the end of the linked list.\r
-// As DPCs are dispatched, they are removed from the beginning of the linked list.\r
-//\r
-LIST_ENTRY mDpcQueue[TPL_HIGH_LEVEL + 1];\r
-\r
-/**\r
- Add a Deferred Procedure Call to the end of the DPC queue.\r
-\r
- @param This Protocol instance pointer.\r
- @param DpcTpl The EFI_TPL that the DPC should be invoked.\r
- @param DpcProcedure Pointer to the DPC's function.\r
- @param DpcContext Pointer to the DPC's context. Passed to DpcProcedure\r
- when DpcProcedure is invoked.\r
-\r
- @retval EFI_SUCCESS The DPC was queued.\r
- @retval EFI_INVALID_PARAMETER DpcTpl is not a valid EFI_TPL.\r
- @retval EFI_INVALID_PARAMETER DpcProcedure is NULL.\r
- @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
- add the DPC to the queue.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-DpcQueueDpc (\r
- IN EFI_DPC_PROTOCOL *This,\r
- IN EFI_TPL DpcTpl,\r
- IN EFI_DPC_PROCEDURE DpcProcedure,\r
- IN VOID *DpcContext OPTIONAL\r
- )\r
-{\r
- EFI_STATUS ReturnStatus;\r
- EFI_TPL OriginalTpl;\r
- DPC_ENTRY *DpcEntry;\r
- UINTN Index;\r
-\r
- //\r
- // Make sure DpcTpl is valid\r
- //\r
- if (DpcTpl < TPL_APPLICATION || DpcTpl > TPL_HIGH_LEVEL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- //\r
- // Make sure DpcProcedure is valid\r
- //\r
- if (DpcProcedure == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- //\r
- // Assume this function will succeed\r
- //\r
- ReturnStatus = EFI_SUCCESS;\r
-\r
- //\r
- // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the\r
- // current TPL value so it can be restored when this function returns.\r
- //\r
- OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
-\r
- //\r
- // Check to see if there are any entries in the DPC free list\r
- //\r
- if (IsListEmpty (&mDpcEntryFreeList)) {\r
- //\r
- // If the current TPL is greater than TPL_NOTIFY, then memory allocations\r
- // can not be performed, so the free list can not be expanded. In this case\r
- // return EFI_OUT_OF_RESOURCES.\r
- //\r
- if (OriginalTpl > TPL_NOTIFY) {\r
- ReturnStatus = EFI_OUT_OF_RESOURCES;\r
- goto Done;\r
- }\r
-\r
- //\r
- // Add 64 DPC entries to the free list\r
- //\r
- for (Index = 0; Index < 64; Index++) {\r
- //\r
- // Lower the TPL level to perform a memory allocation\r
- //\r
- gBS->RestoreTPL (OriginalTpl);\r
-\r
- //\r
- // Allocate a new DPC entry\r
- //\r
- DpcEntry = AllocatePool (sizeof (DPC_ENTRY));\r
-\r
- //\r
- // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations\r
- //\r
- gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
-\r
- //\r
- // If the allocation of a DPC entry fails, and the free list is empty,\r
- // then return EFI_OUT_OF_RESOURCES.\r
- //\r
- if (DpcEntry == NULL) {\r
- if (IsListEmpty (&mDpcEntryFreeList)) {\r
- ReturnStatus = EFI_OUT_OF_RESOURCES;\r
- goto Done;\r
- }\r
- }\r
-\r
- //\r
- // Add the newly allocated DPC entry to the DPC free list\r
- //\r
- InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);\r
- }\r
- }\r
-\r
- //\r
- // Retrieve the first node from the free list of DPCs\r
- //\r
- DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList));\r
-\r
- //\r
- // Remove the first node from the free list of DPCs\r
- //\r
- RemoveEntryList (&DpcEntry->ListEntry);\r
-\r
- //\r
- // Fill in the DPC entry with the DpcProcedure and DpcContext\r
- //\r
- DpcEntry->DpcProcedure = DpcProcedure;\r
- DpcEntry->DpcContext = DpcContext;\r
-\r
- //\r
- // Add the DPC entry to the end of the list for the specified DplTpl.\r
- //\r
- InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry);\r
-\r
- //\r
- // Increment the measured DPC queue depth across all TPLs\r
- //\r
- mDpcQueueDepth++;\r
-\r
- //\r
- // Measure the maximum DPC queue depth across all TPLs\r
- //\r
- if (mDpcQueueDepth > mMaxDpcQueueDepth) {\r
- mMaxDpcQueueDepth = mDpcQueueDepth;\r
- }\r
-\r
-Done:\r
- //\r
- // Restore the original TPL level when this function was called\r
- //\r
- gBS->RestoreTPL (OriginalTpl);\r
-\r
- return ReturnStatus;\r
-}\r
-\r
-/**\r
- Dispatch the queue of DPCs. ALL DPCs that have been queued with a DpcTpl\r
- value greater than or equal to the current TPL are invoked in the order that\r
- they were queued. DPCs with higher DpcTpl values are invoked before DPCs with\r
- lower DpcTpl values.\r
-\r
- @param This Protocol instance pointer.\r
-\r
- @retval EFI_SUCCESS One or more DPCs were invoked.\r
- @retval EFI_NOT_FOUND No DPCs were invoked.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-DpcDispatchDpc (\r
- IN EFI_DPC_PROTOCOL *This\r
- )\r
-{\r
- EFI_STATUS ReturnStatus;\r
- EFI_TPL OriginalTpl;\r
- EFI_TPL Tpl;\r
- DPC_ENTRY *DpcEntry;\r
-\r
- //\r
- // Assume that no DPCs will be invoked\r
- //\r
- ReturnStatus = EFI_NOT_FOUND;\r
-\r
- //\r
- // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the\r
- // current TPL value so it can be restored when this function returns.\r
- //\r
- OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
-\r
- //\r
- // Check to see if there are 1 or more DPCs currently queued\r
- //\r
- if (mDpcQueueDepth > 0) {\r
- //\r
- // Loop from TPL_HIGH_LEVEL down to the current TPL value\r
- //\r
- for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) {\r
- //\r
- // Check to see if the DPC queue is empty\r
- //\r
- while (!IsListEmpty (&mDpcQueue[Tpl])) {\r
- //\r
- // Retrieve the first DPC entry from the DPC queue specified by Tpl\r
- //\r
- DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl]));\r
-\r
- //\r
- // Remove the first DPC entry from the DPC queue specified by Tpl\r
- //\r
- RemoveEntryList (&DpcEntry->ListEntry);\r
-\r
- //\r
- // Decrement the measured DPC Queue Depth across all TPLs\r
- //\r
- mDpcQueueDepth--;\r
-\r
- //\r
- // Lower the TPL to TPL value of the current DPC queue\r
- //\r
- gBS->RestoreTPL (Tpl);\r
-\r
- //\r
- // Invoke the DPC passing in its context\r
- //\r
- (DpcEntry->DpcProcedure) (DpcEntry->DpcContext);\r
-\r
- //\r
- // At least one DPC has been invoked, so set the return status to EFI_SUCCESS\r
- //\r
- ReturnStatus = EFI_SUCCESS;\r
-\r
- //\r
- // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations\r
- //\r
- gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
-\r
- //\r
- // Add the invoked DPC entry to the DPC free list\r
- //\r
- InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);\r
- }\r
- }\r
- }\r
-\r
- //\r
- // Restore the original TPL level when this function was called\r
- //\r
- gBS->RestoreTPL (OriginalTpl);\r
-\r
- return ReturnStatus;\r
-}\r
-\r
-/**\r
- The entry point for DPC driver which installs the EFI_DPC_PROTOCOL onto a new handle.\r
-\r
- @param ImageHandle The image handle of the driver.\r
- @param SystemTable The system table.\r
-\r
- @retval EFI_SUCCES The DPC queues were initialized and the EFI_DPC_PROTOCOL was\r
- installed onto a new handle.\r
- @retval Others Failed to install EFI_DPC_PROTOCOL.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-DpcDriverEntryPoint (\r
- IN EFI_HANDLE ImageHandle,\r
- IN EFI_SYSTEM_TABLE *SystemTable\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN Index;\r
-\r
- //\r
- // ASSERT() if the EFI_DPC_PROTOCOL is already present in the handle database\r
- //\r
- ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiDpcProtocolGuid);\r
-\r
- //\r
- // Initialize the DPC queue for all possible TPL values\r
- //\r
- for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {\r
- InitializeListHead (&mDpcQueue[Index]);\r
- }\r
-\r
- //\r
- // Install the EFI_DPC_PROTOCOL instance onto a new handle\r
- //\r
- Status = gBS->InstallMultipleProtocolInterfaces (\r
- &mDpcHandle,\r
- &gEfiDpcProtocolGuid,\r
- &mDpc,\r
- NULL\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- return Status;\r
-}\r