1 | /** @file
|
---|
2 |
|
---|
3 | Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
|
---|
4 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
5 |
|
---|
6 | Module Name:
|
---|
7 |
|
---|
8 | Dpc.c
|
---|
9 |
|
---|
10 | Abstract:
|
---|
11 |
|
---|
12 |
|
---|
13 | **/
|
---|
14 |
|
---|
15 | #include "Dpc.h"
|
---|
16 |
|
---|
17 | //
|
---|
18 | // Handle for the EFI_DPC_PROTOCOL instance
|
---|
19 | //
|
---|
20 | EFI_HANDLE mDpcHandle = NULL;
|
---|
21 |
|
---|
22 | //
|
---|
23 | // The EFI_DPC_PROTOCOL instances that is installed onto mDpcHandle
|
---|
24 | //
|
---|
25 | EFI_DPC_PROTOCOL mDpc = {
|
---|
26 | DpcQueueDpc,
|
---|
27 | DpcDispatchDpc
|
---|
28 | };
|
---|
29 |
|
---|
30 | //
|
---|
31 | // Global variables used to meaasure the DPC Queue Depths
|
---|
32 | //
|
---|
33 | UINTN mDpcQueueDepth = 0;
|
---|
34 | UINTN mMaxDpcQueueDepth = 0;
|
---|
35 |
|
---|
36 | //
|
---|
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.
|
---|
41 | //
|
---|
42 | LIST_ENTRY mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE(mDpcEntryFreeList);
|
---|
43 |
|
---|
44 | //
|
---|
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.
|
---|
48 | //
|
---|
49 | LIST_ENTRY mDpcQueue[TPL_HIGH_LEVEL + 1];
|
---|
50 |
|
---|
51 | /**
|
---|
52 | Add a Deferred Procedure Call to the end of the DPC queue.
|
---|
53 |
|
---|
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.
|
---|
59 |
|
---|
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.
|
---|
65 |
|
---|
66 | **/
|
---|
67 | EFI_STATUS
|
---|
68 | EFIAPI
|
---|
69 | DpcQueueDpc (
|
---|
70 | IN EFI_DPC_PROTOCOL *This,
|
---|
71 | IN EFI_TPL DpcTpl,
|
---|
72 | IN EFI_DPC_PROCEDURE DpcProcedure,
|
---|
73 | IN VOID *DpcContext OPTIONAL
|
---|
74 | )
|
---|
75 | {
|
---|
76 | EFI_STATUS ReturnStatus;
|
---|
77 | EFI_TPL OriginalTpl;
|
---|
78 | DPC_ENTRY *DpcEntry;
|
---|
79 | UINTN Index;
|
---|
80 |
|
---|
81 | //
|
---|
82 | // Make sure DpcTpl is valid
|
---|
83 | //
|
---|
84 | if (DpcTpl < TPL_APPLICATION || DpcTpl > TPL_HIGH_LEVEL) {
|
---|
85 | return EFI_INVALID_PARAMETER;
|
---|
86 | }
|
---|
87 |
|
---|
88 | //
|
---|
89 | // Make sure DpcProcedure is valid
|
---|
90 | //
|
---|
91 | if (DpcProcedure == NULL) {
|
---|
92 | return EFI_INVALID_PARAMETER;
|
---|
93 | }
|
---|
94 |
|
---|
95 | //
|
---|
96 | // Assume this function will succeed
|
---|
97 | //
|
---|
98 | ReturnStatus = EFI_SUCCESS;
|
---|
99 |
|
---|
100 | //
|
---|
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.
|
---|
103 | //
|
---|
104 | OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
---|
105 |
|
---|
106 | //
|
---|
107 | // Check to see if there are any entries in the DPC free list
|
---|
108 | //
|
---|
109 | if (IsListEmpty (&mDpcEntryFreeList)) {
|
---|
110 | //
|
---|
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.
|
---|
114 | //
|
---|
115 | if (OriginalTpl > TPL_NOTIFY) {
|
---|
116 | ReturnStatus = EFI_OUT_OF_RESOURCES;
|
---|
117 | goto Done;
|
---|
118 | }
|
---|
119 |
|
---|
120 | //
|
---|
121 | // Add 64 DPC entries to the free list
|
---|
122 | //
|
---|
123 | for (Index = 0; Index < 64; Index++) {
|
---|
124 | //
|
---|
125 | // Lower the TPL level to perform a memory allocation
|
---|
126 | //
|
---|
127 | gBS->RestoreTPL (OriginalTpl);
|
---|
128 |
|
---|
129 | //
|
---|
130 | // Allocate a new DPC entry
|
---|
131 | //
|
---|
132 | DpcEntry = AllocatePool (sizeof (DPC_ENTRY));
|
---|
133 |
|
---|
134 | //
|
---|
135 | // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
|
---|
136 | //
|
---|
137 | gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
---|
138 |
|
---|
139 | //
|
---|
140 | // If the allocation of a DPC entry fails, and the free list is empty,
|
---|
141 | // then return EFI_OUT_OF_RESOURCES.
|
---|
142 | //
|
---|
143 | if (DpcEntry == NULL) {
|
---|
144 | if (IsListEmpty (&mDpcEntryFreeList)) {
|
---|
145 | ReturnStatus = EFI_OUT_OF_RESOURCES;
|
---|
146 | goto Done;
|
---|
147 | }
|
---|
148 | }
|
---|
149 |
|
---|
150 | //
|
---|
151 | // Add the newly allocated DPC entry to the DPC free list
|
---|
152 | //
|
---|
153 | InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);
|
---|
154 | }
|
---|
155 | }
|
---|
156 |
|
---|
157 | //
|
---|
158 | // Retrieve the first node from the free list of DPCs
|
---|
159 | //
|
---|
160 | DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList));
|
---|
161 |
|
---|
162 | //
|
---|
163 | // Remove the first node from the free list of DPCs
|
---|
164 | //
|
---|
165 | RemoveEntryList (&DpcEntry->ListEntry);
|
---|
166 |
|
---|
167 | //
|
---|
168 | // Fill in the DPC entry with the DpcProcedure and DpcContext
|
---|
169 | //
|
---|
170 | DpcEntry->DpcProcedure = DpcProcedure;
|
---|
171 | DpcEntry->DpcContext = DpcContext;
|
---|
172 |
|
---|
173 | //
|
---|
174 | // Add the DPC entry to the end of the list for the specified DplTpl.
|
---|
175 | //
|
---|
176 | InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry);
|
---|
177 |
|
---|
178 | //
|
---|
179 | // Increment the measured DPC queue depth across all TPLs
|
---|
180 | //
|
---|
181 | mDpcQueueDepth++;
|
---|
182 |
|
---|
183 | //
|
---|
184 | // Measure the maximum DPC queue depth across all TPLs
|
---|
185 | //
|
---|
186 | if (mDpcQueueDepth > mMaxDpcQueueDepth) {
|
---|
187 | mMaxDpcQueueDepth = mDpcQueueDepth;
|
---|
188 | }
|
---|
189 |
|
---|
190 | Done:
|
---|
191 | //
|
---|
192 | // Restore the original TPL level when this function was called
|
---|
193 | //
|
---|
194 | gBS->RestoreTPL (OriginalTpl);
|
---|
195 |
|
---|
196 | return ReturnStatus;
|
---|
197 | }
|
---|
198 |
|
---|
199 | /**
|
---|
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
|
---|
203 | lower DpcTpl values.
|
---|
204 |
|
---|
205 | @param This Protocol instance pointer.
|
---|
206 |
|
---|
207 | @retval EFI_SUCCESS One or more DPCs were invoked.
|
---|
208 | @retval EFI_NOT_FOUND No DPCs were invoked.
|
---|
209 |
|
---|
210 | **/
|
---|
211 | EFI_STATUS
|
---|
212 | EFIAPI
|
---|
213 | DpcDispatchDpc (
|
---|
214 | IN EFI_DPC_PROTOCOL *This
|
---|
215 | )
|
---|
216 | {
|
---|
217 | EFI_STATUS ReturnStatus;
|
---|
218 | EFI_TPL OriginalTpl;
|
---|
219 | EFI_TPL Tpl;
|
---|
220 | DPC_ENTRY *DpcEntry;
|
---|
221 |
|
---|
222 | //
|
---|
223 | // Assume that no DPCs will be invoked
|
---|
224 | //
|
---|
225 | ReturnStatus = EFI_NOT_FOUND;
|
---|
226 |
|
---|
227 | //
|
---|
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.
|
---|
230 | //
|
---|
231 | OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
---|
232 |
|
---|
233 | //
|
---|
234 | // Check to see if there are 1 or more DPCs currently queued
|
---|
235 | //
|
---|
236 | if (mDpcQueueDepth > 0) {
|
---|
237 | //
|
---|
238 | // Loop from TPL_HIGH_LEVEL down to the current TPL value
|
---|
239 | //
|
---|
240 | for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) {
|
---|
241 | //
|
---|
242 | // Check to see if the DPC queue is empty
|
---|
243 | //
|
---|
244 | while (!IsListEmpty (&mDpcQueue[Tpl])) {
|
---|
245 | //
|
---|
246 | // Retrieve the first DPC entry from the DPC queue specified by Tpl
|
---|
247 | //
|
---|
248 | DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl]));
|
---|
249 |
|
---|
250 | //
|
---|
251 | // Remove the first DPC entry from the DPC queue specified by Tpl
|
---|
252 | //
|
---|
253 | RemoveEntryList (&DpcEntry->ListEntry);
|
---|
254 |
|
---|
255 | //
|
---|
256 | // Decrement the measured DPC Queue Depth across all TPLs
|
---|
257 | //
|
---|
258 | mDpcQueueDepth--;
|
---|
259 |
|
---|
260 | //
|
---|
261 | // Lower the TPL to TPL value of the current DPC queue
|
---|
262 | //
|
---|
263 | gBS->RestoreTPL (Tpl);
|
---|
264 |
|
---|
265 | //
|
---|
266 | // Invoke the DPC passing in its context
|
---|
267 | //
|
---|
268 | (DpcEntry->DpcProcedure) (DpcEntry->DpcContext);
|
---|
269 |
|
---|
270 | //
|
---|
271 | // At least one DPC has been invoked, so set the return status to EFI_SUCCESS
|
---|
272 | //
|
---|
273 | ReturnStatus = EFI_SUCCESS;
|
---|
274 |
|
---|
275 | //
|
---|
276 | // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
|
---|
277 | //
|
---|
278 | gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
---|
279 |
|
---|
280 | //
|
---|
281 | // Add the invoked DPC entry to the DPC free list
|
---|
282 | //
|
---|
283 | InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);
|
---|
284 | }
|
---|
285 | }
|
---|
286 | }
|
---|
287 |
|
---|
288 | //
|
---|
289 | // Restore the original TPL level when this function was called
|
---|
290 | //
|
---|
291 | gBS->RestoreTPL (OriginalTpl);
|
---|
292 |
|
---|
293 | return ReturnStatus;
|
---|
294 | }
|
---|
295 |
|
---|
296 | /**
|
---|
297 | The entry point for DPC driver which installs the EFI_DPC_PROTOCOL onto a new handle.
|
---|
298 |
|
---|
299 | @param ImageHandle The image handle of the driver.
|
---|
300 | @param SystemTable The system table.
|
---|
301 |
|
---|
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.
|
---|
305 |
|
---|
306 | **/
|
---|
307 | EFI_STATUS
|
---|
308 | EFIAPI
|
---|
309 | DpcDriverEntryPoint (
|
---|
310 | IN EFI_HANDLE ImageHandle,
|
---|
311 | IN EFI_SYSTEM_TABLE *SystemTable
|
---|
312 | )
|
---|
313 | {
|
---|
314 | EFI_STATUS Status;
|
---|
315 | UINTN Index;
|
---|
316 |
|
---|
317 | //
|
---|
318 | // ASSERT() if the EFI_DPC_PROTOCOL is already present in the handle database
|
---|
319 | //
|
---|
320 | ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiDpcProtocolGuid);
|
---|
321 |
|
---|
322 | //
|
---|
323 | // Initialize the DPC queue for all possible TPL values
|
---|
324 | //
|
---|
325 | for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {
|
---|
326 | InitializeListHead (&mDpcQueue[Index]);
|
---|
327 | }
|
---|
328 |
|
---|
329 | //
|
---|
330 | // Install the EFI_DPC_PROTOCOL instance onto a new handle
|
---|
331 | //
|
---|
332 | Status = gBS->InstallMultipleProtocolInterfaces (
|
---|
333 | &mDpcHandle,
|
---|
334 | &gEfiDpcProtocolGuid,
|
---|
335 | &mDpc,
|
---|
336 | NULL
|
---|
337 | );
|
---|
338 | ASSERT_EFI_ERROR (Status);
|
---|
339 |
|
---|
340 | return Status;
|
---|
341 | }
|
---|