From 47539beb55a6c1ab4e3b9f1b73c2783ed39ab6af Mon Sep 17 00:00:00 2001 From: Konstantin Aladyshev Date: Wed, 16 Jun 2021 01:54:43 +0300 Subject: Update lesson 8 --- Lesson_8/DevicePath.png | Bin 0 -> 68176 bytes Lesson_8/README.md | 141 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 124 insertions(+), 17 deletions(-) create mode 100644 Lesson_8/DevicePath.png (limited to 'Lesson_8') diff --git a/Lesson_8/DevicePath.png b/Lesson_8/DevicePath.png new file mode 100644 index 0000000..225226f Binary files /dev/null and b/Lesson_8/DevicePath.png differ diff --git a/Lesson_8/README.md b/Lesson_8/README.md index 903a270..fd31bcf 100644 --- a/Lesson_8/README.md +++ b/Lesson_8/README.md @@ -1,4 +1,4 @@ -Let's explore protocols associated with our ImageHandle that we've discovered in the last lesson. +Let's explore protocols associated with our `ImageHandle` that we've discovered in the last lesson. If we try to grep GUIDs in the edk2 source code: ``` @@ -12,8 +12,7 @@ $ grep -i 5B1B31A1 -r ./ --exclude-dir=Build Link to the header file: https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/LoadedImage.h - - +Description for one of these protocol structures can be found in the same file: ``` /// /// Can be used on any image handle to obtain information about the loaded image. @@ -50,7 +49,12 @@ typedef struct { } EFI_LOADED_IMAGE_PROTOCOL; ``` -EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL - When installed, the Loaded Image Device Path Protocol specifies the device path that was used when a PE/COFF image was loaded through the EFI Boot Service LoadImage(). +UEFI specification gives following description for this protocol: +``` +Each loaded image has an image handle that supports EFI_LOADED_IMAGE_PROTOCOL. When an image is started, it is passed the image handle for itself. The image can use the handle to obtain its relevant image data stored in the EFI_LOADED_IMAGE_PROTOCOL structure, such as its load options +``` + +Another GUID stands for the `EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL` - When installed, the Loaded Image Device Path Protocol specifies the device path that was used when a PE/COFF image was loaded through the EFI Boot Service LoadImage(). The Loaded Image Device Path Protocol uses the same protocol interface structure as the Device Path Protocol. The only difference between the Device Path Protocol and the Loaded Image Device Path Protocol is the protocol GUID value. The Loaded Image Device Path Protocol must be installed onto the image handle of a PE/COFF image loaded through the EFI Boot Service LoadImage(). A copy of the device path specified by the DevicePath parameter to the EFI Boot Service LoadImage() is made before it is installed onto the image handle. It is legal to call LoadImage() for a buffer in memory with a NULL DevicePath parameter. In this case, the Loaded Image Device Path Protocol is installed with a NULL interface pointer. @@ -84,6 +88,44 @@ typedef struct { } EFI_DEVICE_PATH_PROTOCOL; ``` +This structure is like a header to an actual path data which immediately follows it for amount of `Length` bytes. +![Device path](DevicePath.png?raw=true "Device Path in memory") + + +Now we have understanding of all the protocol structures, it is time to know the right way of how to get to them. +The "raw" method that we've used in last lessons was purely educational. +UEFI has several functions to help to get the protocol structures in a code. + +`EFI_BOOT_SERVICES.HandleProtocol()` - Queries a handle to determine if it supports a specified protocol. + +UEFI docs: +``` +Prototype: + +typedef +EFI_STATUS +(EFIAPI *EFI_HANDLE_PROTOCOL) ( + IN EFI_HANDLE Handle, + IN EFI_GUID *Protocol, + OUT VOID **Interface +); + +Parameters: +Handle The handle being queried. If Handle isNULL, then EFI_INVALID_PARAMETER is returned. +Protocol The published unique identifier of the protocol. It is the caller’s responsibility to pass in a valid GUID. +Interface Supplies the address where a pointer to the corresponding Protocol Interface is returned. + NULL will be returned in *Interface if a structure is not associated with Protocol. + +Description: +The HandleProtocol() function queries Handle to determine if it supports Protocol. If it does, then on return Interface points to a pointer to the corresponding Protocol Interface. Interface can then be passed to any protocol service to identify the context of the request. + +Status Codes Returned: +EFI_SUCCESS The interface information for the specified protocol was returned. +EFI_UNSUPPORTED The device does not support the specified protocol. +EFI_INVALID_PARAMETER Handle is NULL.. +EFI_INVALID_PARAMETER Protocol is NULL. +EFI_INVALID_PARAMETER Interface is NULL. +``` With all this info let's write source code for our new `ImageInfo` app: @@ -101,31 +143,75 @@ UefiMain ( IN EFI_SYSTEM_TABLE *SystemTable ) { + EFI_STATUS Status; EFI_LOADED_IMAGE_PROTOCOL* LoadedImage; - gBS->HandleProtocol( + Status = gBS->HandleProtocol( ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage ); - EFI_DEVICE_PATH_PROTOCOL* DevicePath; + if (Status == EFI_SUCCESS) { + EFI_DEVICE_PATH_PROTOCOL* DevicePath; - gBS->HandleProtocol( - LoadedImage->DeviceHandle, // EFI_HANDLE - &gEfiLoadedImageDevicePathProtocolGuid, - (VOID**) &DevicePath - ); + Status = gBS->HandleProtocol( + ImageHandle, + &gEfiLoadedImageDevicePathProtocolGuid, + (VOID**) &DevicePath + ); - Print(L"Image device: %s\n", ConvertDevicePathToText(DevicePath, FALSE, TRUE)); - Print(L"Image file: %s\n", ConvertDevicePathToText(LoadedImage->FilePath, FALSE, TRUE)); // EFI_DEVICE_PATH_PROTOCOL *FilePath - Print(L"Image Base: %X\n", LoadedImage->ImageBase); - Print(L"Image Size: %X\n", LoadedImage->ImageSize); + if (Status == EFI_SUCCESS) { + Print(L"Image device: %s\n", ConvertDevicePathToText(DevicePath, FALSE, TRUE)); + Print(L"Image file: %s\n", ConvertDevicePathToText(LoadedImage->FilePath, FALSE, TRUE)); + Print(L"Image Base: %X\n", LoadedImage->ImageBase); + Print(L"Image Size: %X\n", LoadedImage->ImageSize); + } else { + Print(L"Can't get EFI_LOADED_IMAGE_PROTOCOL, Status=%r\n", Status); + } + } else { + Print(L"Can't get EFI_DEVICE_PATH_PROTOCOL, Status=%r\n", Status); + } return EFI_SUCCESS; } ``` +Some new important things: +- `gBS` is a global variable which is a shortcut for the `SystemTable->BootServices` a table which contains UEFI services produced by UEFI firmware. These services are only valid in pre-OS environment opposed to the `gRT=SystemTable->RuntimeServices` +- `EFI_STATUS` variable can be printed with the help of "%r" formatting option (see https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PrintLib.h) +- `ConvertDevicePathToText` can be used to convert device path to a string. +It is defined here: +https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiDevicePathLibDevicePathProtocol/UefiDevicePathLib.c +With a header file https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DevicePathLib.h : +``` +/** + Converts a device path to its text representation. + @param DevicePath A Pointer to the device to be converted. + @param DisplayOnly If DisplayOnly is TRUE, then the shorter text representation + of the display node is used, where applicable. If DisplayOnly + is FALSE, then the longer text representation of the display node + is used. + @param AllowShortcuts If AllowShortcuts is TRUE, then the shortcut forms of text + representation for a device node can be used, where applicable. + @return A pointer to the allocated text representation of the device path or + NULL if DeviceNode is NULL or there was insufficient memory. +**/ +CHAR16 * +EFIAPI +ConvertDevicePathToText ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN BOOLEAN DisplayOnly, + IN BOOLEAN AllowShortcuts + ); +``` + +Ok, let's try to compile our app. +``` +. edksetup.sh +build +``` +Unfortunately build would fail: ``` /home/kostr/tiano/edk2/UefiLessonsPkg/ImageInfo/ImageInfo.c:16: undefined reference to `gEfiLoadedImageProtocolGuid' /usr/bin/ld: /tmp/ImageInfo.dll.JKxzWu.ltrans0.ltrans.o:/home/kostr/tiano/edk2/UefiLessonsPkg/ImageInfo/ImageInfo.c:24: undefined reference to `gEfiLoadedImageDevicePathProtocolGuid' @@ -152,11 +238,32 @@ The [Protocols] section of the EDK II INF file is a list of the global Protocol ## NOTIFY ``` -After we've added these protocols to the *.inf file, the build will succeed. +After we add these protocols to the *.inf file, the build will succeed. -In case you wonder our GUID variables will go to a file `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/ImageInfo/ImageInfo/DEBUG/AutoGen.c` which is automatically generated by the build system: +In case you wonder, our GUID variables will go to a file `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/ImageInfo/ImageInfo/DEBUG/AutoGen.c` which is automatically generated by the build system: ``` // Protocols GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiLoadedImageProtocolGuid = { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiLoadedImageDevicePathProtocolGuid = { 0xbc62157e, 0x3e33, 0x4fec, {0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf }}; ``` + +If we try to execute our app under OVMF we would get something like this: +``` +UEFI Interactive Shell v2.2 +EDK II +UEFI v2.70 (EDK II, 0x00010000) +Mapping table + FS0: Alias(s):HD0a1:;BLK1: + PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1) + BLK0: Alias(s): + PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0) + BLK2: Alias(s): + PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0) +Press ESC in 5 seconds to skip startup.nsh or any other key to continue. +Shell> fs0: +FS0:\> ImageInfo.efi +Image device: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\ImageInfo.efi +Image file: \ImageInfo.efi +Image Base: 6885000 +Image Size: 5140 +``` -- cgit v1.2.3-18-g5258