Monday, September 1, 2014

The Design Pattern of UEFI Report Status Code

Observer is a kind of design pattern that is described in the book, Design Pattern - Elements of Reusable Object-Oriented Software, 1994. I recommend the book as a bible.

This blog page describes that the design patterns are not only used in the implementation with Object Oriented Program (e.g., C++, JAVA), but also used in the implementation (e.g., UEFI) with non OOP (e.g., C). Mostly EDKII are implemented in C language. We can find there are many Observer patterns in it. ReportStatusCode is an example.



If you have the book on your hand, you can compare the package diagram of the blog page with the structure diagram in Observer chapter of the book.

  • The ReportStatusCodeRouterRuntimeDxe.inf  is corresponding to the Subject object.
  • The FirmwarePerformanceDxe.inf and StatusCodeHandlerRuntimeDxe.inf are corresponding to the Observer objects.
  • The Register () is corresponding to the Subject.Attach()
  • The Unregister () is corresponding to the Subject.Detach()
  • The ReportStatusCode() is corresponding to the Subject.Notify()


In OOP implementation, the Notify() calls all Observer->Update() to update all registered Observer objects.
Notify ()
{
  for o in Observers {
    o->Update()
  }
}

But for non-OOP implementation, ReportStatusCode(), how does it work? Below is the protocol definition of EFI_RSC_HANDLER_PROTOCOL.

typedef struct {
  EFI_RSC_HANDLER_REGISTER Register;
  EFI_RSC_HANDLER_UNREGISTER Unregister;
} EFI_RSC_HANDLER_PROTOCOL;

Let's focus on Register() in which we only be interested. For Unregister(), it seems that it is seldom used.

typedef
EFI_STATUS
(EFIAPI *EFI_RSC_HANDLER_REGISTER)(
  IN EFI_RSC_HANDLER_CALLBACK   Callback,
  IN EFI_TPL                    Tpl
  )
;

The most important parameter of the Register() is Callback. Below is the prototype of the Callback, EFI_RSC_HANDLER_CALLBACK.

typedef
EFI_STATUS
(EFIAPI *EFI_RSC_HANDLER_CALLBACK)(
  IN EFI_STATUS_CODE_TYPE   CodeType,
  IN EFI_STATUS_CODE_VALUE  Value,
  IN UINT32                 Instance,
  IN EFI_GUID               *CallerId,
  IN EFI_STATUS_CODE_DATA   *Data
  );

Below is the prototype of the EFI_STATUS_CODE_PROTOCOL.ReportStatusCode().

typedef
EFI_STATUS 
(EFIAPI *EFI_REPORT_STATUS_CODE) (
  IN EFI_STATUS_CODE_TYPE     Type,
  IN EFI_STATUS_CODE_VALUE    Value,
  IN UINT32                   Instance,
  IN EFI_GUID                 *CallerId  OPTIONAL,
  IN EFI_STATUS_CODE_DATA     *Data      OPTIONAL
  )

We found that the prototype of Register() and ReportStatusCode() are same. Therefore I guess that the implementation of the ReportStatusCode() should be as follows. Forgive me that I use python-style pseudo code to explain it.

ReportStatusCode (params):
  for CallBack in mCallBackList:
    CallBack (params)  

Is pseudo code really true? You can trace the EDKII source code by yourself to verify if my assumption is true. This page just describes that there are many design patterns in EDK and EDKII. Furthermore, the ReportStatusCodeRouterRuntimeDxe.inf should be a design pattern of Singleton. What is Singleton? Please read book or GOOGLE it.

No comments:

Post a Comment