aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons_uncategorized
diff options
context:
space:
mode:
Diffstat (limited to 'Lessons_uncategorized')
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/Agreement.pngbin0 -> 43871 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/GHIDRA_logo.pngbin0 -> 189819 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/README.md1152
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/analyze1.pngbin0 -> 121432 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/analyze2.pngbin0 -> 41886 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/analyze3.pngbin0 -> 152214 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/efiSeek1.pngbin0 -> 34215 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/efiSeek2.pngbin0 -> 36787 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/efiSeek3.pngbin0 -> 10639 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/efiSeek4.pngbin0 -> 48641 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/efiSeek5.pngbin0 -> 160136 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/fileinfo.pngbin0 -> 65238 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/fileparse.pngbin0 -> 17015 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils1.pngbin0 -> 42788 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils2.pngbin0 -> 13849 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.pngbin0 -> 42788 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.png:Zone.Identifier3
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/new_project1.pngbin0 -> 12724 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/new_project2.pngbin0 -> 13627 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/noproject.pngbin0 -> 24410 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/projectwithapp.pngbin0 -> 26452 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Ghidra/test_project.pngbin0 -> 25148 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password/README.md362
-rw-r--r--Lessons_uncategorized/Lesson_Password/flowchart.pngbin0 -> 243502 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password/password1.pngbin0 -> 6469 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password/password2.pngbin0 -> 5992 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password/password3.pngbin0 -> 5952 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password/password4.pngbin0 -> 5959 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password/password5.pngbin0 -> 5975 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password/password6.pngbin0 -> 5980 bytes
-rw-r--r--Lessons_uncategorized/Lesson_Password_2/README.md437
-rw-r--r--Lessons_uncategorized/Lesson_RegisterProtocolNotify/README.md470
-rw-r--r--Lessons_uncategorized/Lesson_Varstore_5/README.md36
-rw-r--r--Lessons_uncategorized/Lesson_Varstore_5/UefiLessonsPkg/HIIFormDataElementsVarstore/HIIFormDataElementsVarstore.c5
-rw-r--r--Lessons_uncategorized/Lesson_Varstore_6/README.md5
-rw-r--r--Lessons_uncategorized/Lesson_Varstore_7/README.md212
36 files changed, 2677 insertions, 5 deletions
diff --git a/Lessons_uncategorized/Lesson_Ghidra/Agreement.png b/Lessons_uncategorized/Lesson_Ghidra/Agreement.png
new file mode 100644
index 0000000..3799252
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/Agreement.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/GHIDRA_logo.png b/Lessons_uncategorized/Lesson_Ghidra/GHIDRA_logo.png
new file mode 100644
index 0000000..659c3f9
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/GHIDRA_logo.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/README.md b/Lessons_uncategorized/Lesson_Ghidra/README.md
new file mode 100644
index 0000000..3bad743
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/README.md
@@ -0,0 +1,1152 @@
+In this lesson we would learn how to disassemble UEFI modules.
+
+There are several popular frameworks for reverse engineering. In our lesson we would use [Ghidra](https://ghidra-sre.org/) since it is a pretty mature framework and a fully open-source and free option.
+
+![GHIDRA_logo](GHIDRA_logo.png)
+
+You can find Ghidra source code on [GitHub](https://github.com/NationalSecurityAgency/ghidra).
+
+You can download the latest Ghidra release from its GitHub releases page - [https://github.com/NationalSecurityAgency/ghidra/releases](https://github.com/NationalSecurityAgency/ghidra/releases).
+
+At the time of this writing (03-2024) the latest release of Ghidra is [11.0.1](https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_11.0.1_build) so this is a version that I would use in the examples below.
+
+Since Ghidra is written in Java language you need to install OpenJDK to run it. Currently the necessary version is JDK 17 64-bit. In Ubuntu you can install it via a package manager:
+```
+$ sudo apt install openjdk-17-jdk
+```
+
+Now we are ready to actually download and run Ghidra:
+```
+$ wget https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_11.0.1_build/ghidra_11.0.1_PUBLIC_20240130.zip
+$ unzip ghidra_11.0.1_PUBLIC_20240130.zip
+$ cd ghidra_11.0.1_PUBLIC
+$ $ ./ghidraRun
+```
+
+On the first launch you would be prompted with the Ghidra User Agreement:
+
+![Agreement](Agreement.png)
+
+If you are agree with that you would proceed to the general project window:
+
+![noproject](noproject.png)
+
+Here we need to create a project via `File->New Project` menu option. You would be prompted with a window with selection of a project type:
+
+![new_project1](new_project1.png)
+
+Choose default `Non-Shared Project` option and click `Next`.
+
+In the next window set the `Project Directory` and a `Project Name`:
+
+![new_project2](new_project2.png)
+
+After that in the main Ghidra window you would see the created project:
+
+![test_project](test_project.png)
+
+Now let's import some `*.efi` file for investigation. You can do it via the `File->Import File` menu option. Let's start with the most simple UEFI module and choose our `SimplestApp.efi` application.
+
+In the next window Ghidra would describe it as PE format executable. Just leave everything as it is and click `OK`:
+
+![fileparse](fileparse.png)
+
+The Ghidra would print some info about our file:
+
+![fileinfo](fileinfo.png)
+
+After that our application would appear under the project:
+
+![projectwithapp](projectwithapp.png)
+
+Double click on the `SimplestApp.efi` to open it in the `CodeBrowser` window.
+
+The Ghidra would prompt if you want to analyze the file. Click `Yes`.
+
+![analyze1](analyze1.png)
+
+Next Ghidra would prompt you with a `Analysis Options` window, where you can select `Analyzers` that you want to launch on our file. If you want to you can click on every analyzer and read it description. Right now we don't need to change anything, so when you are ready just press the `Analyze` button:
+
+![analyze2](analyze2.png)
+
+Here is the Ghidra output after running all the default analyzers:
+
+![analyze3](analyze3.png)
+
+At the right window you can see that Ghidra has successfully decompiled the code our UEFI module:
+```cpp
+/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
+
+undefined8 entry(longlong param_1,longlong param_2)
+
+{
+ _DAT_00001750 = param_1;
+ if (param_1 == 0) {
+ FUN_00000e0b();
+ }
+ _DAT_00001748 = param_2;
+ if (param_2 == 0) {
+ FUN_00000e0b();
+ }
+ _.data = *(longlong *)(param_2 + 0x60);
+ if (_.data == 0) {
+ FUN_00000e0b();
+ }
+ return 0;
+}
+```
+
+We can work with this output, but actually we can make the output even more human-friendly.
+
+One of the advantages of Ghidra that it can be extended with a help of plugins. Therefore before investigating the disassembler code, let's learn about `efiSeek` plugin.
+
+# `efiSeek`
+
+ [`efiSeek`](https://github.com/DSecurity/efiSeek) is a Ghidra plugin that helps to analyze UEFI firmware. Particularly it can:
+- Find known EFI GUID’s,
+- Identify protocols which are finding with `LOCATE_PROTOCOL` function,
+- Identify functions used as the `NOTIFY` function
+- Identify protocols installed in the module through `INSTALL_PROTOCOL_INTERFACE`,
+- Identify functions used as an interrupt function (like some hardware, software or child interrupt),
+- Script for loading efi modules to relevant directories upon import in Headless mode,
+- Sort smm modules relying on meta information by next folders[...].
+
+Let's clone the repo and bulld it:
+```
+$ git clone https://github.com/DSecurity/efiSeek.git
+$ cd efiSeek/
+$ chmod +x gradlew
+$ GHIDRA_INSTALL_DIR=~/ghidra/ghidra_11.0.1_PUBLIC ./gradlew # set your path to the GHIDRA_INSTALL_DIR
+```
+
+After that copy the created `*.zip` archive to the ghidra `Extensions/Ghidra/` folder:
+```
+$ cp dist/ghidra_11.0.1_PUBLIC_20240330_efiSeek.zip ../ghidra_11.0.1_PUBLIC/Extensions/Ghidra/
+```
+
+Close Ghidra window and run it again.
+
+Select menu option `File->Install Extensions`. In the opened window you should see the `efiSeek` plugin:
+
+![efiSeek1](efiSeek1.png)
+
+Tick this option and click `OK`:
+
+![efiSeekr2](efiSeek2.png)
+
+After that Ghidra would prompt that you need to restart it again:
+
+![efiSeek3](efiSeek3.png)
+
+Click `OK` and restart Ghidra one more time.
+
+Open our `SimplestApp.efi` with double click.
+
+Now in the analyzer option you should see the `efiSeek` plugin:
+
+![efiSeek4](efiSeek4.png)
+
+Here is the `CodeBrowser` window after the analyze operation:
+
+![efiSeek5](efiSeek5.png)
+
+You can see that now the decompiler output now is more human-friendly:
+```cpp
+EFI_STATUS ModuleEntryPoint(EFI_HANDLE ImageHandle,EFI_SYSTEM_TABLE *SystemTable)
+
+{
+ gImageHandle_37 = ImageHandle;
+ if (ImageHandle == (EFI_HANDLE)0x0) {
+ FUN_80000e0b();
+ }
+ gST_36 = SystemTable;
+ if (SystemTable == (EFI_SYSTEM_TABLE *)0x0) {
+ FUN_80000e0b();
+ }
+ gBS_35 = SystemTable->BootServices;
+ if (gBS_35 == (EFI_BOOT_SERVICES *)0x0) {
+ FUN_80000e0b();
+ }
+ return 0;
+}
+```
+
+# Decompiler output
+
+Let's try to investigate the decompiler output. First let's look at the source code of our application:
+
+```cpp
+EFI_STATUS
+EFIAPI
+UefiMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EFI_SUCCESS;
+}
+```
+
+```
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = SimplestApp
+ FILE_GUID = 4a298956-fbe0-47fb-ae3a-2d5a0a959a26
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = UefiMain
+
+[Sources]
+ SimplestApp.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ UefiApplicationEntryPoint
+```
+
+If you don't understand why there is more code in the decompiler is than in the initial source code, the key thing here is the `UefiApplicationEntryPoint` LibraryClass.
+
+This module defines the `_ModuleEntryPoint` in the following way:
+```cpp
+EFI_STATUS
+EFIAPI
+_ModuleEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ if (_gUefiDriverRevision != 0) {
+ //
+ // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application.
+ //
+ if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+ }
+
+ //
+ // Call constructor for all libraries.
+ //
+ ProcessLibraryConstructorList (ImageHandle, SystemTable);
+
+ //
+ // Call the module's entry point
+ //
+ Status = ProcessModuleEntryPointList (ImageHandle, SystemTable);
+
+ //
+ // Process destructor for all libraries.
+ //
+ ProcessLibraryDestructorList (ImageHandle, SystemTable);
+
+ //
+ // Return the return status code from the driver entry point
+ //
+ return Status;
+}
+```
+
+The definitions to most of these functions you can find in the generated `AutoGen.c` file (`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/SimplestApp/SimplestApp/DEBUG/AutoGen.c`):
+```cpp
+RETURN_STATUS
+EFIAPI
+PlatformDebugLibIoPortConstructor (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+UefiBootServicesTableLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+
+VOID
+EFIAPI
+ProcessLibraryConstructorList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PlatformDebugLibIoPortConstructor ();
+ ASSERT_RETURN_ERROR (Status);
+
+ Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);
+ ASSERT_EFI_ERROR (Status);
+
+}
+
+
+
+VOID
+EFIAPI
+ProcessLibraryDestructorList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+
+}
+
+const UINT32 _gUefiDriverRevision = 0x00000000U;
+
+
+EFI_STATUS
+EFIAPI
+ProcessModuleEntryPointList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+
+{
+ return UefiMain (ImageHandle, SystemTable);
+}
+```
+
+So the empty functions and the check for the static variable `_gUefiDriverRevision` were optimized by the compiler. We also know the code from the `ProcessModuleEntryPointList` (i.e. `UefiMain`) shouldn't give us anything since the entry point of our application is basically empty. So the code that we actually see in the decompiler output is from the `ProcessLibraryConstructorList` (i.e. `UefiBootServicesTableLibConstructor`).
+
+If you look at the [UefiApplicationEntryPoint.inf](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf) you would see that it has the [`UefiBootServicesTableLib`](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf) LibraryClass in itself. This is why it is included in the `AutoGen.c` in the first place.
+
+Now let's look at the [UefiBootServicesTableLib source code](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c):
+```cpp
+EFI_HANDLE gImageHandle = NULL;
+EFI_SYSTEM_TABLE *gST = NULL;
+EFI_BOOT_SERVICES *gBS = NULL;
+
+/**
+ The constructor function caches the pointer of Boot Services Table.
+
+ The constructor function caches the pointer of Boot Services Table through System Table.
+ It will ASSERT() if the pointer of System Table is NULL.
+ It will ASSERT() if the pointer of Boot Services Table is NULL.
+ It will always return EFI_SUCCESS.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
+
+**/
+
+EFI_STATUS
+EFIAPI
+UefiBootServicesTableLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ //
+ // Cache the Image Handle
+ //
+ gImageHandle = ImageHandle;
+ ASSERT (gImageHandle != NULL);
+
+ //
+ // Cache pointer to the EFI System Table
+ //
+ gST = SystemTable;
+ ASSERT (gST != NULL);
+
+ //
+ // Cache pointer to the EFI Boot Services Table
+ //
+ gBS = SystemTable->BootServices;
+ ASSERT (gBS != NULL);
+
+ return EFI_SUCCESS;
+}
+```
+
+This is the decompiler output that we saw in Ghidra! Here it is one more time for comparision:
+```cpp
+EFI_STATUS ModuleEntryPoint(EFI_HANDLE ImageHandle,EFI_SYSTEM_TABLE *SystemTable)
+
+{
+ gImageHandle_37 = ImageHandle;
+ if (ImageHandle == (EFI_HANDLE)0x0) {
+ FUN_80000e0b();
+ }
+ gST_36 = SystemTable;
+ if (SystemTable == (EFI_SYSTEM_TABLE *)0x0) {
+ FUN_80000e0b();
+ }
+ gBS_35 = SystemTable->BootServices;
+ if (gBS_35 == (EFI_BOOT_SERVICES *)0x0) {
+ FUN_80000e0b();
+ }
+ return 0;
+}
+```
+
+You can see that the decompiler did a good job, one code practicaly matches the one.
+
+Off course it is not the exact match. For example the C preprocessor has substituted `EFI_SUCCESS` and `NULL` defines with 0, so this information was lost in the compilation step, and the decompiler can't do anything about it.
+
+Also instead of the `ASSERT` define statement there are actual functions `FUN_80000e0b()`. You can double-click it in the decompiler window to check the implementation, but beware that it is not a small function.
+
+From this example you can also see how the the compiler optimizes the empty functions or performs tail-call optimizations.
+
+But all and all the general code flow is pretty understandable and matches the original source code.
+
+# `SimpleDriver`
+
+Let's try to decompile UEFI driver now. Use `File->Import File` menu option to load our `SimpleDriver` UEFI driver.
+
+Here is its source code:
+```cpp
+EFI_STATUS
+EFIAPI
+SimpleDriverUnload (
+ EFI_HANDLE ImageHandle
+ )
+{
+ Print(L"Bye-bye from driver!\n");
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+SimpleDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ Print(L"Hello from driver!\n");
+
+ return EFI_SUCCESS;
+}
+```
+
+ANd INF file:
+```
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = SimpleDriver
+ FILE_GUID = 384aeb18-105d-4af1-bf17-5e349e8f4d4c
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = SimpleDriverEntryPoint
+ UNLOAD_IMAGE = SimpleDriverUnload
+
+[Sources]
+ SimpleDriver.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiLib
+```
+
+And here is what you could see in the decompiler:
+```cpp
+EFI_STATUS ModuleEntryPoint(EFI_HANDLE ImageHandle,EFI_SYSTEM_TABLE *SystemTable)
+
+{
+ char *pcVar1;
+ EFI_GUID *SystemTable_00;
+ undefined8 in_R9;
+ void *local_20;
+
+ gImageHandle_136 = ImageHandle;
+ if (ImageHandle == (EFI_HANDLE)0x0) {
+ FUN_8000108b();
+ }
+ gST_139 = SystemTable;
+ if (SystemTable == (EFI_SYSTEM_TABLE *)0x0) {
+ FUN_8000108b();
+ }
+ gBS_138 = SystemTable->BootServices;
+ if (gBS_138 == (EFI_BOOT_SERVICES *)0x0) {
+ FUN_8000108b();
+ }
+ gRS_137 = SystemTable->RuntimeServices;
+ if (gRS_137 == (EFI_RUNTIME_SERVICES *)0x0) {
+ FUN_8000108b();
+ }
+ SystemTable_00 = &EFI_LOADED_IMAGE_PROTOCOL_GUID;
+ pcVar1 = (char *)(*gBS_138->HandleProtocol)(ImageHandle,&EFI_LOADED_IMAGE_PROTOCOL_GUID,&local_20)
+ ;
+ if ((longlong)pcVar1 < 0) {
+ FUN_800011c0();
+ pcVar1 = s_!(((INTN)(RETURN_STATUS)(Status)_80001a92;
+ SystemTable_00 = (EFI_GUID *)0x74;
+ FUN_8000108b();
+ }
+ *(undefined **)((longlong)local_20 + 0x58) = &LAB_8000140a;
+ FUN_80001259((byte *)u_Hello_from_driver!_80001cb6,(EFI_SYSTEM_TABLE *)SystemTable_00,pcVar1,in_R9
+ );
+ return 0;
+}
+```
+
+Once again we need to start from the entry point. For the driver it is the [`UefiDriverEntryPoint`](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf). Here is how it defines the `_ModuleEntryPoint`:
+
+```cpp
+EFI_STATUS
+EFIAPI
+_ModuleEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+
+ if (_gUefiDriverRevision != 0) {
+ //
+ // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the driver
+ //
+ if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+ }
+
+ //
+ // Call constructor for all libraries
+ //
+ ProcessLibraryConstructorList (ImageHandle, SystemTable);
+
+ //
+ // Install unload handler...
+ //
+ if (_gDriverUnloadImageCount != 0) {
+ Status = gBS->HandleProtocol (
+ ImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&LoadedImage
+ );
+ ASSERT_EFI_ERROR (Status);
+ LoadedImage->Unload = _DriverUnloadHandler;
+ }
+
+ //
+ // Call the driver entry point
+ //
+ Status = ProcessModuleEntryPointList (ImageHandle, SystemTable);
+
+ //
+ // If all of the drivers returned errors, then invoke all of the library destructors
+ //
+ if (EFI_ERROR (Status)) {
+ ProcessLibraryDestructorList (ImageHandle, SystemTable);
+ }
+
+ //
+ // Return the cummalative return status code from all of the driver entry points
+ //
+ return Status;
+}
+```
+
+As before let's look at the `AutoGen.c` file (`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/SimpleDriver/SimpleDriver/DEBUG/AutoGen.c`):
+```cpp
+RETURN_STATUS
+EFIAPI
+PlatformDebugLibIoPortConstructor (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+UefiBootServicesTableLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+EFI_STATUS
+EFIAPI
+UefiRuntimeServicesTableLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+EFI_STATUS
+EFIAPI
+UefiLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+
+VOID
+EFIAPI
+ProcessLibraryConstructorList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PlatformDebugLibIoPortConstructor ();
+ ASSERT_RETURN_ERROR (Status);
+
+ Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = UefiRuntimeServicesTableLibConstructor (ImageHandle, SystemTable);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = UefiLibConstructor (ImageHandle, SystemTable);
+ ASSERT_EFI_ERROR (Status);
+
+}
+
+
+
+VOID
+EFIAPI
+ProcessLibraryDestructorList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+
+}
+
+const UINT32 _gUefiDriverRevision = 0x00000000U;
+const UINT32 _gDxeRevision = 0x00000000U;
+
+
+EFI_STATUS
+EFIAPI
+ProcessModuleEntryPointList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+
+{
+ return SimpleDriverEntryPoint (ImageHandle, SystemTable);
+}
+
+...
+
+GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gDriverUnloadImageCount = 1U;
+
+
+EFI_STATUS
+EFIAPI
+ProcessModuleUnloadList (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ return SimpleDriverUnload (ImageHandle);
+}
+```
+
+Once again the `if (_gUefiDriverRevision != 0)` check is optimized since its value is known at the compilation step.
+
+As we already know the `ProcessLibraryConstructorList(...)` function inserts all the library constructor codes. In our case it is the code for the `UefiBootServicesTableLib` library code that is used in the [`UefiDriverEntryPoint.inf` itself](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf), the `UefiRuntimeServicesTableLib` library code that is used by the [`UefiLib`](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiLib/UefiLib.inf) that we've included to our driver to be able to use the `Print` function, and the `UefiLib` library constructor itself. But since the last one is basically an empty function ([https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiLib/UefiLib.c](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiLib/UefiLib.c)):
+```
+EFI_STATUS
+EFIAPI
+UefiLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EFI_SUCCESS;
+}
+```
+it is optimized by the compiler.
+
+Therefore the library construction code gives us the following decompiler output:
+```cpp
+ gImageHandle_136 = ImageHandle;
+ if (ImageHandle == (EFI_HANDLE)0x0) {
+ FUN_8000108b();
+ }
+ gST_139 = SystemTable;
+ if (SystemTable == (EFI_SYSTEM_TABLE *)0x0) {
+ FUN_8000108b();
+ }
+ gBS_138 = SystemTable->BootServices;
+ if (gBS_138 == (EFI_BOOT_SERVICES *)0x0) {
+ FUN_8000108b();
+ }
+ gRS_137 = SystemTable->RuntimeServices;
+ if (gRS_137 == (EFI_RUNTIME_SERVICES *)0x0) {
+ FUN_8000108b();
+ }
+```
+
+After the `ProcessLibraryConstructorList(...)` there is a `HandleProtocol` call:
+```cpp
+if (_gDriverUnloadImageCount != 0) {
+ Status = gBS->HandleProtocol (
+ ImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&LoadedImage
+ );
+ ASSERT_EFI_ERROR (Status);
+ LoadedImage->Unload = _DriverUnloadHandler;
+}
+```
+
+Once again the check for global variable (`_gDriverUnloadImageCount` in this case) is optimized by the compiler:
+```cpp
+ char *pcVar1;
+ EFI_GUID *SystemTable_00;
+ void *local_20;
+ ...
+
+ SystemTable_00 = &EFI_LOADED_IMAGE_PROTOCOL_GUID;
+ pcVar1 = (char *)(*gBS_138->HandleProtocol)(ImageHandle,&EFI_LOADED_IMAGE_PROTOCOL_GUID,&local_20)
+ ;
+ if ((longlong)pcVar1 < 0) {
+ FUN_800011c0();
+ pcVar1 = s_!(((INTN)(RETURN_STATUS)(Status)_80001a92;
+ SystemTable_00 = (EFI_GUID *)0x74;
+ FUN_8000108b();
+ }
+ *(undefined **)((longlong)local_20 + 0x58) = &LAB_8000140a
+```
+
+The second parameter of the `HandleProtocol` function probably should be the `SystemTable_00` instead of the `EFI_LOADED_IMAGE_PROTOCOL_GUID`, this is probably a `efiSeek` plugin mistake. Everything under the `((longlong)pcVar1 < 0)` is the `ASSERT_EFI_ERROR (Status)` check, so don't pay very much attention to it. The `local_20` variable is our `EFI_LOADED_IMAGE_PROTOCOL *LoadedImage` variable, and `local_20 + 0x58` is just an address for the `LoadedImage->Unload` function. If you want to verify it, just check the definition for the `EFI_LOADED_IMAGE_PROTOCOL` and calculate the offset to its `Unload` field.
+```cpp
+typedef struct {
+ UINT32 Revision;
+ EFI_HANDLE ParentHandle;
+ EFI_System_Table *SystemTable;
+
+ // Source location of the image
+ EFI_HANDLE DeviceHandle;
+ EFI_DEVICE_PATH_PROTOCOL *FilePath;
+ VOID *Reserved;
+
+ // Image’s load options
+ UINT32 LoadOptionsSize;
+ VOID *LoadOptions;
+
+ // Location where image was loaded
+ VOID *ImageBase;
+ UINT64 ImageSize;
+ EFI_MEMORY_TYPE ImageCodeType;
+ EFI_MEMORY_TYPE ImageDataType;
+ EFI_IMAGE_UNLOAD Unload;
+} EFI_LOADED_IMAGE_PROTOCOL;
+```
+
+Therefore the `LAB_8000140a` should be our `SimpleDriverUnload` function. You can double-click to the `LAB_8000140a` to jump to its definition and actually see the content of our `SimpleDriverUnload` function:
+```cpp
+undefined8
+UndefinedFunction_8000140a
+ (undefined8 param_1,EFI_SYSTEM_TABLE *param_2,undefined8 param_3,undefined8 param_4)
+
+{
+ FUN_80001259((byte *)u_Bye-bye_from_driver!_80001b14,param_2,param_3,param_4);
+ return 0;
+}
+```
+It is pretty obvious that the `FUN_80001259` is the `Print` function.
+
+Finally the last string of the decompiler output is from the source code:
+```
+ //
+ // Call the driver entry point
+ //
+ Status = ProcessModuleEntryPointList (ImageHandle, SystemTable);
+
+ ...
+
+ return Status;
+```
+
+Here you can see the the compiler simply has inserted the `Print` statement from the entry point of our driver:
+```cpp
+ FUN_80001259((byte *)u_Hello_from_driver!_80001cb6,(EFI_SYSTEM_TABLE *)SystemTable_00,pcVar1,in_R9);
+ return 0;
+```
+
+That is all. We've covered all the decompiler output. Once again you could see how heavily tail-call/inline optimizations are used by the compiler and how it optimizes all the empty functions.
+
+# SimpleShellApp
+
+Let's try to decompile some application with a Shell entry point. As a simple example we can use our `SimpleShellApp.efi` application. It has the following `*.c` file:
+```cpp
+INTN EFIAPI ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
+{
+ gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
+ Print(L"Bye!\n");
+
+ for (UINTN i=Argc; i>0; i--) {
+ Print(L"Arg[%d]=%s\n", Argc-i, Argv[Argc-i]);
+ }
+ return 0;
+}
+```
+
+And a following INF file:
+```
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = SimpleShellApp
+ FILE_GUID = 2afd1202-545e-4f8d-b8fb-bc179e84ddc8
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = ShellCEntryLib
+
+[Sources]
+ SimpleShellApp.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ UefiLib
+ ShellCEntryLib
+```
+
+The decompiler output for such module looks like this:
+```cpp
+EFI_STATUS ModuleEntryPoint(EFI_HANDLE ImageHandle,EFI_SYSTEM_TABLE *SystemTable)
+
+{
+ EFI_STATUS EVar1;
+ EFI_SYSTEM_TABLE *ImageHandle0;
+ EFI_SYSTEM_TABLE *SystemTable93;
+ EFI_SYSTEM_TABLE ***Interface;
+ EFI_HANDLE pvVar2;
+ EFI_SYSTEM_TABLE **local_28;
+ EFI_SYSTEM_TABLE **local_20;
+
+ gImageHandle_152 = ImageHandle;
+ if (ImageHandle == (EFI_HANDLE)0x0) {
+ FUN_8000108b();
+ }
+ gST_154 = SystemTable;
+ if (SystemTable == (EFI_SYSTEM_TABLE *)0x0) {
+ FUN_8000108b();
+ }
+ gBS_151 = SystemTable->BootServices;
+ if (gBS_151 == (EFI_BOOT_SERVICES *)0x0) {
+ FUN_8000108b();
+ }
+ gRS_153 = SystemTable->RuntimeServices;
+ if (gRS_153 == (EFI_RUNTIME_SERVICES *)0x0) {
+ FUN_8000108b();
+ }
+ Interface = &local_28;
+ local_28 = (EFI_SYSTEM_TABLE **)0x0;
+ local_20 = (EFI_SYSTEM_TABLE **)0x0;
+ pvVar2 = ImageHandle;
+ EVar1 = (*SystemTable->BootServices->OpenProtocol)
+ (ImageHandle,&EFI_SHELL_PARAMETERS_PROTOCOL_GUID,Interface,ImageHandle,
+ (EFI_HANDLE)0x0,2);
+ if ((longlong)EVar1 < 0) {
+ Interface = &local_20;
+ EVar1 = (*SystemTable->BootServices->OpenProtocol)
+ (ImageHandle,&SHELL_INTERFACE_PROTOCOL_GUID,Interface,ImageHandle,
+ (EFI_HANDLE)0x0,2);
+ if ((longlong)EVar1 < 0) {
+ FUN_8000108b();
+ return 0xffffffffffffffff;
+ }
+ SystemTable93 = local_20[2];
+ ImageHandle0 = local_20[3];
+ }
+ else {
+ ImageHandle0 = local_28[1];
+ SystemTable93 = *local_28;
+ ImageHandle = pvVar2;
+ }
+ EVar1 = FUN_8000140a(ImageHandle0,SystemTable93,Interface,ImageHandle);
+ return EVar1;
+}
+```
+
+Once again let's start from the entry point. In this case it is `ShellCEntryLib`. But if you'll look at its INF file you will see that it is just a wrapper around the `UefiApplicationEntryPoint` [(`https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf`)](https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf).
+
+We've already investigated the `UefiApplicationEntryPoint` entry point and know that it calls all the library constructors at the beginning. So this decompiler output is produced from that part:
+```cpp
+gImageHandle_152 = ImageHandle;
+if (ImageHandle == (EFI_HANDLE)0x0) {
+ FUN_8000108b();
+}
+gST_154 = SystemTable;
+if (SystemTable == (EFI_SYSTEM_TABLE *)0x0) {
+ FUN_8000108b();
+}
+gBS_151 = SystemTable->BootServices;
+if (gBS_151 == (EFI_BOOT_SERVICES *)0x0) {
+ FUN_8000108b();
+}
+gRS_153 = SystemTable->RuntimeServices;
+if (gRS_153 == (EFI_RUNTIME_SERVICES *)0x0) {
+ FUN_8000108b();
+}
+```
+Nothing new here for us, so let's move next. In case you don't remember after the library constructors the `UefiApplicationEntryPoint` calls the `ProcessModuleEntryPointList` function.
+
+And if you'll look at the `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/SimpleShellApp/SimpleShellApp/DEBUG/AutoGen.c` you will see how this call connects to the `ShellCEntryLib` entry point:
+```cpp
+EFI_STATUS
+EFIAPI
+ProcessModuleEntryPointList (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+
+{
+ return ShellCEntryLib (ImageHandle, SystemTable);
+}
+```
+
+The implementation of the `ShellCEntryLib` is provided in the [`ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c`](https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c):
+```cpp
+EFI_STATUS
+EFIAPI
+ShellCEntryLib (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ INTN ReturnFromMain;
+ EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol;
+ EFI_SHELL_INTERFACE *EfiShellInterface;
+ EFI_STATUS Status;
+
+ ReturnFromMain = -1;
+ EfiShellParametersProtocol = NULL;
+ EfiShellInterface = NULL;
+
+ Status = SystemTable->BootServices->OpenProtocol (
+ ImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ (VOID **)&EfiShellParametersProtocol,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // use shell 2.0 interface
+ //
+ ReturnFromMain = ShellAppMain (
+ EfiShellParametersProtocol->Argc,
+ EfiShellParametersProtocol->Argv
+ );
+ } else {
+ //
+ // try to get shell 1.0 interface instead.
+ //
+ Status = SystemTable->BootServices->OpenProtocol (
+ ImageHandle,
+ &gEfiShellInterfaceGuid,
+ (VOID **)&EfiShellInterface,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // use shell 1.0 interface
+ //
+ ReturnFromMain = ShellAppMain (
+ EfiShellInterface->Argc,
+ EfiShellInterface->Argv
+ );
+ } else {
+ ASSERT (FALSE);
+ }
+ }
+
+ return ReturnFromMain;
+}
+```
+
+This looks similar to the decompiler output:
+```cpp
+EFI_STATUS EVar1;
+EFI_SYSTEM_TABLE *ImageHandle0;
+EFI_SYSTEM_TABLE *SystemTable93;
+EFI_SYSTEM_TABLE ***Interface;
+EFI_HANDLE pvVar2;
+EFI_SYSTEM_TABLE **local_28;
+EFI_SYSTEM_TABLE **local_20;
+
+...
+
+Interface = &local_28;
+local_28 = (EFI_SYSTEM_TABLE **)0x0;
+local_20 = (EFI_SYSTEM_TABLE **)0x0;
+pvVar2 = ImageHandle;
+EVar1 = (*SystemTable->BootServices->OpenProtocol)
+ (ImageHandle,&EFI_SHELL_PARAMETERS_PROTOCOL_GUID,Interface,ImageHandle,
+ (EFI_HANDLE)0x0,2);
+if ((longlong)EVar1 < 0) {
+ Interface = &local_20;
+ EVar1 = (*SystemTable->BootServices->OpenProtocol)
+ (ImageHandle,&SHELL_INTERFACE_PROTOCOL_GUID,Interface,ImageHandle,
+ (EFI_HANDLE)0x0,2);
+ if ((longlong)EVar1 < 0) {
+ FUN_8000108b();
+ return 0xffffffffffffffff;
+ }
+ SystemTable93 = local_20[2];
+ ImageHandle0 = local_20[3];
+}
+else {
+ ImageHandle0 = local_28[1];
+ SystemTable93 = *local_28;
+ ImageHandle = pvVar2;
+}
+EVar1 = FUN_8000140a(ImageHandle0,SystemTable93,Interface,ImageHandle);
+return EVar1;
+```
+
+If you'll compare this output with the original source code you can identify some decompiler flaws.
+
+For example it is pretty obvious that `local_28` should be the type of `EFI_SHELL_PARAMETERS_PROTOCOL*` and `local_20` should be the type of `EFI_SHELL_INTERFACE*`. But unfortunately Ghidra's `efiSeek` plugin is not aware of such types. Moreover for some reason it tends to insert `EFI_SYSTEM_TABLE*` where it can instead of something like `VOID*`. So don't get confused with the `EFI_SYSTEM_TABLE's` everywhere. The same is for the `ImageHandle` and `SystemTable`. The decompiler can give such prefixes to the variables totally unrelated to the `ImageHandle` or `SystemTable`. For example the `ImageHandle0` and `SystemTable93` in the output is just `UINTN Argc`/`CHAR16 **Argv` arguments for our console application. So sometimes it can even be beneficial to turn off the `efiSeek` plugin and work with just the native types:
+```cpp
+undefined8 entry(longlong param_1,longlong param_2)
+
+{
+ longlong lVar1;
+ longlong lVar2;
+ undefined8 uVar3;
+ ulonglong *puVar4;
+ longlong **pplVar5;
+ longlong *local_28;
+ longlong *local_20;
+
+ <...>
+
+ pplVar5 = &local_28;
+ local_28 = (longlong *)0x0;
+ local_20 = (longlong *)0x0;
+ lVar2 = param_1;
+ lVar1 = (**(code **)(*(longlong *)(param_2 + 0x60) + 0x118))
+ (param_1,&DAT_00002150,pplVar5,param_1,0,2);
+ if (lVar1 < 0) {
+ pplVar5 = &local_20;
+ lVar2 = (**(code **)(*(longlong *)(param_2 + 0x60) + 0x118))(param_1,&.data,pplVar5,param_1,0,2)
+ ;
+ if (lVar2 < 0) {
+ FUN_0000108b();
+ return 0xffffffffffffffff;
+ }
+ lVar1 = local_20[2];
+ puVar4 = (ulonglong *)local_20[3];
+ }
+ else {
+ puVar4 = (ulonglong *)local_28[1];
+ lVar1 = *local_28;
+ param_1 = lVar2;
+ }
+ uVar3 = FUN_0000140a(puVar4,lVar1,pplVar5,param_1);
+ return uVar3;
+}
+```
+
+Another strange thing that you can notice is an array access like `local_20[3]`. To understand it don't think of `local_20` as a pointer to a protocol, but just a pointer to memory (aka `VOID*`). And replace array access with an offset, i.e.:
+```
+local_20[3] = (void*)local_20 + sizeof(void*)*3
+```
+
+To understand the origin of the actual offsets you need to look at the definitions of the [`EFI_SHELL_PARAMETERS_PROTOCOL`](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/ShellParameters.h) and [`EFI_SHELL_INTERFACE`(https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Protocol/EfiShellInterface.h):
+
+```cpp
+typedef struct _EFI_SHELL_PARAMETERS_PROTOCOL {
+ CHAR16 **Argv;
+ UINTN Argc;
+ SHELL_FILE_HANDLE StdIn;
+ SHELL_FILE_HANDLE StdOut;
+ SHELL_FILE_HANDLE StdErr;
+} EFI_SHELL_PARAMETERS_PROTOCOL;
+```
+```cpp
+typedef struct {
+ EFI_HANDLE ImageHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *Info;
+ CHAR16 **Argv;
+ UINTN Argc;
+ CHAR16 **RedirArgv;
+ UINTN RedirArgc;
+ EFI_FILE_PROTOCOL *StdIn;
+ EFI_FILE_PROTOCOL *StdOut;
+ EFI_FILE_PROTOCOL *StdErr;
+ EFI_SHELL_ARG_INFO *ArgInfo;
+ BOOLEAN EchoOn;
+} EFI_SHELL_INTERFACE;
+```
+If you'll look closely to these definition you'll see that these offsets is just a way to get to the `**Argv`/`Argc` fields.
+
+Finally let's click to the `FUN_8000140a` at the end of the decompiler output to jump to the code that actually looks like our program:
+```cpp
+undefined8
+FUN_8000140a(EFI_HANDLE ImageHandle0,EFI_SYSTEM_TABLE *SystemTable93,undefined8 param_3,
+ undefined8 param_4)
+
+{
+ wchar_t *SystemTable;
+ EFI_SYSTEM_TABLE *SystemTable_00;
+
+ SystemTable = u_Hello_again!_80001ba4;
+ (*gST_154->ConOut->OutputString)(gST_154->ConOut,(CHAR16 *)u_Hello_again!_80001ba4);
+ FUN_80001259((byte *)u_Bye!_80001bc0,(EFI_SYSTEM_TABLE *)SystemTable,param_3,param_4);
+ for (SystemTable_00 = (EFI_SYSTEM_TABLE *)0x0; (EFI_SYSTEM_TABLE *)ImageHandle0 != SystemTable_00;
+ SystemTable_00 = (EFI_SYSTEM_TABLE *)((longlong)&(SystemTable_00->Hdr).Signature + 1)) {
+ FUN_80001259((byte *)u_Arg[%d]=%s_80001bcc,SystemTable_00,
+ (&(SystemTable93->Hdr).Signature)[(longlong)SystemTable_00],param_4);
+ }
+ return 0;
+}
+```
+
+Don't get shocked by the `for` condition statement:
+```cpp
+for (SystemTable_00 = (EFI_SYSTEM_TABLE *)0x0; (EFI_SYSTEM_TABLE *)ImageHandle0 != SystemTable_00; SystemTable_00 = (EFI_SYSTEM_TABLE *)((longlong)&(SystemTable_00->Hdr).Signature + 1))
+```
+This is once again have happend because the decompiler incorrectly guessed the `EFI_SYSTEM_TABLE` type.
+
+If you'll look at the same statement without the `efiSeek` analyzer it would make much more sense:
+```cpp
+for (puVar2 = (ulonglong *)0x0; param_1 != puVar2; puVar2 = (ulonglong *)((longlong)puVar2 + 1))
+```
+
+I hope this simple example gave you some idea about the decompiler flaws and possible complications that you can encounter on your reverse engeneering journey.
+
+# OptionROM
+
+By default Ghidra is unable to debug OptionROMs. But this functionality can be added via the [ghidra-firmware-utils](https://github.com/al3xtjames/ghidra-firmware-utils) plugin.
+
+So let's try to download it and see it in action.
+
+The installation process is pretty similar to the `efiSeek` plugin installation. First we have to download and build plugin:
+```
+$ git clone https://github.com/al3xtjames/ghidra-firmware-utils.git
+$ cd ghidra-firmware-utils/
+$ GHIDRA_INSTALL_DIR=~/ghidra/ghidra_11.0.1_PUBLIC ./gradlew # set your path to the GHIDRA_INSTALL_DIR
+$ cp dist/ghidra_11.0.1_PUBLIC_20240305_ghidra-firmware-utils.zip ../ghidra_11.0.1_PUBLIC/Extensions/Ghidra/
+```
+Now re-launch Ghidra and go to the `File->Install Extensions` menu.
+
+`ghidra-firmware-utils` should be listed in the possible plugins:
+
+![ghidra-firmware-utils1](ghidra-firmware-utils1.png)
+
+Select it and click `OK` to install the plugin. After that restart Ghidra one more time.
+
+As a test example let's take `pc-bios/efi-pcnet.rom` OptionROM from the `qemu` build. If you import this file to Ghidra now you would be prompted with the following window:
+
+![ghidra-firmware-utils2](ghidra-firmware-utils2.png)
+
+Click `File System` to see the images inside the OptionROM:
+
+![ghidra-firmware-utils3](ghidra-firmware-utils3.png)
+
+As you can see both EFI and Legacy images were successfully parsed from the optionROM. And now you can debug which one you like. For that right click on the necessary image and select `Import` to add it to the project.
+
+# Alternative tools
+
+The most popular alternative framework for the reverse engeneering would probably be the [IDA Pro](https://hex-rays.com/ida-pro/). It also has some plugins that help to investigate UEFI modules. For example [efiXplorer](https://github.com/binarly-io/efiXplorer). The main disadvantage of the IDA Pro is that it is not free. That is why we've preferred Ghidra in our lessons. But if you are into reverse engeneering, you should probably check it out as well.
diff --git a/Lessons_uncategorized/Lesson_Ghidra/analyze1.png b/Lessons_uncategorized/Lesson_Ghidra/analyze1.png
new file mode 100644
index 0000000..fd89962
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/analyze1.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/analyze2.png b/Lessons_uncategorized/Lesson_Ghidra/analyze2.png
new file mode 100644
index 0000000..676d9b2
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/analyze2.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/analyze3.png b/Lessons_uncategorized/Lesson_Ghidra/analyze3.png
new file mode 100644
index 0000000..f23eb50
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/analyze3.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/efiSeek1.png b/Lessons_uncategorized/Lesson_Ghidra/efiSeek1.png
new file mode 100644
index 0000000..5ebdac9
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/efiSeek1.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/efiSeek2.png b/Lessons_uncategorized/Lesson_Ghidra/efiSeek2.png
new file mode 100644
index 0000000..16935e4
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/efiSeek2.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/efiSeek3.png b/Lessons_uncategorized/Lesson_Ghidra/efiSeek3.png
new file mode 100644
index 0000000..259f9de
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/efiSeek3.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/efiSeek4.png b/Lessons_uncategorized/Lesson_Ghidra/efiSeek4.png
new file mode 100644
index 0000000..bab5fcd
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/efiSeek4.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/efiSeek5.png b/Lessons_uncategorized/Lesson_Ghidra/efiSeek5.png
new file mode 100644
index 0000000..1e71a40
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/efiSeek5.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/fileinfo.png b/Lessons_uncategorized/Lesson_Ghidra/fileinfo.png
new file mode 100644
index 0000000..7607d56
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/fileinfo.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/fileparse.png b/Lessons_uncategorized/Lesson_Ghidra/fileparse.png
new file mode 100644
index 0000000..4e25c4f
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/fileparse.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils1.png b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils1.png
new file mode 100644
index 0000000..a0f9f86
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils1.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils2.png b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils2.png
new file mode 100644
index 0000000..81baa51
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils2.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.png b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.png
new file mode 100644
index 0000000..a0f9f86
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.png:Zone.Identifier b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.png:Zone.Identifier
new file mode 100644
index 0000000..1bf0b28
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/ghidra-firmware-utils3.png:Zone.Identifier
@@ -0,0 +1,3 @@
+[ZoneTransfer]
+ZoneId=3
+HostUrl=https://github.com/
diff --git a/Lessons_uncategorized/Lesson_Ghidra/new_project1.png b/Lessons_uncategorized/Lesson_Ghidra/new_project1.png
new file mode 100644
index 0000000..7015cd2
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/new_project1.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/new_project2.png b/Lessons_uncategorized/Lesson_Ghidra/new_project2.png
new file mode 100644
index 0000000..5505c73
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/new_project2.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/noproject.png b/Lessons_uncategorized/Lesson_Ghidra/noproject.png
new file mode 100644
index 0000000..d440843
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/noproject.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/projectwithapp.png b/Lessons_uncategorized/Lesson_Ghidra/projectwithapp.png
new file mode 100644
index 0000000..947d66d
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/projectwithapp.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Ghidra/test_project.png b/Lessons_uncategorized/Lesson_Ghidra/test_project.png
new file mode 100644
index 0000000..bc68bb0
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Ghidra/test_project.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password/README.md b/Lessons_uncategorized/Lesson_Password/README.md
new file mode 100644
index 0000000..02bc6d6
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/README.md
@@ -0,0 +1,362 @@
+In this lesson we would investigate the `password` VFR element.
+
+For that let's create a driver `PasswordForm` with the following VFR code (`UefiLessonsPkg/PasswordForm/Form.vfr`):
+```
+#include <Uefi/UefiMultiPhase.h>
+#include "Data.h"
+
+formset
+ guid = FORMSET_GUID,
+ title = STRING_TOKEN(FORMSET_TITLE),
+ help = STRING_TOKEN(FORMSET_HELP),
+
+ varstore VARIABLE_STRUCTURE,
+ name = FormData,
+ guid = STORAGE_GUID;
+
+ defaultstore StandardDefault,
+ prompt = STRING_TOKEN(STANDARD_DEFAULT_PROMPT),
+ attribute = 0x0000;
+
+ form
+ formid = 1,
+ title = STRING_TOKEN(FORMID1_TITLE);
+
+ password
+ varid = FormData.Password,
+ prompt = STRING_TOKEN(PASSWORD_PROMPT),
+ help = STRING_TOKEN(PASSWORD_HELP),
+ minsize = PASSWORD_MIN_LEN,
+ maxsize = PASSWORD_MAX_LEN,
+ endpassword;
+ endform;
+endformset;
+```
+
+As you can see we can set minimal and maximum size for the password via its `minsize`/`maxsize` fields.
+
+The values for these defines and the overall `varstore` structure as usually we would place to the header file (`UefiLessonsPkg/PasswordForm/Data.h`):
+```
+#ifndef _DATA_H_
+#define _DATA_H_
+
+#define FORMSET_GUID {0xe54b953d, 0x7ddc, 0x455c, {0x8c, 0x1a, 0x59, 0x92, 0xbb, 0xc7, 0x72, 0xc4}}
+#define DATAPATH_GUID {0xb289cf9f, 0xf911, 0x41fd, {0x9b, 0xad, 0x2c, 0x92, 0x57, 0x68, 0x86, 0x64}}
+#define STORAGE_GUID {0xe7f2d73c, 0x699a, 0x4606, {0x92, 0xb6, 0xa3, 0x5e, 0x49, 0x27, 0xc4, 0xd4}}
+
+#define PASSWORD_MIN_LEN 6
+#define PASSWORD_MAX_LEN 8
+#define PASSWORD_STORAGE_SIZE 9
+
+
+#pragma pack(1)
+typedef struct {
+ CHAR16 Password[PASSWORD_STORAGE_SIZE];
+} VARIABLE_STRUCTURE;
+#pragma pack()
+
+#endif
+```
+
+Here you can see that internally the `password` element is a simple string. Looking ahead we set the size of the array `PASSWORD_STORAGE_SIZE = (PASSWORD_MAX_LEN + 1)`. This way the password string would be stored along with the null-terminator symbol. This way it would be easier to use standard string handling function on the password data.
+
+Now let's compile our application and try to use it.
+
+Unfortunately when we try to access the `password` element we would get the following error:
+
+![password1](password1.png?raw=true "password1")
+
+So let's add `flags = INTERACTIVE` to our element. As you know with that flag our element would call `EFI_HII_CONFIG_ACCESS_PROTOCOL.Callback()` function when we would access the element (open/close its form, try to change value of the element).
+
+In our case the `password` element is the only element on the form, so all the generated callbacks would be the callbacks generated by it. But as a good tone let's also add `key = KEY_PASSWORD` to the VFR code:
+```
+password
+ varid = FormData.Password,
+ prompt = STRING_TOKEN(PASSWORD_PROMPT),
+ help = STRING_TOKEN(PASSWORD_HELP),
+ flags = INTERACTIVE,
+ key = KEY_PASSWORD,
+ minsize = PASSWORD_MIN_LEN,
+ maxsize = PASSWORD_MAX_LEN,
+endpassword;
+```
+
+Don't forget to add `#define KEY_PASSWORD 0x1234` to the `UefiLessonsPkg/PasswordForm/Data.h`.
+
+Before diving into the actual callback code let's make a short stop exploring the IFR code of the element:
+
+# `EFI_IFR_PASSWORD`
+
+The password element produces the `EFI_IFR_PASSWORD` and `EFI_IFR_END` opcodes: `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PasswordForm/PasswordForm/DEBUG/Form.lst`
+```
+ password
+>00000058: 08 91 06 00 07 00 34 12 01 00 00 00 04 06 00 08 00
+ varid = FormData.Password,
+ prompt = STRING_TOKEN(0x0006),
+ help = STRING_TOKEN(0x0007),
+ flags = INTERACTIVE,
+ key = 0x1234,
+ minsize = 6,
+ maxsize = 8,
+ endpassword;
+>00000069: 29 02
+```
+
+Here is a definition for the `EFI_IFR_PASSWORD`:
+```
+EFI_IFR_PASSWORD
+
+Summary:
+Creates a password question
+
+Prototype:
+#define EFI_IFR_PASSWORD_OP 0x08
+
+typedef struct _EFI_IFR_PASSWORD {
+ EFI_IFR_OP_HEADER Header;
+ EFI_IFR_QUESTION_HEADER Question;
+ UINT16 MinSize;
+ UINT16 MaxSize;
+} EFI_IFR_PASSWORD;
+
+Members:
+Header The sequence that defines the type of opcode as well as the length of the opcode being defined.
+ Header.OpCode = EFI_IFR_PASSWORD_OP
+Question The standard question header
+MinSize The minimum number of characters that can be accepted for this opcode
+MaxSize The maximum number of characters that can be accepted for this opcode
+
+Description:
+Creates a password question in the current form.
+```
+
+We've covered `EFI_IFR_OP_HEADER` and `EFI_IFR_QUESTION_HEADER` many times, and `MinSize`/`MaxSize` are pretty trivial. So there is nothing new for us here. But it is good to know that we understand the underlying IFR code now.
+
+# Callback code
+
+Now we need to write an implementation for the `EFI_HII_CONFIG_ACCESS_PROTOCOL.CallBack()` to correctly handle our password element.
+
+This element is kind of special and UEFI specification even defines a dedicated flowchart about how the browser should communicate with the driver having the `password` element:
+
+![flowchart](flowchart.png?raw=true "flowchart")
+
+The specification also has `part two` of the flowchart, but currently it seems like is not supported in the edk2 code. The `part two` is for `password` elements without the `flags = INTERACTIVE`, but as you saw earlier the Form Browser simply outputs an error for such cases.
+
+Before we would write the necessary callback code we need to understand when and how the Form Browser will call `Callback()`.
+
+Here is couple of observations regarding the Form Browser:
+
+- When we open the form `Callback()` function is called 2 times for the password element, one with the `Action=EFI_BROWSER_ACTION_FORM_OPEN` argument and one with the `Action=EFI_BROWSER_ACTION_RETRIEVE` argument, in both cases the passed agument `Value` would be the string token pointing to the empty string "":
+```
+EFI_BROWSER_ACTION_FORM_OPEN (Value="")
+EFI_BROWSER_ACTION_RETRIEVE (Value="")
+```
+This is even true when the password has some value, the Form Browser still woudn't pass it to `Callback()`. This is a security measure.
+
+- When we close the form `Callback()` function is called 1 time, also with an empty string:
+```
+EFI_BROWSER_ACTION_FORM_CLOSE (Value="")
+```
+
+- All the rest calls of the `Callback()` function would happen with the `Action=EFI_BROWSER_ACTION_CHANGING` argument.
+
+
+We don't need to do anything special in the `EFI_BROWSER_ACTION_FORM_OPEN`/`EFI_BROWSER_ACTION_RETRIEVE` callbacks, so all our handling code can be placed inside the `EFI_BROWSER_ACTION_CHANGING` action handling:
+```cpp
+STATIC
+EFI_STATUS
+EFIAPI
+Callback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN OUT EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ if ((QuestionId == KEY_PASSWORD) && (Action == EFI_BROWSER_ACTION_CHANGING)) {
+ <...>
+ }
+
+ return EFI_UNSUPPORTED;
+}
+```
+
+Now to the actual logic. According to the UEFI specification password flowchart when you access the password element (i.e. try to change it) first the browser asks if the password is already set. For that the form browser calls the `Callback()` function with a `Value` string token pointing to an empty string. In the edk2 code the Form Browser uses `Action=EFI_BROWSER_ACTION_CHANGING` for that action.
+
+Let's start with a case when the password is not yet set. Then the `Callback()` code should return `EFI_SUCCESS` for the above call.
+
+After that the Form Browser prompts user to type new password:
+
+![password2](password2.png?raw=true "password2")
+
+If the input is valid the Form Browser prompts again for password confirmation:
+
+![password3](password3.png?raw=true "password3")
+
+If the user input is incorrect (user has typed password with a length less than password `minsize`, or user has typed the second time different password) the Form Broswer calls the `Callback()` function with the `Value` equal to 0. Keep in mind that since in this case `Value` is a string token, you shouldn't even try to get an actual string from that with the `HiiGetString` call.
+
+Here is a flowchart describing possible form browser calls:
+```
+ EFI_BROWSER_ACTION_CHANGING (Value="")
+[Callback returns EFI_SUCCESS to say that password is not set yet]
+ |
+ "Please type in your new password"
+ ----------------------------------------------------------------------------
+ | |
+ "Please confirm your new password" "Please enter enough characters"
+ | EFI_BROWSER_ACTION_CHANGING (Token=ZERO)
+ |
+ ----------------------------------------------------------------------------------------------------------------------------------
+ | | |
+ EFI_BROWSER_ACTION_CHANGING (Value="<USER INPUT>") "Please enter enough characters" "Passwords are not the same"
+ [Callback sets password and returns EFI_SUCCESS] EFI_BROWSER_ACTION_CHANGING (Token=ZERO) EFI_BROWSER_ACTION_CHANGING (Token=ZERO)
+
+```
+
+Here is a minimal code to support that logic:
+```cpp
+EFI_STATUS HandlePasswordInput(EFI_STRING Password)
+{
+ if (Password[0] == 0) {
+ // Form Browser checks if password exists
+ if (StrLen(FormStorage.Password) != 0) {
+ //TODO
+ }
+ return EFI_SUCCESS;
+ }
+
+ // Form Browser sends 'new passwd' to set
+
+ if (FormStorage.Password[0] == 0) {
+ StrnCpyS(FormStorage.Password, PASSWORD_STORAGE_SIZE, Password, StrLen(Password));
+ return EFI_SUCCESS;
+ }
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+Callback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN OUT EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ EFI_STATUS Status;
+ if ((QuestionId == KEY_PASSWORD) && (Action == EFI_BROWSER_ACTION_CHANGING)) {
+ if (Value->string == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ EFI_STRING Password = HiiGetString(mHiiHandle, Value->string, "en-US");
+ Status = HandlePasswordInput(Password);
+ FreePool(Password);
+ return Status;
+ }
+
+ return EFI_UNSUPPORTED;
+}
+```
+
+Now we can successfully set initial value for the password from the browser. So it it time to investigate the Form Browser `Callback()` calls for the case when password is already set.
+
+As before, when we try to access the password element, first the browser would ask if the password is already set. But this time we should return an error. We would return `EFI_ALREADY_STARTED` (other errors are possible, but let's stick with that).
+
+After that the Form Browser will ask user to input the current password.
+
+![password4](password4.png?raw=true "password4")
+
+If it has enough characters the browser will pass the input to the `Callback()`. The `Callback()` code should check if the input is equal to the old password. If everything is ok, the code should return `EFI_SUCCESS`.
+
+After that the Form Browser will ask user to type new password:
+
+![password5](password5.png?raw=true "password5")
+
+And if everything correct to confirm it:
+
+![password6](password6.png?raw=true "password6")
+
+The logic is the same as earlier. If the user input is incorrect (user has typed password with a length less than password `minsize`, or user typed the second time different password) the Form Broswer calls the `Callback()` function with the `Value` equal to 0.
+
+Once again here is a flowchart for better understanding:
+```
+ EFI_BROWSER_ACTION_CHANGING (Value="")
+[Callback should return EFI_ALREADY_STARTED to say the password is set]
+ |
+ "Please type in your password"
+ |
+ ----------------------------------------------------------------------------
+ | |
+ EFI_BROWSER_ACTION_CHANGING (Value="<USER INPUT>") "Please enter enough characters"
+[Callback checks if <USER INPUT> equal to current password]
+[and returns EFI_SUCCESS on match, EFI_NOT_READY on fail]
+ |
+ ---------------------------------------------------------------------------
+ | |
+ "Please type in your new password" "Incorrect password"
+ |
+ ----------------------------------------------------------------------------
+ | |
+ "Please confirm your new password" "Please enter enough characters"
+ | EFI_BROWSER_ACTION_CHANGING (Token=ZERO)
+ |
+ ----------------------------------------------------------------------------------------------------------------------------------
+ | | |
+ EFI_BROWSER_ACTION_CHANGING (Value="<USER INPUT>") "Please enter enough characters" "Passwords are not the same"
+ [Callback sets password and returns EFI_SUCCESS] EFI_BROWSER_ACTION_CHANGING (Token=ZERO) EFI_BROWSER_ACTION_CHANGING (Token=ZERO)
+```
+
+Since now the Form Browser can send non-empty string not only to set the new password, but also to verify the old one, we need to understand our place in the flowchart for the correct input handling. For that let's create a global variable `BOOLEAN OldPasswordVerified`:
+- initially we would set this variable to `FALSE`,
+- every time the user resets the Form Browser logic typing incorrect data, we would set it to `FALSE`,
+- when the old password is verified we would set it to `TRUE`,
+- when the password is updated correctly, we would set it back to `FALSE` again.
+
+With everything in mind let's update our `HandlePasswordInput()` code:
+```cpp
+BOOLEAN OldPasswordVerified = FALSE;
+
+EFI_STATUS HandlePasswordInput(EFI_STRING Password)
+{
+ if (Password[0] == 0) {
+ // Form Browser checks if password exists
+ if (FormStorage.Password[0] != 0) {
+ return EFI_ALREADY_STARTED;
+ } else {
+ return EFI_SUCCESS;
+ }
+ } else {
+ // Form Browser sends password value
+ // It can be old password to check or initial/updated password to set
+
+ if (FormStorage.Password[0] == 0) {
+ // Set initial password
+ StrnCpyS(FormStorage.Password, PASSWORD_STORAGE_SIZE, Password, StrLen(Password));
+ return EFI_SUCCESS;
+ }
+
+ if (!OldPasswordVerified) {
+ // Check old password
+ if (StrCmp(Password, FormStorage.Password))
+ return EFI_NOT_READY;
+
+ OldPasswordVerified = TRUE;
+ return EFI_SUCCESS;
+ }
+
+ // Update password
+ StrnCpyS(FormStorage.Password, PASSWORD_STORAGE_SIZE, Password, StrLen(Password));
+ OldPasswordVerified = FALSE;
+ return EFI_SUCCESS;
+ }
+}
+```
+
+With the following code you can set initial password and also have a possibility update it via the Form Browser.
+
+In the next lesson we would investigate why we shouldn't store password as a plain text in the storage and how we can make our password storage more secure.
diff --git a/Lessons_uncategorized/Lesson_Password/flowchart.png b/Lessons_uncategorized/Lesson_Password/flowchart.png
new file mode 100644
index 0000000..7bee7e0
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/flowchart.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password/password1.png b/Lessons_uncategorized/Lesson_Password/password1.png
new file mode 100644
index 0000000..63aef82
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/password1.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password/password2.png b/Lessons_uncategorized/Lesson_Password/password2.png
new file mode 100644
index 0000000..79462d8
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/password2.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password/password3.png b/Lessons_uncategorized/Lesson_Password/password3.png
new file mode 100644
index 0000000..d951fe1
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/password3.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password/password4.png b/Lessons_uncategorized/Lesson_Password/password4.png
new file mode 100644
index 0000000..43dfeea
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/password4.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password/password5.png b/Lessons_uncategorized/Lesson_Password/password5.png
new file mode 100644
index 0000000..136eeb5
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/password5.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password/password6.png b/Lessons_uncategorized/Lesson_Password/password6.png
new file mode 100644
index 0000000..17c59b0
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password/password6.png
Binary files differ
diff --git a/Lessons_uncategorized/Lesson_Password_2/README.md b/Lessons_uncategorized/Lesson_Password_2/README.md
new file mode 100644
index 0000000..6ec75a9
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Password_2/README.md
@@ -0,0 +1,437 @@
+In the last lesson we've investigated the VFR `password` element and developed a code that was saving the password value as a plain text in the `varstore` storage. But in reality you shouldn't store passwords as it is in your storage! This is a security vulnerability. Instead you should at least store a hash value of the password. So even if the database is compromised, the attacker can't reverse the original password value.
+
+Here is a simple example. If our form has only the `password element` it wouldn't be present in the `HIIConfig dump` output. But when I've added a `checkbox` element to the form, the `HIIConfig dump` printed all the values inside the `varstorage` including the current value for the password field:
+```
+GUID=3cd7f2e79a69064692b6a35e4927c4d4 (E7F2D73C-699A-4606-92B6-A35E4927C4D4)
+NAME=0046006f0072006d0044006100740061 (FormData)
+PATH=010414009fcf89b211f9fd419bad2c92576886647fff0400 (VenHw(B289CF9F-F911-41FD-9BAD-2C9257688664))
+OFFSET=0 WIDTH=0000000000000013 VALUE=0100000074006500720063006500730079006d
+6D 00 79 00 73 00 65 00 63 00 72 00 65 00 74 00 | m.y.s.e.c.r.e.t.
+00 00 01 | ...
+
+GUID=3cd7f2e79a69064692b6a35e4927c4d4 (E7F2D73C-699A-4606-92B6-A35E4927C4D4)
+NAME=0046006f0072006d0044006100740061 (FormData)
+PATH=010414009fcf89b211f9fd419bad2c92576886647fff0400 (VenHw(B289CF9F-F911-41FD-9BAD-2C9257688664))
+ALTCFG=0000
+OFFSET=0012 WIDTH=0001 VALUE=01
+01 | .
+
+GUID=3cd7f2e79a69064692b6a35e4927c4d4 (E7F2D73C-699A-4606-92B6-A35E4927C4D4)
+NAME=0046006f0072006d0044006100740061 (FormData)
+PATH=010414009fcf89b211f9fd419bad2c92576886647fff0400 (VenHw(B289CF9F-F911-41FD-9BAD-2C9257688664))
+ALTCFG=0001
+OFFSET=0012 WIDTH=0001 VALUE=01
+01 | .
+```
+
+This is clearly no good! This is a potential loophole for the attaker to get our password. Not sure if this is a vulnerability in the edk2 code, but at least we can protect the initial password by storing it not as a simple string, but as something like SHA512 hash value of the password string.
+
+Let's investigate how we can do this.
+
+For the hash calculation UEFI specification defines the `EFI_HASH2_PROTOCOL` protocol. Before diving into its specification, let's understand how to get it.
+
+This protocol is not supposed to be acquired via the usual `LocateProtocol` routine. It is supposed to be acquired via the standard EFI service binding scheme. To get the `EFI_HASH2_PROTOCOL` you need to call `CreateChild()` on the `EFI_HASH2_SERVICE_BINDING_PROTOCOL` and perform `HandleProtocol()` on the returned handle. To clear the resources on driver unload we need to call the `DestroyChild` respectively.
+
+With the service binding scheme multiple drivers can use the hashing services. Or even one driver can have multiple simultaneous non-overlapping hash operations.
+
+So let's try to get the `EFI_HASH2_PROTOCOL` protocol:
+```cpp
+<...>
+
+#include <Protocol/Hash2.h>
+#include <Protocol/ServiceBinding.h>
+
+<...>
+
+EFI_SERVICE_BINDING_PROTOCOL* hash2ServiceBinding;
+EFI_HASH2_PROTOCOL* hash2Protocol;
+EFI_HANDLE hash2ChildHandle = NULL;
+
+<...>
+
+EFI_STATUS
+EFIAPI
+PasswordFormWithHashUnload (
+ EFI_HANDLE ImageHandle
+ )
+{
+ hash2ServiceBinding->DestroyChild(hash2ServiceBinding, hash2ChildHandle);
+
+ <...>
+}
+
+EFI_STATUS
+EFIAPI
+PasswordFormWithHashEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = gBS->LocateProtocol(&gEfiHash2ServiceBindingProtocolGuid,
+ NULL,
+ (VOID **)&hash2ServiceBinding);
+ if (EFI_ERROR(Status)) {
+ Print(L"Error! Can't locate gEfiHash2ServiceBindingProtocolGuid: %r\n", Status);
+ return Status;
+ }
+
+ Status = hash2ServiceBinding->CreateChild(hash2ServiceBinding,
+ &hash2ChildHandle);
+ if (EFI_ERROR(Status)) {
+ Print(L"Error! Can't create child on gEfiHash2ServiceBindingProtocolGuid: %r\n", Status);
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol(hash2ChildHandle,
+ &gEfiHash2ProtocolGuid,
+ (VOID **)&hash2Protocol,
+ NULL,
+ hash2ChildHandle,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
+ if (EFI_ERROR(Status)) {
+ Print(L"Error! Can't open gEfiHashProtocolGuid: %r\n", Status);
+ return Status;
+ }
+
+ <...>
+}
+```
+
+Don't forget to add the necessary protocols to the INF file:
+```
+[Protocols]
+ ...
+ gEfiHash2ServiceBindingProtocolGuid
+ gEfiHash2ProtocolGuid
+```
+
+Now to the actual `EFI_HASH2_PROTOCOL` itself:
+```
+EFI_HASH2_PROTOCOL
+
+Summary:
+This protocol describes hashing functions for which the algorithm-required message padding and finalization are performed by the supporting driver. In previous versions of the specification, the algorithms supported by EFI_HASH2_PROTOCOL were also available for use with EFI_HASH_PROTOCOL but this usage has been deprecated.
+
+GUID:
+#define EFI_HASH2_PROTOCOL_GUID { 0x55b1d734, 0xc5e1, 0x49db, 0x96, 0x47, 0xb1, 0x6a, 0xfb, 0xe, 0x30, 0x5b}
+
+typedef _EFI_HASH2_PROTOCOL {
+ EFI_HASH2_GET_HASH_SIZE GetHashSize;
+ EFI_HASH2_HASH Hash;
+ EFI_HASH2_HASH_INIT HashInit;
+ EFI_HASH2_HASH_UPDATE HashUpdate;
+ EFI_HASH2_HASH_FINAL HashFinal;
+} EFI_HASH2_PROTOCOL;
+
+Parameters:
+GetHashSize Return the result size of a specific type of resulting hash.
+Hash Create a final non-extendable hash for a single message block in a single call.
+HashInit Initializes an extendable multi-part hash calculation
+HashUpdate Continues a hash in progress by supplying the first or next sequential portion of the message text
+HashFinal Finalizes a hash in progress by padding as required by algorithm and returning the hash output.
+```
+
+In the edk2 this protocol is defined in the header [MdePkg/Include/Protocol/Hash2.h](file https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Hash2.h).
+
+In our simple case we would use the `EFI_HASH2_HASH Hash` function.
+
+If you want to know about the `HashInit`/`HashUpdate`/`HashFinal` functions, I think it is better to illustrate their work on a simple example. The 3 cases below would give you identical hash results:
+```
+Hash("ABCDEF") | HashInit() | HashInit ()
+ | HashUpdate("ABCDEF") | HashUpdate ("ABC")
+ | HashFinal() | HashUpdate ("DE")
+ | | HashUpdate ("F")
+ | | HashFinal ()
+```
+I hope this is pretty self-explanatory.
+
+Now let's see the specification for the `EFI_HASH2_HASH Hash` function that we would use:
+```
+EFI_HASH2_PROTOCOL.Hash()
+
+Summary:
+Creates a hash for a single message text. The hash is not extendable. The output is final with any algorithm-required
+padding added by the function.
+
+Prototype:
+
+EFI_STATUS
+EFIAPI
+Hash(
+ IN CONST EFI_HASH2_PROTOCOL *This,
+ IN CONST EFI_GUID *HashAlgorithm,
+ IN CONST UINT8 *Message,
+ IN UINTN MessageSize,
+ IN OUT EFI_HASH2_OUTPUT *Hash
+);
+
+Parameters:
+This Points to this instance of EFI_HASH2_PROTOCOL
+HashAlgorithm Points to the EFI_GUID which identifies the algorithm to use
+Message Points to the start of the message
+MessageSize The size of Message, in bytes
+Hash On input, points to a caller-allocated buffer of the size returned by GetHashSize() for the specified HashAlgorithm
+ On output, the buffer holds the resulting hash computed from the message
+
+Description:
+This function creates the hash of specified single block message text based on the specified algorithm HashAlgorithm and copies the result to the caller-provided buffer Hash. The resulting hash cannot be extended. All padding required by HashAlgorithm is added by the implementation.
+```
+
+The possible hash algorithms and their respected GUIDs are present below:
+```
+SHA-1: EFI_HASH_ALGORITHM_SHA1_GUID
+SHA224: EFI_HASH_ALGORITHM_SHA224_GUID
+SHA256: EFI_HASH_ALGORITHM_SHA256_GUID
+SHA384: EFI_HASH_ALGORITHM_SHA384_GUID
+SHA512: EFI_HASH_ALGORITHM_SHA512_GUID
+MD5: EFI_HASH_ALGORTIHM_MD5_GUID
+```
+
+We would use the `EFI_HASH_ALGORITHM_SHA512_GUID` as the strongest of the above in our hash calculation.
+
+The `EFI_HASH2_PROTOCOL.Hash()` returns its output in the `EFI_HASH2_OUTPUT *Hash` argument. The `EFI_HASH2_OUTPUT` type is a `enum` defined like this:
+```cpp
+typedef UINT8 EFI_MD5_HASH2[16];
+typedef UINT8 EFI_SHA1_HASH2[20];
+typedef UINT8 EFI_SHA224_HASH2[28];
+typedef UINT8 EFI_SHA256_HASH2[32];
+typedef UINT8 EFI_SHA384_HASH2[48];
+typedef UINT8 EFI_SHA512_HASH2[64];
+
+typedef union {
+ EFI_MD5_HASH2 Md5Hash;
+ EFI_SHA1_HASH2 Sha1Hash;
+ EFI_SHA224_HASH2 Sha224Hash;
+ EFI_SHA256_HASH2 Sha256Hash;
+ EFI_SHA384_HASH2 Sha384Hash;
+ EFI_SHA512_HASH2 Sha512Hash;
+} EFI_HASH2_OUTPUT;
+```
+
+In our form storage we need to create appropriate field to store the password hash. Since we need to know the size of the storage at compile time and our hash algorithm is fixed, instead of using the protocol `EFI_HASH2_GET_HASH_SIZE GetHashSize` we would just define it as a static number. From the description above you can see that the size of the SHA512 hash is 64:
+```
+#define HASHED_PASSWORD_SIZE 64
+
+#pragma pack(1)
+typedef struct {
+ UINT8 Password[HASHED_PASSWORD_SIZE];
+} VARIABLE_STRUCTURE;
+#pragma pack()
+```
+
+Here you can also observe that now `Password` field is not even a `CHAR16` string, but a `UINT8 []`.
+
+Now we are ready to create a `ComputeStringHash` function that would calculate a hash from the `EFI_STRING`:
+```cpp
+EFI_STATUS ComputeStringHash(EFI_STRING Password, UINT8* HashedPassword)
+{
+ EFI_GUID HashGuid = EFI_HASH_ALGORITHM_SHA512_GUID;
+ EFI_HASH2_OUTPUT Hash;
+ EFI_STATUS Status = hash2Protocol->Hash(hash2Protocol,
+ &HashGuid,
+ (UINT8*)Password,
+ StrLen(Password)*sizeof(CHAR16),
+ &Hash);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ CopyMem(HashedPassword, Hash.Sha512Hash, HASHED_PASSWORD_SIZE);
+ return EFI_SUCCESS;
+}
+```
+
+With it we can update our `HandlePasswordInput` function to store hashes instead of plain passwords:
+```cpp
+EFI_STATUS HandlePasswordInput(EFI_STRING Password)
+{
+ EFI_STATUS Status;
+
+ if (Password[0] == 0) {
+ // Form Browser checks if password exists
+ if (FormStorage.Password[0] != 0) {
+ return EFI_ALREADY_STARTED;
+ } else {
+ return EFI_SUCCESS;
+ }
+ } else {
+ // Form Browser sends password value
+ // It can be old password to check or initial/updated password to set
+
+ if (FormStorage.Password[0] == 0) {
+ // Set initial password
+ ComputeStringHash(Password, FormStorage.Password);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ return EFI_SUCCESS;
+ }
+
+ if (!OldPasswordVerified) {
+ // Check old password
+ UINT8 TempHash[HASHED_PASSWORD_SIZE];
+ ComputeStringHash(Password, TempHash);
+ if (CompareMem(TempHash, FormStorage.Password, HASHED_PASSWORD_SIZE))
+ return EFI_NOT_READY;
+
+ OldPasswordVerified = TRUE;
+ return EFI_SUCCESS;
+ }
+
+ // Update password
+ Status = ComputeStringHash(Password, FormStorage.Password);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ OldPasswordVerified = FALSE;
+ return EFI_SUCCESS;
+ }
+}
+```
+
+Now we are ready to build and test our driver.
+
+Unfortunately the driver would fail on load:
+```
+FS0:\> load PasswordFormWithHash.efi
+Error! Can't locate gEfiHash2ServiceBindingProtocolGuid: Not Found
+Image 'FS0:\PasswordFormWithHash.efi' error in StartImage: Not Found
+```
+
+As you can see there is no `EFI_HASH2_SERVICE_BINDING_PROTOCOL` in the default OVMF build. So let's try to fix it.
+
+The `EFI_HASH2_SERVICE_BINDING_PROTOCOL` is produced by the [Hash2DxeCrypto](https://github.com/tianocore/edk2/tree/master/SecurityPkg/Hash2DxeCrypto).
+
+Since its module type `UEFI_DRIVER` we can just build it:
+```
+build --platform=SecurityPkg/SecurityPkg.dsc --module=SecurityPkg/Hash2DxeCrypto/Hash2DxeCrypto.inf --arch=X64 --buildtarget=RELEASE --tagname=GCC5
+cp Build/SecurityPkg/RELEASE_GCC5/X64/SecurityPkg/Hash2DxeCrypto/Hash2DxeCrypto/OUTPUT/Hash2DxeCrypto.efi ~/disk
+```
+
+and load in shell:
+```
+FS0:\> load Hash2DxeCrypto.efi
+Image 'FS0:\Hash2DxeCrypto.efi' loaded at 640D000 - Success
+```
+
+Now our driver `PasswordFormWithHash` can load without any problems
+```
+FS0:\> load PasswordFormWithHash.efi
+Image 'FS0:\PasswordFormWithHash.efi' loaded at 63D8000 - Success
+```
+
+You can test that the form works with the password element the same as before.
+
+The difference is not visible to the user, but now if we repeat the exploit presented at the start of this lesson, this is what we would get:
+```
+GUID=3cd7f2e79a69064692b6a35e4927c4d4 (E7F2D73C-699A-4606-92B6-A35E4927C4D4)
+NAME=0046006f0072006d0044006100740061 (FormData)
+PATH=010414009fcf89b211f9fd419bad2c92576886647fff0400 (VenHw(B289CF9F-F911-41FD-9BAD-2C9257688664))
+OFFSET=0 WIDTH=0000000000000041 VALUE=01a71c52e40993cb53ae8fc16c3ad3c0596cc03b67a225bef0effa0f43e9f362921ce17b26f9cf7ad6ed487fa955438206d0f211bffea4e9d216eb2b0d2133882f
+2F 88 33 21 0D 2B EB 16 D2 E9 A4 FE BF 11 F2 D0 | /.3!.+..........
+06 82 43 55 A9 7F 48 ED D6 7A CF F9 26 7B E1 1C | ..CU..H..z..&{..
+92 62 F3 E9 43 0F FA EF F0 BE 25 A2 67 3B C0 6C | .b..C.....%.g;.l
+59 C0 D3 3A 6C C1 8F AE 53 CB 93 09 E4 52 1C A7 | Y..:l...S....R..
+01 | .
+
+GUID=3cd7f2e79a69064692b6a35e4927c4d4 (E7F2D73C-699A-4606-92B6-A35E4927C4D4)
+NAME=0046006f0072006d0044006100740061 (FormData)
+PATH=010414009fcf89b211f9fd419bad2c92576886647fff0400 (VenHw(B289CF9F-F911-41FD-9BAD-2C9257688664))
+ALTCFG=0000
+OFFSET=0040 WIDTH=0001 VALUE=01
+01 | .
+
+GUID=3cd7f2e79a69064692b6a35e4927c4d4 (E7F2D73C-699A-4606-92B6-A35E4927C4D4)
+NAME=0046006f0072006d0044006100740061 (FormData)
+PATH=010414009fcf89b211f9fd419bad2c92576886647fff0400 (VenHw(B289CF9F-F911-41FD-9BAD-2C9257688664))
+ALTCFG=0001
+OFFSET=0040 WIDTH=0001 VALUE=01
+01 | .
+```
+
+As you can see now the form storage contains not a password itself, but a hash value of the password. This way even if the storage is compromised the potential attaker still wouldn't have the original password value.
+
+
+# Compiling `Hash2DxeCrypto` to the OVMF
+
+As you saw earlier by default the OVMF doesn't populate the `EFI_HASH2_SERVICE_BINDING_PROTOCOL`. To get it we needed to manually load the `Hash2DxeCrypto.efi` driver.
+
+Alternatively we can include the `Hash2DxeCrypto` to the OVMF build, so it would execute in the DXE stage every time on the machine load.
+
+For that we need to add it to the DSC (`OvmfPkg/OvmfPkgX64.dsc`):
+```
+[Components]
+
+<...>
+
+SecurityPkg/Hash2DxeCrypto/Hash2DxeCrypto.inf
+```
+
+And to the FDF (`OvmfPkg/OvmfPkgX64.fdf`):
+```
+[FV.DXEFV]
+
+<...>
+
+INF SecurityPkg/Hash2DxeCrypto/Hash2DxeCrypto.inf
+```
+
+And recompile the OVMF image after the changes:
+```
+build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
+```
+
+Now you should be able to load our `PasswordFormWithHash` application right after the shell load.
+
+# Alternatives to the `EFI_HASH2_PROTOCOL`
+
+Instead of using the `EFI_HASH2_PROTOCOL` you can just use the hash functions from the `BaseCryptLib` library [https://github.com/tianocore/edk2/blob/master/CryptoPkg/Include/Library/BaseCryptLib.h](https://github.com/tianocore/edk2/blob/master/CryptoPkg/Include/Library/BaseCryptLib.h).
+
+For example here is a description for the `Sha512HashAll` function:
+```cpp
+/**
+ Computes the SHA-512 message digest of a input data buffer.
+
+ This function performs the SHA-512 message digest of a given data buffer, and places
+ the digest value into the specified memory.
+
+ If this interface is not supported, then return FALSE.
+
+ @param[in] Data Pointer to the buffer containing the data to be hashed.
+ @param[in] DataSize Size of Data buffer in bytes.
+ @param[out] HashValue Pointer to a buffer that receives the SHA-512 digest
+ value (64 bytes).
+
+ @retval TRUE SHA-512 digest computation succeeded.
+ @retval FALSE SHA-512 digest computation failed.
+ @retval FALSE This interface is not supported.
+
+**/
+BOOLEAN
+EFIAPI
+Sha512HashAll (
+ IN CONST VOID *Data,
+ IN UINTN DataSize,
+ OUT UINT8 *HashValue
+ );
+```
+
+We can just use it in our calculations simple as this:
+```
+#include <Library/BaseCryptLib.h>
+
+...
+
+UINT8 Hash[SHA512_DIGEST_SIZE];
+BOOLEAN Status = Sha512HashAll(Password, StrLen(Password) * sizeof(CHAR16), Hash);
+if (!Status) {
+ return EFI_DEVICE_ERROR;
+}
+return EFI_SUCCESS;
+```
+
+The downside of this approach is that this will definitely blow the driver image size. When we use the function from the protocol, in the nutshell we just find some memory address and cast it to a target function. So no actual impementation code of the function is present in our driver.
+
+On the other case when you use the library approach, all the function implementation code would be encoded in our driver at compile-time. This would bloat the image, but will make the driver self-sufficient.
+
+Right now edk2 is in the process of refactoring code to the usage of `EFI_HASH2_PROTOCOL`. Most of the code that you would find now is based on the library approach. But nevertheless a more correct approach is to move towards the protocol solution.
diff --git a/Lessons_uncategorized/Lesson_RegisterProtocolNotify/README.md b/Lessons_uncategorized/Lesson_RegisterProtocolNotify/README.md
new file mode 100644
index 0000000..72f4e65
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_RegisterProtocolNotify/README.md
@@ -0,0 +1,470 @@
+In this lesson we would investigate the `EFI_BOOT_SERVICES.RegisterProtocolNotify()` function. This API can be usefull to create callbacks that are executed when certain protocols are installed in the system.
+
+For our investigation let's create a simple driver `ProtocolEventDriver`:
+```
+./createNewDriver.sh ProtocolEventDriver
+```
+
+Add the following code to create a callback function `NotifyFunc` executed when the local `EFI_EVENT Event` is signaled:
+```cpp
+EFI_EVENT Event;
+UINTN NotifyData = 0;
+
+VOID EFIAPI NotifyFunc(EFI_EVENT Event, VOID* Context)
+{
+ ...
+}
+
+
+EFI_STATUS
+EFIAPI
+ProtocolEventDriverUnload (
+ EFI_HANDLE ImageHandle
+ )
+{
+ gBS->CloseEvent(Event);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+ProtocolEventDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ &NotifyFunc,
+ &NotifyData,
+ &Event);
+ if (EFI_ERROR(Status)) {
+ Print(L"Error! CreateEvent returned: %r\n", Status);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+```
+
+Here we pass `UINTN NotifyData` to the callback function. Let's increment it inside the callback code, so we can understand how many times the callback was called:
+```cpp
+ if (Context == NULL)
+ return;
+
+ Print(L"\nEvent is signaled! Context = %d\n", *(UINTN*)Context);
+ *(UINTN*)Context += 1;
+```
+
+Right now no one can signal our event, therefore the callback code would never be executed. Let's register the protocol install notifier. Here we would monitor installation of our own `SIMPLE_CLASS_PROTOCOL` which we've created in the earlier lesson.
+
+In case you don't remember check out the protocol API in the `UefiLessonsPkg/Include/Protocol/SimpleClass.h`
+```cpp
+typedef
+EFI_STATUS
+(EFIAPI* SIMPLE_CLASS_GET_NUMBER)(
+ UINTN* Number
+ );
+
+
+typedef
+EFI_STATUS
+(EFIAPI* SIMPLE_CLASS_SET_NUMBER)(
+ UINTN Number
+ );
+
+
+struct _SIMPLE_CLASS_PROTOCOL {
+ SIMPLE_CLASS_GET_NUMBER GetNumber;
+ SIMPLE_CLASS_SET_NUMBER SetNumber;
+};
+```
+
+In the nutshell this protocol is a simple abstraction to the `UINTN Number` field.
+
+Now to the `RegisterProtocolNotify()`. Here is its description from the UEFI specification:
+```
+EFI_BOOT_SERVICES.RegisterProtocolNotify()
+
+Summary:
+Creates an event that is to be signaled whenever an interface is installed for a specified protocol.
+
+Prototype:
+typedef
+EFI_STATUS
+(EFIAPI *EFI_REGISTER_PROTOCOL_NOTIFY) (
+ IN EFI_GUID *Protocol,
+ IN EFI_EVENT Event,
+ OUT VOID **Registration
+);
+
+Parameters:
+Protocol - The numeric ID of the protocol for which the event is to be registered
+Event - Event that is to be signaled whenever a protocol interface is registered for Protocol.
+ The same EFI_EVENT may be used for multiple protocol notify registrations
+Registration - A pointer to a memory location to receive the registration value. This value must be saved and used by the notification function
+ of Event to retrieve the list of handles that have added a protocol interface of type Protocol
+
+Description:
+The RegisterProtocolNotify() function creates an event that is to be signaled whenever a protocol interface is installed for Protocol by InstallProtocolInterface() or EFI_BOOT_SERVICES.ReinstallProtocolInterface().
+
+Once Event has been signaled, the EFI_BOOT_SERVICES.LocateHandle() function can be called to identify the newly installed, or reinstalled, handles that support Protocol. The Registration parameter in EFI_BOOT_SERVICES.RegisterProtocolNotify() corresponds to the SearchKey parameter in LocateHandle().
+```
+
+And this is how we would use it:
+```cpp
+#include <Protocol/SimpleClass.h>
+
+STATIC VOID *mRegistrationTracker;
+
+EFI_STATUS
+EFIAPI
+ProtocolEventDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ ...
+
+ Status = gBS->RegisterProtocolNotify(&gSimpleClassProtocolGuid,
+ Event,
+ &mRegistrationTracker);
+ if (EFI_ERROR(Status)) {
+ Print(L"Error! RegisterProtocolNotify returned: %r\n", Status);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+```
+Don't mind the `Registration` parameter for now, we'll investigate it later.
+
+Also don't forget to add the necessary code to the `INF` file to be able to use our `SIMPLE_CLASS_PROTOCOL` in the driver code:
+```
+[Packages]
+ ...
+ UefiLessonsPkg/UefiLessonsPkg.dec
+
+[Protocols]
+ gSimpleClassProtocolGuid
+```
+
+Now when we load `SimpleClassProtocol.efi` driver (created in the earlier lesson) that installs the `SIMPLE_CLASS_PROTOCOL` protocol we would see that our callback code is executed:
+```
+FS0:\> load ProtocolEventDriver.efi
+Image 'FS0:\ProtocolEventDriver.efi' loaded at 6415000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 0
+, handle=640FB98
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 640C000 - Success
+```
+
+The strings
+```
+Hello from SimpleClassProtocol driver
+, handle=640FB98
+```
+
+Are printed from the `SimpleClassProtocol` driver. Here is its code:
+```cpp
+EFI_STATUS
+EFIAPI
+SimpleClassProtocolDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ Print(L"Hello from SimpleClassProtocol driver");
+
+ EFI_STATUS Status = gBS->InstallMultipleProtocolInterfaces(
+ &mSimpleClassHandle,
+ &gSimpleClassProtocolGuid,
+ &mSimpleClass,
+ NULL
+ );
+ if (!EFI_ERROR(Status))
+ Print(L", handle=%p\n", mSimpleClassHandle);
+ else
+ Print(L"\n", mSimpleClassHandle);
+
+ return Status;
+}
+```
+
+You can see how the `\nEvent is signaled! Context = 0\n` from the `ProtocolEventDriver` was printed in between the prints of `SimpleClassProtocol` driver. That means that as soon as the `SIMPLE_CLASS_PROTOCOL` was installed, our callback was executed interrupting the `SimpleClassProtocol` driver code.
+
+You can execute the `load SimpleClassProtocol.efi` again and see that our callback would be called on each protocol install:
+```
+FS0:\> load ProtocolEventDriver.efi
+Image 'FS0:\ProtocolEventDriver.efi' loaded at 6415000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 0
+, handle=640FB98
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 640C000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 1
+, handle=641AB18
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 6408000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 2
+, handle=6419918
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 63E3000 - Success
+```
+
+You can also see that as we've coded earlier, the `UINTN NotifyData` (aka `Context`) is incremented on each callback execution.
+
+Now let's check the `dh` output:
+```
+FS0:\> dh
+...
+A4: ImageDevicePath(..C1)/\ProtocolEventDriver.efi) LoadedImage(\ProtocolEventDriver.efi)
+A5: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
+A6: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
+A7: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
+A8: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
+A9: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
+AA: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
+```
+You can see that each `load SimpleClassProtocol.efi` command installs a separate `SIMPLE_CLASS_PROTOCOL` protocol to the system.
+
+Now let's try to actually use the `SIMPLE_CLASS_PROTOCOL` inside the callback function. We would use the `gBS->LocateProtocol` to find the protocol and perform `+5` operation on its internal number via the protocol `SIMPLE_CLASS_GET_NUMBER/SIMPLE_CLASS_SET_NUMBER` functions.
+```cpp
+VOID EFIAPI NotifyFunc(EFI_EVENT Event, VOID* Context)
+{
+ ...
+
+ EFI_STATUS Status;
+ SIMPLE_CLASS_PROTOCOL* SimpleClass;
+ Status = gBS->LocateProtocol(&gSimpleClassProtocolGuid,
+ NULL,
+ (VOID**)&SimpleClass);
+ if (EFI_ERROR(Status)) {
+ Print(L"Error! LocateProtocol returned: %r\n", Status);
+ return;
+ }
+
+ UINTN Number;
+ Status = SimpleClass->GetNumber(&Number);
+ if (!EFI_ERROR(Status)) {
+ Print(L"Current number = %d\n", Number);
+ } else {
+ Print(L"Error! Can't get number: %r\n", Status);
+ return;
+ }
+
+ Status = SimpleClass->SetNumber(Number+5);
+ if (EFI_ERROR(Status)) {
+ Print(L"Error! Can't set number: %r\n", Status);
+ return;
+ }
+}
+```
+
+This is what we would get from this code:
+```
+FS0:\> load ProtocolEventDriver.efi
+Image 'FS0:\ProtocolEventDriver.efi' loaded at 6415000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 0
+Current number = 0
+, handle=640FB98
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 640C000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 1
+Current number = 5
+, handle=646A818
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 6408000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 2
+Current number = 10
+, handle=6419918
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 63E3000 - Success
+```
+
+You can see the problem here. Each time we perform `load SimpleClassProtocol.efi` we install additional protocol to the system. We saw that in the `dh` command output.
+But `gBS->LocateProtocol` always finds the first installed protocol. So how can we call the callback code on the newly installed protocol that caused the callback in the first place?
+
+To fix that we can utilize `Registration` parameter of the `RegisterProtocolNotify/LocateProtocol` functions.
+
+With it the `LocateProtocol` function would return the next handle that is new for the registration.
+
+Here is a description of the `EFI_BOOT_SERVICES.LocateProtocol()` function from the specification. We've already saw it, and used it many times, but now pay attention to the `Registration` parameter:
+```
+EFI_BOOT_SERVICES.LocateProtocol()
+
+Summary:
+Returns the first protocol instance that matches the given protocol.
+
+Prototype:
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOCATE_PROTOCOL) (
+ IN EFI_GUID *Protocol,
+ IN VOID *Registration OPTIONAL,
+ OUT VOID **Interface
+);
+
+Parameters:
+Protocol - Provides the protocol to search for
+Registration - Optional registration key returned from EFI_BOOT_SERVICES.RegisterProtocolNotify(). If Registration is NULL, then it is ignored
+Interface - On return, a pointer to the first interface that matches Protocol and Registration
+
+Description:
+The LocateProtocol() function finds the first device handle that support Protocol, and returns a pointer to the protocol interface from that handle in Interface. If no protocol instances are found, then Interface is set to NULL.
+
+If Registration is not NULL, and there are no new handles for Registration, then EFI_NOT_FOUND is returned.
+```
+
+All we need to do now to fix the problem is to use `mRegistrationTracker` instead of `NULL` in the `gBS->LocateProtocol` call. In the nutshell the registration variable is used in 3 places:
+```cpp
+STATIC VOID *mRegistrationTracker; // <-----
+
+VOID EFIAPI NotifyFunc(EFI_EVENT Event, VOID* Context)
+{
+ ...
+ Status = gBS->LocateProtocol(&gSimpleClassProtocolGuid,
+ mRegistrationTracker, // <-----
+ (VOID**)&SimpleClass);
+ ...
+}
+
+...
+
+EFI_STATUS
+EFIAPI
+ProtocolEventDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ ...
+ Status = gBS->RegisterProtocolNotify(&gSimpleClassProtocolGuid,
+ Event,
+ &mRegistrationTracker); // <-----
+ ...
+}
+```
+
+Now on each callback we would work with the newly installed protocol:
+```
+FS0:\> load ProtocolEventDriver.efi
+Image 'FS0:\ProtocolEventDriver.efi' loaded at 6415000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 0
+Current number = 0
+, handle=640FB98
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 640C000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 1
+Current number = 0
+, handle=646C218
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 6408000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 2
+Current number = 0
+, handle=6419918
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 63E3000 - Success
+```
+
+# `EfiCreateProtocolNotifyEvent`
+
+To ease creation process of the protocol notification callbacks the [UefiLib](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiLib.h) offers a `EfiCreateProtocolNotifyEvent` function:
+```cpp
+/**
+ Creates and returns a notification event and registers that event with all the protocol
+ instances specified by ProtocolGuid.
+
+ This function causes the notification function to be executed for every protocol of type
+ ProtocolGuid instance that exists in the system when this function is invoked. If there are
+ no instances of ProtocolGuid in the handle database at the time this function is invoked,
+ then the notification function is still executed one time. In addition, every time a protocol
+ of type ProtocolGuid instance is installed or reinstalled, the notification function is also
+ executed. This function returns the notification event that was created.
+ If ProtocolGuid is NULL, then ASSERT().
+ If NotifyTpl is not a legal TPL value, then ASSERT().
+ If NotifyFunction is NULL, then ASSERT().
+ If Registration is NULL, then ASSERT().
+
+
+ @param ProtocolGuid Supplies GUID of the protocol upon whose installation the event is fired.
+ @param NotifyTpl Supplies the task priority level of the event notifications.
+ @param NotifyFunction Supplies the function to notify when the event is signaled.
+ @param NotifyContext The context parameter to pass to NotifyFunction.
+ @param Registration A pointer to a memory location to receive the registration value.
+ This value is passed to LocateHandle() to obtain new handles that
+ have been added that support the ProtocolGuid-specified protocol.
+
+ @return The notification event that was created.
+
+**/
+EFI_EVENT
+EFIAPI
+EfiCreateProtocolNotifyEvent (
+ IN EFI_GUID *ProtocolGuid,
+ IN EFI_TPL NotifyTpl,
+ IN EFI_EVENT_NOTIFY NotifyFunction,
+ IN VOID *NotifyContext OPTIONAL,
+ OUT VOID **Registration
+ )
+```
+
+This API abstracts the calling of `CreateEvent/RegisterProtocolNotify` functions.
+
+Keep in mind that `EfiCreateProtocolNotifyEvent` also immediately signals the callback function `NotifyFunction` manually right after the `RegisterProtocolNotify` call. So your notification function can be executed even if there are no target protocols in the system.
+
+You can see how the `EfiCreateProtocolNotifyEvent` can simplify our main code:
+```cpp
+EFI_STATUS
+EFIAPI
+ProtocolEventDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ Event = EfiCreateProtocolNotifyEvent(&gSimpleClassProtocolGuid,
+ TPL_NOTIFY,
+ &NotifyFunc,
+ &NotifyData,
+ &mRegistrationTracker);
+ return EFI_SUCCESS;
+}
+```
+
+If you'll test this application you'll see that opposed to our own code before, here the created event is signaled one time even when there are no `SIMPLE_CLASS_PROTOCOL` protocols in the system.
+```
+FS0:\> load ProtocolEventDriver.efi
+
+Event is signaled! Context = 0 <-------
+Error! LocateProtocol returned: Not Found <-------
+Image 'FS0:\ProtocolEventDriver.efi' loaded at 6415000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 1
+Current number = 0
+, handle=640FB98
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 640C000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 2
+Current number = 0
+, handle=641A918
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 6408000 - Success
+FS0:\> load SimpleClassProtocol.efi
+Hello from SimpleClassProtocol driver
+Event is signaled! Context = 3
+Current number = 0
+, handle=6419918
+Image 'FS0:\SimpleClassProtocol.efi' loaded at 63E3000 - Success
+```
diff --git a/Lessons_uncategorized/Lesson_Varstore_5/README.md b/Lessons_uncategorized/Lesson_Varstore_5/README.md
index f3c9f23..d672998 100644
--- a/Lessons_uncategorized/Lesson_Varstore_5/README.md
+++ b/Lessons_uncategorized/Lesson_Varstore_5/README.md
@@ -1,4 +1,6 @@
-In all our experments the driver `Callback()` function was never called.
+The source code for this lesson is provided in the 'HIIFormCallbackDebug' driver which is based on our standard `varstore` HII driver.
+
+In all our earlier experiments with the `EFI_HII_CONFIG_ACCESS_PROTOCOL` functions the driver `Callback()` function was never called.
This has happend because none of the driver form elements had `EFI_IFR_FLAG_CALLBACK` flag set.
@@ -247,7 +249,10 @@ VOID DebugCallbackValue(UINT8 Type, EFI_IFR_TYPE_VALUE *Value)
DEBUG ((EFI_D_INFO, "%04d/%02d/%02d\n", Value->date.Year, Value->date.Month, Value->date.Day));
break;
case EFI_IFR_TYPE_STRING:
- DEBUG ((EFI_D_INFO, "%s\n", HiiGetString(mHiiHandle, Value->string, "en-US") ));
+ if (Value->string)
+ DEBUG ((EFI_D_INFO, "%s\n", HiiGetString(mHiiHandle, Value->string, "en-US") ));
+ else
+ DEBUG ((EFI_D_INFO, "NO STRING!\n" ));
break;
default:
DEBUG ((EFI_D_INFO, "Unknown\n" ));
@@ -318,7 +323,7 @@ Now it is time to test our driver. Build our driver, copy it to the shared folde
Load our driver to the UEFI shell.
```
-FS0:\> load HIIFormDataElementsVarstore.efi
+FS0:\> load HIIFormCallbackDebug.efi
```
The `Callback()` code is run only by the Form Browser. Therefore if we would issue requests with our `HIIConfig.efi` application, the `Callback()` would never run. So let's type `exit` to run the Form Browser.
@@ -444,3 +449,28 @@ Callback: Action=EFI_BROWSER_ACTION_FORM_CLOSE, QuestionId=0x0001, Type=EFI_IFR_
I hope all of these experiments have got you some understanding about when and how the `Callback()` function is executed.
+To finish this lesson here is a logic diagram for an individual numeric element with the current value of 8:
+```
+ Open form (even before the form is displayed)
+EFI_BROWSER_ACTION_FORM_OPEN (Value=0)
+EFI_BROWSER_ACTION_RETRIEVE (Value=8)
+ |
+ -------------------------------------------------
+ | |
+ Change value from 8 to 8 Close form
+EFI_BROWSER_ACTION_CHANGING (Value=8) EFI_BROWSER_ACTION_FORM_CLOSE (Value=8)
+ |
+ -------------------------------------------------
+ | |
+ Change value from 8 to 9 Close form
+EFI_BROWSER_ACTION_CHANGING (Value=9) EFI_BROWSER_ACTION_FORM_CLOSE (Value=8)
+EFI_BROWSER_ACTION_CHANGED (Value=9)
+ |
+ -------------------------------------------------------------------------------------------------
+ | | |
+ Submit Close form with sumbit Close form without submit
+EFI_BROWSER_ACTION_SUBMITTED (Value=9) EFI_BROWSER_ACTION_SUBMITTED (Value=9) EFI_BROWSER_ACTION_CHANGED (Value=8)
+ | EFI_BROWSER_ACTION_FORM_CLOSE (Value=9) EFI_BROWSER_ACTION_FORM_CLOSE (Value=8)
+ Close
+EFI_BROWSER_ACTION_FORM_CLOSE (Value=9)
+```
diff --git a/Lessons_uncategorized/Lesson_Varstore_5/UefiLessonsPkg/HIIFormDataElementsVarstore/HIIFormDataElementsVarstore.c b/Lessons_uncategorized/Lesson_Varstore_5/UefiLessonsPkg/HIIFormDataElementsVarstore/HIIFormDataElementsVarstore.c
index 42af437..3804f0a 100644
--- a/Lessons_uncategorized/Lesson_Varstore_5/UefiLessonsPkg/HIIFormDataElementsVarstore/HIIFormDataElementsVarstore.c
+++ b/Lessons_uncategorized/Lesson_Varstore_5/UefiLessonsPkg/HIIFormDataElementsVarstore/HIIFormDataElementsVarstore.c
@@ -224,7 +224,10 @@ VOID DebugCallbackValue(UINT8 Type, EFI_IFR_TYPE_VALUE *Value)
DEBUG ((EFI_D_INFO, "%04d/%02d/%02d\n", Value->date.Year, Value->date.Month, Value->date.Day));
break;
case EFI_IFR_TYPE_STRING:
- DEBUG ((EFI_D_INFO, "%s\n", HiiGetString(mHiiHandle, Value->string, "en-US") ));
+ if (Value->string)
+ DEBUG ((EFI_D_INFO, "%s\n", HiiGetString(mHiiHandle, Value->string, "en-US") ));
+ else
+ DEBUG ((EFI_D_INFO, "NO STRING!\n" ));
break;
default:
DEBUG ((EFI_D_INFO, "Unknown\n" ));
diff --git a/Lessons_uncategorized/Lesson_Varstore_6/README.md b/Lessons_uncategorized/Lesson_Varstore_6/README.md
index a5f9236..b6af888 100644
--- a/Lessons_uncategorized/Lesson_Varstore_6/README.md
+++ b/Lessons_uncategorized/Lesson_Varstore_6/README.md
@@ -145,7 +145,10 @@ VOID CallbackValueToStr(UINT8 Type, EFI_IFR_TYPE_VALUE *Value, EFI_STRING* Value
UnicodeSPrint(*ValueStr, ValueStrSize, L"%04d/%02d/%02d", Value->date.Year, Value->date.Month, Value->date.Day);
break;
case EFI_IFR_TYPE_STRING:
- UnicodeSPrint(*ValueStr, ValueStrSize, L"%s", HiiGetString(mHiiHandle, Value->string, "en-US"));
+ if (Value->string)
+ UnicodeSPrint(*ValueStr, ValueStrSize, L"%s", HiiGetString(mHiiHandle, Value->string, "en-US"));
+ else
+ UnicodeSPrint(*ValueStr, ValueStrSize, L"NO STRING!");
break;
default:
UnicodeSPrint(*ValueStr, ValueStrSize, L"Unknown");
diff --git a/Lessons_uncategorized/Lesson_Varstore_7/README.md b/Lessons_uncategorized/Lesson_Varstore_7/README.md
new file mode 100644
index 0000000..80f1ed5
--- /dev/null
+++ b/Lessons_uncategorized/Lesson_Varstore_7/README.md
@@ -0,0 +1,212 @@
+Let's continue our investigation of the `EFI_HII_CONFIG_ACCESS_PROTOCOL.Callback` function.
+
+In the last lesson we've researched all the input parameters of the function and understood when the function is called.
+
+Now let's check the output parameter of the function - `OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest`. According to the UEFI specification `Upon return, the callback function may specify the desired browser action`.
+
+All the available values for the `ActionRequest` are listed in the file [https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/FormBrowser2.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/FormBrowser2.h):
+```
+typedef UINTN EFI_BROWSER_ACTION_REQUEST;
+
+#define EFI_BROWSER_ACTION_REQUEST_NONE 0
+#define EFI_BROWSER_ACTION_REQUEST_RESET 1
+#define EFI_BROWSER_ACTION_REQUEST_SUBMIT 2
+#define EFI_BROWSER_ACTION_REQUEST_EXIT 3
+#define EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT 4
+#define EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT 5
+#define EFI_BROWSER_ACTION_REQUEST_FORM_APPLY 6
+#define EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD 7
+#define EFI_BROWSER_ACTION_REQUEST_RECONNECT 8
+#define EFI_BROWSER_ACTION_REQUEST_QUESTION_APPLY 9
+```
+
+According to the UEFI specification:
+```
+If the callback function returns with the ActionRequest set to:
+ _NONE, then the Forms Browser will take no special behavior,
+ _RESET, then the Forms Browser will exit and request the platform to reset,
+ _SUBMIT, then the Forms Browser will save all modified question values to storage and exit,
+ _EXIT, then the Forms Browser will discard all modified question values and exit,
+ _FORM_SUBMIT_EXIT, then the Forms Browser will write all modified question values on the selected form to storage and then exit the selected form,
+ _FORM_DISCARD_EXIT, then the Forms Browser will discard the modified question values on the selected form and then exit the selected form,
+ _FORM_APPLY, then the Forms Browser will write all modified current question values on the selected form to storage,
+ _FORM_DISCARD, then the Forms Browser will discard the current question values on the selected form and replace them with the original question values,
+ _RECONNECT, a hardware and/or software configuration change was performed by the user, and the controller needs to be reconnected for the driver to recognize the change. The Forms Browser is required to call the EFI Boot Service DisconnectController() followed by the EFI Boot Service ConnectController() to reconnect the controller, and then exit. The controller handle passed to DisconnectController() and ConnectController() is the handle on which this EFI_HII_CONFIG_ACCESS_PROTOCOL is installed,
+ _QUESTION_APPLY, then the Forms Browser will write the current modified question value on the selected form to storage.
+```
+
+In a short form this means:
+```
+_NONE - nothing
+_RESET - exit and reset platform
+_SUBMIT - save all modifications and exit
+_EXIT - discard all modifications and exit
+_FORM_SUBMIT_EXIT - save current form modifications and exit form
+_FORM_DISCARD_EXIT - discard current form modifications and exit form
+_FORM_APPLY - save current form modifications
+_FORM_DISCARD - discard current form modifications
+_RECONNECT - reconnect the controller and exit
+_QUESTION_APPLY - save current question modification
+```
+
+Let's try to see them in action. For that we would create `HIIFormCallbackDebug2` based on our recent `HIIFormCallbackDebug` driver.
+
+Since the actual `ActionRequest` values are numbers between 0 and 9 we can test them via our `numeric` input. You already know that when the user changes the element value in the Form Browser, the following callbacks are called:
+- `EFI_BROWSER_ACTION_CHANGING`
+- `EFI_BROWSER_ACTION_CHANGED`
+
+So let's set the `*ActionRequest` based on the user input in the `EFI_BROWSER_ACTION_CHANGED` callback for the `numeric`:
+```cpp
+STATIC
+EFI_STATUS
+EFIAPI
+Callback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN OUT EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ //DEBUG ((EFI_D_INFO, "Callback: Action=%s, QuestionId=0x%04x, Type=%s, Value=", ActionToStr(Action), QuestionId, TypeToStr(Type)));
+ //DebugCallbackValue(Type, Value);
+
+ HIIPopupCallbackInfo(Action, QuestionId, Type, Value);
+
+ if ((QuestionId == NUMERIC_QUESTION_ID) && (Action == EFI_BROWSER_ACTION_CHANGED)) {
+ if ((Value->u16 >= 0) && (Value->u16 <= 9)) {
+ *ActionRequest = Value->u16;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_UNSUPPORTED;
+}
+```
+This way when you set `numeric` to 4 for example, in the `EFI_BROWSER_ACTION_CHANGED` callback you would return `ActionRequest = 4 = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT`. Neat!
+
+As you can see from the UEFI specification for the `ActionRequest` some actions close form, and some close entire formset. So to fully debug it we need to create a multiform formset.
+
+Let's create 3 forms linked in the following way:
+```
+Form1 -> Form2 -> Form3
+```
+And move one of our interactive elements (for example `string`) to the Form2 and the rest of the input element leave in the most nested Form3.
+
+Here is a snippet how it would look in the VFR:
+```
+formset
+
+ <...>
+
+ form
+ formid = 1,
+ title = STRING_TOKEN(FORMID1_TITLE);
+
+ goto 2,
+ prompt = STRING_TOKEN(GOTO_FORM2_PROMPT),
+ help = STRING_TOKEN(GOTO_FORM2_HELP);
+
+ endform;
+
+ form
+ formid = 2,
+ title = STRING_TOKEN(FORMID2_TITLE);
+
+ goto 3,
+ prompt = STRING_TOKEN(GOTO_FORM3_PROMPT),
+ help = STRING_TOKEN(GOTO_FORM3_HELP);
+
+ string
+ <...>
+ endstring;
+
+ endform;
+
+ form
+ formid = 3,
+ title = STRING_TOKEN(FORMID3_TITLE);
+
+ <... all other elements ... >
+
+ endform;
+endformset;
+```
+
+Off course don't forget to define the newly added strings:
+```
+#string FORMID1_TITLE #language en-US "Form 1"
+#string FORMID2_TITLE #language en-US "Form 2"
+#string FORMID3_TITLE #language en-US "Form 3"
+#string GOTO_FORM2_PROMPT #language en-US "Enter Form 2"
+#string GOTO_FORM2_HELP #language en-US "Enter Form 2"
+#string GOTO_FORM3_PROMPT #language en-US "Enter Form 3"
+#string GOTO_FORM3_HELP #language en-US "Enter Form 3"
+```
+
+Now you can experiment with the driver, by setting different values to the `numeric`.
+
+First of all couple of new facts that we can obtain from the multiform formset:
+- `Extract` is called once for all the elements when you enter the first form of the formset (Form1),
+- Callbacks `FORM_OPEN`/`FORM_RETRIEVE`/`FORM_CLOSE` are called when you enter the from with the target element. I.e for `string` it will be Form2, for the rest of elements - Form3,
+- You can leave the form with a changed value, but not a formset. When you try to leave formset the browser will ask you if you want to submit the changes,
+- Even if the elements are on different forms the submit action (F10) would produce the callbacks for all the elements
+
+Now back to the `ActionRequest` investigation. Here I've tried to describe addition actions that you would get from setting the `ActionRequest` (i.e. our `numeric`) to the particular value.
+```
+Changing to 0 - _NONE - no additional actions
+
+Changing to 1 - _RESET
+ + ACTION_CHANGED <--- revert uncommited changes for all elements in the formset
+ + ACTION_FORM_CLOSE <--- close for all elements on the form
+ + Close entire formset
+ (Now if you try to exit browser it will prompt that the platform reset is needed)
+
+Changing to 2 - _SUBMIT
+ + Route with settings for all elements
+ + ACTION_SUBMITTED <--- submit for all elements in the formset
+ + ACTION_FORM_CLOSE <--- close for all elements on the form
+ + Close entire formset
+
+Changing to 3 - _EXIT
+ + ACTION_CHANGED <--- revert uncommited changes for all elements in the formset
+ + ACTION_FORM_CLOSE <--- close for all elements on the form
+ + Close entire formset
+
+Changing to 4 - _FORM_SUBMIT_EXIT
+ + Route with settings for all elements
+ + ACTION_SUBMITTED <--- submit for all elements on the form
+ + ACTION_FORM_CLOSE <--- close for all elements on the form
+ + Close current form
+
+Changing to 5 - _FORM_DISCARD_EXIT
+ + ACTION_CHANGED <--- revert uncommited changes for all elements on the form
+ + ACTION_FORM_CLOSE <--- close for all elements on the form
+ + Close current form
+
+Changing to 6 - _FORM_APPLY
+ + Route with settings for all elements
+ + ACTION_SUBMITTED <--- submit for all form elements
+
+Changing to 7 - _FORM_DISCARD
+ + ACTION_CHANGED <--- revert uncommited changes for all form elements
+
+Changing to 8 - _RECONNECT
+ "Reconnect is required, confirm the changes then exit and reconnect"
+ If user answers "N"
+ + ACTION_CHANGED <--- revert uncommited changes in thr formset
+ + ACTION_FORM_CLOSE <--- close for all elements in the form
+ + Close entire formset
+ If user answers "Y"
+ + Route with settings for all elements
+ + ACTION_SUBMITTED <--- submit for all elements in a formset
+ + ACTION_FORM_CLOSE <--- close for all elements in the form
+ + Reconnect
+ + Close entire formset
+
+Changing to 9 - _QUESTION_APPLY <--- no additional callbacks
+ + Route with settings for target question only
+```
+
+I hope I didn't mess anything in the above description. Anyway now you have a `HIIFormCallbackDebug2` application to verify how the FormBrowser calls callbacks for the elements.