3 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
18 // Handle for the EFI_DPC_PROTOCOL instance
20 EFI_HANDLE mDpcHandle
= NULL
;
23 // The EFI_DPC_PROTOCOL instances that is installed onto mDpcHandle
25 EFI_DPC_PROTOCOL mDpc
= {
31 // Global variables used to meaasure the DPC Queue Depths
33 UINTN mDpcQueueDepth
= 0;
34 UINTN mMaxDpcQueueDepth
= 0;
37 // Free list of DPC entries. As DPCs are queued, entries are removed from this
38 // free list. As DPC entries are dispatched, DPC entries are added to the free list.
39 // If the free list is empty and a DPC is queued, the free list is grown by allocating
40 // an additional set of DPC entries.
42 LIST_ENTRY mDpcEntryFreeList
= INITIALIZE_LIST_HEAD_VARIABLE(mDpcEntryFreeList
);
45 // An array of DPC queues. A DPC queue is allocated for every leval EFI_TPL value.
46 // As DPCs are queued, they are added to the end of the linked list.
47 // As DPCs are dispatched, they are removed from the beginning of the linked list.
49 LIST_ENTRY mDpcQueue
[TPL_HIGH_LEVEL
+ 1];
52 Add a Deferred Procedure Call to the end of the DPC queue.
54 @param This Protocol instance pointer.
55 @param DpcTpl The EFI_TPL that the DPC should be invoked.
56 @param DpcProcedure Pointer to the DPC's function.
57 @param DpcContext Pointer to the DPC's context. Passed to DpcProcedure
58 when DpcProcedure is invoked.
60 @retval EFI_SUCCESS The DPC was queued.
61 @retval EFI_INVALID_PARAMETER DpcTpl is not a valid EFI_TPL.
62 @retval EFI_INVALID_PARAMETER DpcProcedure is NULL.
63 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
64 add the DPC to the queue.
70 IN EFI_DPC_PROTOCOL
*This
,
72 IN EFI_DPC_PROCEDURE DpcProcedure
,
73 IN VOID
*DpcContext OPTIONAL
76 EFI_STATUS ReturnStatus
;
82 // Make sure DpcTpl is valid
84 if (DpcTpl
< TPL_APPLICATION
|| DpcTpl
> TPL_HIGH_LEVEL
) {
85 return EFI_INVALID_PARAMETER
;
89 // Make sure DpcProcedure is valid
91 if (DpcProcedure
== NULL
) {
92 return EFI_INVALID_PARAMETER
;
96 // Assume this function will succeed
98 ReturnStatus
= EFI_SUCCESS
;
101 // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the
102 // current TPL value so it can be restored when this function returns.
104 OriginalTpl
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
107 // Check to see if there are any entries in the DPC free list
109 if (IsListEmpty (&mDpcEntryFreeList
)) {
111 // If the current TPL is greater than TPL_NOTIFY, then memory allocations
112 // can not be performed, so the free list can not be expanded. In this case
113 // return EFI_OUT_OF_RESOURCES.
115 if (OriginalTpl
> TPL_NOTIFY
) {
116 ReturnStatus
= EFI_OUT_OF_RESOURCES
;
121 // Add 64 DPC entries to the free list
123 for (Index
= 0; Index
< 64; Index
++) {
125 // Lower the TPL level to perform a memory allocation
127 gBS
->RestoreTPL (OriginalTpl
);
130 // Allocate a new DPC entry
132 DpcEntry
= AllocatePool (sizeof (DPC_ENTRY
));
135 // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
137 gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
140 // If the allocation of a DPC entry fails, and the free list is empty,
141 // then return EFI_OUT_OF_RESOURCES.
143 if (DpcEntry
== NULL
) {
144 if (IsListEmpty (&mDpcEntryFreeList
)) {
145 ReturnStatus
= EFI_OUT_OF_RESOURCES
;
151 // Add the newly allocated DPC entry to the DPC free list
153 InsertTailList (&mDpcEntryFreeList
, &DpcEntry
->ListEntry
);
158 // Retrieve the first node from the free list of DPCs
160 DpcEntry
= (DPC_ENTRY
*)(GetFirstNode (&mDpcEntryFreeList
));
163 // Remove the first node from the free list of DPCs
165 RemoveEntryList (&DpcEntry
->ListEntry
);
168 // Fill in the DPC entry with the DpcProcedure and DpcContext
170 DpcEntry
->DpcProcedure
= DpcProcedure
;
171 DpcEntry
->DpcContext
= DpcContext
;
174 // Add the DPC entry to the end of the list for the specified DplTpl.
176 InsertTailList (&mDpcQueue
[DpcTpl
], &DpcEntry
->ListEntry
);
179 // Increment the measured DPC queue depth across all TPLs
184 // Measure the maximum DPC queue depth across all TPLs
186 if (mDpcQueueDepth
> mMaxDpcQueueDepth
) {
187 mMaxDpcQueueDepth
= mDpcQueueDepth
;
192 // Restore the original TPL level when this function was called
194 gBS
->RestoreTPL (OriginalTpl
);
200 Dispatch the queue of DPCs. ALL DPCs that have been queued with a DpcTpl
201 value greater than or equal to the current TPL are invoked in the order that
202 they were queued. DPCs with higher DpcTpl values are invoked before DPCs with
205 @param This Protocol instance pointer.
207 @retval EFI_SUCCESS One or more DPCs were invoked.
208 @retval EFI_NOT_FOUND No DPCs were invoked.
214 IN EFI_DPC_PROTOCOL
*This
217 EFI_STATUS ReturnStatus
;
223 // Assume that no DPCs will be invoked
225 ReturnStatus
= EFI_NOT_FOUND
;
228 // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the
229 // current TPL value so it can be restored when this function returns.
231 OriginalTpl
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
234 // Check to see if there are 1 or more DPCs currently queued
236 if (mDpcQueueDepth
> 0) {
238 // Loop from TPL_HIGH_LEVEL down to the current TPL value
240 for (Tpl
= TPL_HIGH_LEVEL
; Tpl
>= OriginalTpl
; Tpl
--) {
242 // Check to see if the DPC queue is empty
244 while (!IsListEmpty (&mDpcQueue
[Tpl
])) {
246 // Retrieve the first DPC entry from the DPC queue specified by Tpl
248 DpcEntry
= (DPC_ENTRY
*)(GetFirstNode (&mDpcQueue
[Tpl
]));
251 // Remove the first DPC entry from the DPC queue specified by Tpl
253 RemoveEntryList (&DpcEntry
->ListEntry
);
256 // Decrement the measured DPC Queue Depth across all TPLs
261 // Lower the TPL to TPL value of the current DPC queue
263 gBS
->RestoreTPL (Tpl
);
266 // Invoke the DPC passing in its context
268 (DpcEntry
->DpcProcedure
) (DpcEntry
->DpcContext
);
271 // At least one DPC has been invoked, so set the return status to EFI_SUCCESS
273 ReturnStatus
= EFI_SUCCESS
;
276 // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
278 gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
281 // Add the invoked DPC entry to the DPC free list
283 InsertTailList (&mDpcEntryFreeList
, &DpcEntry
->ListEntry
);
289 // Restore the original TPL level when this function was called
291 gBS
->RestoreTPL (OriginalTpl
);
297 The entry point for DPC driver which installs the EFI_DPC_PROTOCOL onto a new handle.
299 @param ImageHandle The image handle of the driver.
300 @param SystemTable The system table.
302 @retval EFI_SUCCES The DPC queues were initialized and the EFI_DPC_PROTOCOL was
303 installed onto a new handle.
304 @retval Others Failed to install EFI_DPC_PROTOCOL.
309 DpcDriverEntryPoint (
310 IN EFI_HANDLE ImageHandle
,
311 IN EFI_SYSTEM_TABLE
*SystemTable
318 // ASSERT() if the EFI_DPC_PROTOCOL is already present in the handle database
320 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gEfiDpcProtocolGuid
);
323 // Initialize the DPC queue for all possible TPL values
325 for (Index
= 0; Index
<= TPL_HIGH_LEVEL
; Index
++) {
326 InitializeListHead (&mDpcQueue
[Index
]);
330 // Install the EFI_DPC_PROTOCOL instance onto a new handle
332 Status
= gBS
->InstallMultipleProtocolInterfaces (
334 &gEfiDpcProtocolGuid
,
338 ASSERT_EFI_ERROR (Status
);