diff options
Diffstat (limited to 'Lessons/Lesson_36/README.md')
-rw-r--r-- | Lessons/Lesson_36/README.md | 254 |
1 files changed, 162 insertions, 92 deletions
diff --git a/Lessons/Lesson_36/README.md b/Lessons/Lesson_36/README.md index f6d64ad..8f73957 100644 --- a/Lessons/Lesson_36/README.md +++ b/Lessons/Lesson_36/README.md @@ -1,157 +1,227 @@ -In this lesson we will try to create the most simple library. +In this lesson we'll learn about library constructor and destructor. -Usually libraries are present in these directories: -``` -<Pkg Name>/Library/<Library Name>/ <---- inf and source files for the library (=library implementation) -<Pkg Name>/Include/Library/ <---- library headers (=library interface) -``` - -Create folders for our `SimpleLibrary`: -``` -$ mkdir -p UefiLessonsPkg/Library/SimpleLibrary/ -$ mkdir -p UefiLessonsPkg/Include/Library/ -``` - -First let's implement the header file, the interface for our library. Our `SimpleLibrary` would contain the only function `Plus2` that would receive a `number` and return a `number+2`. -Therefore the content in the header file (`UefiLessonsPkg/Include/Library/SimpleLibrary.h`) would look like this: -``` -UINTN Plus2(UINTN number); -``` - -No harder the library implementation file `UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.c`: -``` -#include <Library/SimpleLibrary.h> - -UINTN Plus2(UINTN number) { - return number+2; -} -``` - -This is really a simple library, it stands to its name! - -Now we need to create an INF file for the library `UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf`: +Create a new library module `UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf`: ``` [Defines] INF_VERSION = 1.25 - BASE_NAME = SimpleLibrary - FILE_GUID = 826c8951-5bd2-4d72-a9d9-f7ab48684117 + BASE_NAME = SimpleLibraryWithConstructor + FILE_GUID = 96952c1e-86a6-4700-96b0-e7303ac3f92d MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 1.0 LIBRARY_CLASS = SimpleLibrary | UEFI_APPLICATION + CONSTRUCTOR = SimpleLibraryConstructor <-------- [Sources] - SimpleLibrary.c + SimpleLibraryWithConstructor.c [Packages] MdePkg/MdePkg.dec + UefiLessonsPkg/UefiLessonsPkg.dec ``` +Here we've added `CONSTRUCTOR` statement with a name of constructor function. Let's add `Print` statement to it, to know when it is executed. -The interesting string here is the: -``` -LIBRARY_CLASS = SimpleLibrary | UEFI_APPLICATION -``` -It says that this library can only be used in modules with a type `UEFI_APPLICATION`. If you would say here `DXE_DRIVER` and try to link it to some of you UEFI applications, build process would fail. The error message would look like this: -``` -build.py... -/home/kostr/tiano/edk2/UefiLessonsPkg/UefiLessonsPkg.dsc(...): error 1001: Module type [UEFI_APPLICATION] is not supported by library instance [/home/kostr/tiano/edk2/UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf] - consumed by [/home/kostr/tiano/edk2/UefiLessonsPkg/<path to your app inf file>] +`UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.c`: ``` +#include <Library/UefiLib.h> +#include <Library/SimpleLibrary.h> -Now we need to include our library to our package DSC file `UefiLessonsPkg/UefiLessonsPkg.dsc`, so it would get build on a package build: -``` -[Components] - ... - UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf -``` +UINTN Plus2(UINTN number) { + return number+2; +} -But if you try to build our package now build would fail with a message: -``` -/home/kostr/tiano/edk2/UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.c:1:10: fatal error: Library/SimpleLibrary.h: No such file or directory - 1 | #include <Library/SimpleLibrary.h> - | ^~~~~~~~~~~~~~~~~~~~~~~~~ -compilation terminated. +EFI_STATUS +EFIAPI +SimpleLibraryConstructor( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + Print(L"Hello from library constructor!\n"); + return EFI_SUCCESS; +} ``` -The reason of that is a fact that our `UefiLessonsPkg/Include/Library/` folder is not recognized by a build system as a place where headers might be. -To fix it we need to add to our `UefiLessonsPkg/UefiLessonsPkg.dec` file `[Includes]` section: +Now we don't need to create another app that would use our new lib, we can simply change library implementation in the `UefiLessonsPkg/UefiLessonsPkg.dsc` and our `SimpleLibraryUser` would be recompiled with our new library version: ``` -[Includes] - Include +[LibraryClasses] + ... + #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf + SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf ``` -And include this `UefiLessonsPkg/UefiLessonsPkg.dec` file to the library module INF file section `[Packages]`: +If you build our app and execute it under OVMF now you would get: ``` -[Packages] - MdePkg/MdePkg.dec - UefiLessonsPkg/UefiLessonsPkg.dec +FS0:\> SimpleLibraryUser.efi +Hello from library constructor! +5 ``` -Now we are good, build would succeed. +An example of a library that uses constructor would be `UefiBootServicesTableLib` library https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf. We've used it all over in our lessons, so let's take a look at it. -# SimpleLibraryUser +As a matter of fact, constructor is the only thing that this library has. -Now let's create an application that would use our library. +To understand how this library works take a look at its *.c and *.h files: -UefiLessonsPkg/SimpleLibraryUser/SimpleLibraryUser.c: +https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c ``` -#include <Library/UefiBootServicesTableLib.h> -#include <Library/UefiLib.h> - -#include <Library/SimpleLibrary.h> +EFI_HANDLE gImageHandle = NULL; +EFI_SYSTEM_TABLE *gST = NULL; +EFI_BOOT_SERVICES *gBS = NULL; EFI_STATUS EFIAPI -UefiMain ( +UefiBootServicesTableLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { - Print(L"%d\n", Plus2(3)); + gImageHandle = ImageHandle; + gST = SystemTable; + gBS = SystemTable->BootServices; return EFI_SUCCESS; } ``` +https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiBootServicesTableLib.h: +``` +extern EFI_HANDLE gImageHandle; +extern EFI_SYSTEM_TABLE *gST; +extern EFI_BOOT_SERVICES *gBS; +``` + +So as you can see this library just sets some global variables - shortcuts for the UEFI main parts. As the library constructors execute before the main app code, with this library you can access `gImageHandle`/`gST`/`gBS` anywhere in your app code from the very start. -UefiLessonsPkg/SimpleLibraryUser/SimpleLibraryUser.inf +# DESTRUCTOR + +Similar we can create another version of a `SimpleLibrary` that would have both constructor and destructor. + +`UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.inf`: ``` [Defines] INF_VERSION = 1.25 - BASE_NAME = SimpleLibraryUser - FILE_GUID = 22a1f57c-21ca-4011-9133-e3df0d01dace + BASE_NAME = SimpleLibraryWithConstructorAndDestructor + FILE_GUID = 96952c1e-86a6-4700-96b0-e7303ac3f92d MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 1.0 - ENTRY_POINT = UefiMain + LIBRARY_CLASS = SimpleLibrary | UEFI_APPLICATION + CONSTRUCTOR = SimpleLibraryConstructor + DESTRUCTOR = SimpleLibraryDestructor <----------- [Sources] - SimpleLibraryUser.c + SimpleLibraryWithConstructorAndDestructor.c [Packages] MdePkg/MdePkg.dec - UefiLessonsPkg/UefiLessonsPkg.dec <--- we need to include this for the same reason as in library INF file (for the header search) + UefiLessonsPkg/UefiLessonsPkg.dec +``` +`UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.c`: +``` +#include <Library/UefiLib.h> +#include <Library/SimpleLibrary.h> -[LibraryClasses] - UefiApplicationEntryPoint - UefiLib - SimpleLibrary <--- library is included as usual +UINTN Plus2(UINTN number) { + return number+2; +} + +EFI_STATUS +EFIAPI +SimpleLibraryConstructor( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + Print(L"Hello from library constructor!\n"); + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +SimpleLibraryDestructor( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + Print(L"Hello from library destructor!\n"); + return EFI_SUCCESS; +} ``` -Now add modifications to the `UefiLessonsPkg/UefiLessonsPkg.dsc`: +Don't forget to change the library backend in the `UefiLessonsPkg/UefiLessonsPkg.dsc`: ``` [LibraryClasses] ... - SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf - -[Components] - ... - UefiLessonsPkg/SimpleLibraryUser/SimpleLibraryUser.inf + #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf + #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf + SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.inf ``` -Here we've added implementation for our library class and added our new module to the package components. -If you build everything now and execute it under OVMF, you would get: +Now our app would have print strings both at the beginning and in the end: ``` FS0:\> SimpleLibraryUser.efi +Hello from library constructor! 5 +Hello from library destructor! ``` -`3+2` is indeed `5`, so our library works correctly! + +As an example of a library with both CONSTRUCTOR and DESTRUCTOR take a look at the https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiDebugLibConOut/DebugLibConstructor.c, it uses `CreateEvent` in a constructor, and closes it in a desctrutor with a `CloseEvent`. + + +# `NULL` library + + +As you already know OVMF includes `Shell` app in itself. For its compilation OVMF package DSC file (https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.dsc) contains these strings: +``` +[Components] + ... + ShellPkg/Application/Shell/Shell.inf { + <LibraryClasses> + ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf + NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf + NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf + NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf + NULL|ShellPkg/Library/UefiShellDriver1CommandsLib/UefiShellDriver1CommandsLib.inf + NULL|ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf + NULL|ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf + NULL|ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf + ... + HandleParsingLib|ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + BcfgCommandLib|ShellPkg/Library/UefiShellBcfgCommandLib/UefiShellBcfgCommandLib.inf + + ... + } +``` + +Here you can notice that some of the library classes have `NULL` class. + +`NULL` library classes are conceptually an "anonymous library". It enables one to statically link code into a module even if the module doesn't directly call functions in that library. + +It can be useful if we don't need to call library API in our app, but just need library constructor/destructor functions. + +If you'll take a look at the file https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.c you'll see that this module constructor is used to add additional commands to the Shell: +``` +EFI_STATUS +EFIAPI +ShellLevel1CommandsLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + ... + ShellCommandRegisterCommandName(L"stall", ShellCommandRunStall , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_STALL) )); + ShellCommandRegisterCommandName(L"for", ShellCommandRunFor , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_FOR) )); + ShellCommandRegisterCommandName(L"goto", ShellCommandRunGoto , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_GOTO) )); + ShellCommandRegisterCommandName(L"if", ShellCommandRunIf , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_IF) )); + ShellCommandRegisterCommandName(L"shift", ShellCommandRunShift , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_SHIFT) )); + ShellCommandRegisterCommandName(L"exit", ShellCommandRunExit , ShellCommandGetManFileNameLevel1, 1, L"", TRUE , gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_EXIT) )); + ShellCommandRegisterCommandName(L"else", ShellCommandRunElse , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_ELSE) )); + ShellCommandRegisterCommandName(L"endif", ShellCommandRunEndIf , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_ENDIF) )); + ShellCommandRegisterCommandName(L"endfor", ShellCommandRunEndFor , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_ENDFOR))); + + return (EFI_SUCCESS); +``` + +This is an elegant way to split shell command support to different modules. +With this functionality you can easily compile your image of `Shell` with a necessary commands support. |