Dpc.c 8.8 KB

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