Home | History | Annotate | Download | only in DpcDxe
      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