1 /** @file 2 3 Copyright (c) 2007 - 2008, Intel Corporation. All rights reserved.<BR> 4 This program and the accompanying materials 5 are licensed and made available under the terms and conditions of the BSD License 6 which accompanies this distribution. The full text of the license may be found at 7 http://opensource.org/licenses/bsd-license.php 8 9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 11 12 Module Name: 13 14 Dpc.c 15 16 Abstract: 17 18 19 **/ 20 21 #include "Dpc.h" 22 23 // 24 // Handle for the EFI_DPC_PROTOCOL instance 25 // 26 EFI_HANDLE mDpcHandle = NULL; 27 28 // 29 // The EFI_DPC_PROTOCOL instances that is installed onto mDpcHandle 30 // 31 EFI_DPC_PROTOCOL mDpc = { 32 DpcQueueDpc, 33 DpcDispatchDpc 34 }; 35 36 // 37 // Global variables used to meaasure the DPC Queue Depths 38 // 39 UINTN mDpcQueueDepth = 0; 40 UINTN mMaxDpcQueueDepth = 0; 41 42 // 43 // Free list of DPC entries. As DPCs are queued, entries are removed from this 44 // free list. As DPC entries are dispatched, DPC entries are added to the free list. 45 // If the free list is empty and a DPC is queued, the free list is grown by allocating 46 // an additional set of DPC entries. 47 // 48 LIST_ENTRY mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE(mDpcEntryFreeList); 49 50 // 51 // An array of DPC queues. A DPC queue is allocated for every leval EFI_TPL value. 52 // As DPCs are queued, they are added to the end of the linked list. 53 // As DPCs are dispatched, they are removed from the beginning of the linked list. 54 // 55 LIST_ENTRY mDpcQueue[TPL_HIGH_LEVEL + 1]; 56 57 /** 58 Add a Deferred Procedure Call to the end of the DPC queue. 59 60 @param This Protocol instance pointer. 61 @param DpcTpl The EFI_TPL that the DPC should be invoked. 62 @param DpcProcedure Pointer to the DPC's function. 63 @param DpcContext Pointer to the DPC's context. Passed to DpcProcedure 64 when DpcProcedure is invoked. 65 66 @retval EFI_SUCCESS The DPC was queued. 67 @retval EFI_INVALID_PARAMETER DpcTpl is not a valid EFI_TPL. 68 @retval EFI_INVALID_PARAMETER DpcProcedure is NULL. 69 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to 70 add the DPC to the queue. 71 72 **/ 73 EFI_STATUS 74 EFIAPI 75 DpcQueueDpc ( 76 IN EFI_DPC_PROTOCOL *This, 77 IN EFI_TPL DpcTpl, 78 IN EFI_DPC_PROCEDURE DpcProcedure, 79 IN VOID *DpcContext OPTIONAL 80 ) 81 { 82 EFI_STATUS ReturnStatus; 83 EFI_TPL OriginalTpl; 84 DPC_ENTRY *DpcEntry; 85 UINTN Index; 86 87 // 88 // Make sure DpcTpl is valid 89 // 90 if (DpcTpl < TPL_APPLICATION || DpcTpl > TPL_HIGH_LEVEL) { 91 return EFI_INVALID_PARAMETER; 92 } 93 94 // 95 // Make sure DpcProcedure is valid 96 // 97 if (DpcProcedure == NULL) { 98 return EFI_INVALID_PARAMETER; 99 } 100 101 // 102 // Assume this function will succeed 103 // 104 ReturnStatus = EFI_SUCCESS; 105 106 // 107 // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the 108 // current TPL value so it can be restored when this function returns. 109 // 110 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); 111 112 // 113 // Check to see if there are any entries in the DPC free list 114 // 115 if (IsListEmpty (&mDpcEntryFreeList)) { 116 // 117 // If the current TPL is greater than TPL_NOTIFY, then memory allocations 118 // can not be performed, so the free list can not be expanded. In this case 119 // return EFI_OUT_OF_RESOURCES. 120 // 121 if (OriginalTpl > TPL_NOTIFY) { 122 ReturnStatus = EFI_OUT_OF_RESOURCES; 123 goto Done; 124 } 125 126 // 127 // Add 64 DPC entries to the free list 128 // 129 for (Index = 0; Index < 64; Index++) { 130 // 131 // Lower the TPL level to perform a memory allocation 132 // 133 gBS->RestoreTPL (OriginalTpl); 134 135 // 136 // Allocate a new DPC entry 137 // 138 DpcEntry = AllocatePool (sizeof (DPC_ENTRY)); 139 140 // 141 // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations 142 // 143 gBS->RaiseTPL (TPL_HIGH_LEVEL); 144 145 // 146 // If the allocation of a DPC entry fails, and the free list is empty, 147 // then return EFI_OUT_OF_RESOURCES. 148 // 149 if (DpcEntry == NULL) { 150 if (IsListEmpty (&mDpcEntryFreeList)) { 151 ReturnStatus = EFI_OUT_OF_RESOURCES; 152 goto Done; 153 } 154 } 155 156 // 157 // Add the newly allocated DPC entry to the DPC free list 158 // 159 InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry); 160 } 161 } 162 163 // 164 // Retrieve the first node from the free list of DPCs 165 // 166 DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList)); 167 168 // 169 // Remove the first node from the free list of DPCs 170 // 171 RemoveEntryList (&DpcEntry->ListEntry); 172 173 // 174 // Fill in the DPC entry with the DpcProcedure and DpcContext 175 // 176 DpcEntry->DpcProcedure = DpcProcedure; 177 DpcEntry->DpcContext = DpcContext; 178 179 // 180 // Add the DPC entry to the end of the list for the specified DplTpl. 181 // 182 InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry); 183 184 // 185 // Increment the measured DPC queue depth across all TPLs 186 // 187 mDpcQueueDepth++; 188 189 // 190 // Measure the maximum DPC queue depth across all TPLs 191 // 192 if (mDpcQueueDepth > mMaxDpcQueueDepth) { 193 mMaxDpcQueueDepth = mDpcQueueDepth; 194 } 195 196 Done: 197 // 198 // Restore the original TPL level when this function was called 199 // 200 gBS->RestoreTPL (OriginalTpl); 201 202 return ReturnStatus; 203 } 204 205 /** 206 Dispatch the queue of DPCs. ALL DPCs that have been queued with a DpcTpl 207 value greater than or equal to the current TPL are invoked in the order that 208 they were queued. DPCs with higher DpcTpl values are invoked before DPCs with 209 lower DpcTpl values. 210 211 @param This Protocol instance pointer. 212 213 @retval EFI_SUCCESS One or more DPCs were invoked. 214 @retval EFI_NOT_FOUND No DPCs were invoked. 215 216 **/ 217 EFI_STATUS 218 EFIAPI 219 DpcDispatchDpc ( 220 IN EFI_DPC_PROTOCOL *This 221 ) 222 { 223 EFI_STATUS ReturnStatus; 224 EFI_TPL OriginalTpl; 225 EFI_TPL Tpl; 226 DPC_ENTRY *DpcEntry; 227 228 // 229 // Assume that no DPCs will be invoked 230 // 231 ReturnStatus = EFI_NOT_FOUND; 232 233 // 234 // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the 235 // current TPL value so it can be restored when this function returns. 236 // 237 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); 238 239 // 240 // Check to see if there are 1 or more DPCs currently queued 241 // 242 if (mDpcQueueDepth > 0) { 243 // 244 // Loop from TPL_HIGH_LEVEL down to the current TPL value 245 // 246 for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) { 247 // 248 // Check to see if the DPC queue is empty 249 // 250 while (!IsListEmpty (&mDpcQueue[Tpl])) { 251 // 252 // Retrieve the first DPC entry from the DPC queue specified by Tpl 253 // 254 DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl])); 255 256 // 257 // Remove the first DPC entry from the DPC queue specified by Tpl 258 // 259 RemoveEntryList (&DpcEntry->ListEntry); 260 261 // 262 // Decrement the measured DPC Queue Depth across all TPLs 263 // 264 mDpcQueueDepth--; 265 266 // 267 // Lower the TPL to TPL value of the current DPC queue 268 // 269 gBS->RestoreTPL (Tpl); 270 271 // 272 // Invoke the DPC passing in its context 273 // 274 (DpcEntry->DpcProcedure) (DpcEntry->DpcContext); 275 276 // 277 // At least one DPC has been invoked, so set the return status to EFI_SUCCESS 278 // 279 ReturnStatus = EFI_SUCCESS; 280 281 // 282 // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations 283 // 284 gBS->RaiseTPL (TPL_HIGH_LEVEL); 285 286 // 287 // Add the invoked DPC entry to the DPC free list 288 // 289 InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry); 290 } 291 } 292 } 293 294 // 295 // Restore the original TPL level when this function was called 296 // 297 gBS->RestoreTPL (OriginalTpl); 298 299 return ReturnStatus; 300 } 301 302 /** 303 The entry point for DPC driver which installs the EFI_DPC_PROTOCOL onto a new handle. 304 305 @param ImageHandle The image handle of the driver. 306 @param SystemTable The system table. 307 308 @retval EFI_SUCCES The DPC queues were initialized and the EFI_DPC_PROTOCOL was 309 installed onto a new handle. 310 @retval Others Failed to install EFI_DPC_PROTOCOL. 311 312 **/ 313 EFI_STATUS 314 EFIAPI 315 DpcDriverEntryPoint ( 316 IN EFI_HANDLE ImageHandle, 317 IN EFI_SYSTEM_TABLE *SystemTable 318 ) 319 { 320 EFI_STATUS Status; 321 UINTN Index; 322 323 // 324 // ASSERT() if the EFI_DPC_PROTOCOL is already present in the handle database 325 // 326 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiDpcProtocolGuid); 327 328 // 329 // Initialize the DPC queue for all possible TPL values 330 // 331 for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) { 332 InitializeListHead (&mDpcQueue[Index]); 333 } 334 335 // 336 // Install the EFI_DPC_PROTOCOL instance onto a new handle 337 // 338 Status = gBS->InstallMultipleProtocolInterfaces ( 339 &mDpcHandle, 340 &gEfiDpcProtocolGuid, 341 &mDpc, 342 NULL 343 ); 344 ASSERT_EFI_ERROR (Status); 345 346 return Status; 347 } 348