These are tips for building applications.
You can create a top level application directory and build it via the following command:
build -m MyFindRom3\MyFindRome.inf.
The contents of MyFindRome directory.
MyFindRome is not referenced in any other files except for the emulator Nt32Pkg\Nt32Pkg.dsc. This is unlike the application I added to MdeModulePkg\Applications\MyFindRom. In that case, I specified in multiple files. MyFindRome does not have any of the pcd entries though.
The benefit of this is that you don't need to build the complete tree each time you make a change. ie. build code, run the emulator, change code, close emulator, build all, run emulator. Instead you can keep the emulator running, build your code and test it.
I was getting a build error in one app when I included code in another app. Both apps were in the MdeModulePkg/Applications subdirectory. Here is the error in the helloworld app even though the include file was in MyFindRom its peer.
HelloWorld\HelloWorld\DEBUG\HelloWorld.efi c:\fw\edk2\Build\NT32IA32\DEBUG_VS2010x8 eModulePkg\Application\HelloWorld\HelloWorld\OUTPUT c:\fw\edk2\IntelFrameworkModulePkg\Include\Library/GenericBdsLib.h(239) : error C2143: syntax error : missing ')' before '*' c:\fw\edk2\IntelFrameworkModulePkg\Include\Library/GenericBdsLib.h(239) : error C2143: syntax error : missing '{' before '*' c:\fw\edk2\IntelFrameworkModulePkg\Include\Library/GenericBdsLib.h(240) : error C2059: syntax error : ')' c:\fw\edk2\IntelFrameworkModulePkg\Include\Library/GenericBdsLib.h(240) : warning C4431: missing type specifier - int assumed. Note: C no longer supports default-int c:\fw\edk2\IntelFrameworkModulePkg\Include\Library/GenericBdsLib.h(240) : warning C4218: nonstandard extension used : must specify at least a storage class or a type 1 file(s) copied.
Bill Paul told me I needed to include PiPei.h like so:
#include <PiPei.h> The compiler is complaining because the EFI_BOOT_MODE macro isn't defined. It's define in PiBootMode.h, which gets pulled in if you use the above #include.
Around the same time, Jaben Carsey told me of a similar routine to DevPathToString
Depending on what you are doing with the resultant string ConvertDevicePathToText from DevicePathLib (header file located in MdePkg) is a much more common method to convert a device path to a text. This also supports some options on some conversion results.
After I added the PiPei.h file I was able to compile but not link. Jaben replied a second time and asked if I had included the library in the [LibraryClasses] section of the .inf file. I made this change to .inf file and it worked.
The .c includes.
#include <Uefi.h> #include <PiPei.h> #include <Pi/PiFirmwareFile.h> #include <Library/DebugLib.h> // ASSERT_EFI_ERROR #include <Protocol/LoadedImage.h> // EFI_LOADED_IMAGE_PROTOCOL #include <Pi/PiFirmwareVolume.h> #include <Library/PcdLib.h> #include <Library/UefiLib.h> #include <Library/UefiApplicationEntryPoint.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiRuntimeServicesTableLib.h> #include <Protocol/FirmwareVolume.h> #include <Library/GenericBdsLib.h> // DevicePathToStr //findstr /s EFI_FV_FILETYPE *.h | findstr typedef
And the .inf file.
[Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec IntelFrameworkPkg/IntelFrameworkPkg.dec IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec [LibraryClasses] UefiApplicationEntryPoint UefiLib PcdLib GenericBdsLib
Around this time, Andrew Fish gave an explanation of the include files. Here is his notes.
To be pedantic you would want #include <PiDxe.h>, and then you don’t need <Uefi.h> I’ll explain what is going on…. UEFI spec replaces legacy BIOS and is an interface specification that talks to Option ROMs, Applications, and OS Loaders. It does not go into how the firmware is constructed internally. The PI spec is a a standaard way to construct firmware based on the Platform Initialization specifications. A UEFI platform does not have to support PI, but a PI platform supports UEFI. PEI - is Pre EFI Initialization. This is the IA32 code that turns on memory and jumps to X64 mode (on a typical PC). The DXE Phase is the Driver Execution Phase. The DXE Core produces the EFI services. The general rule of thumb is match your module type: PEIM -> #include <PiPei.h> DXE_DRIVER -> #include <PiDxe.h> UEFI_DRIVER -> #include <Uefi.h> UEFI_APPLICATION -> #include <Uefi.h> If you need to write an application that accesses things PI (it may not run everywhere) you can #include <PiDxe.h> in place of Uefi.h as this will also include the main stuff from the UEFI spec. https://svn.code.sf.net/p/edk2/code/trunk/edk2/MdePkg/Include/PiDxe.h #ifndef __PI_DXE_H__ #define __PI_DXE_H__ #include <Uefi/UefiBaseType.h> #include <Uefi/UefiSpec.h> #include <Pi/PiDxeCis.h> #endif
Still later, Andrew told me that you can load uefi apps to the emulator via a usb key. I'm not sure exactly what he means, but perhaps he is saying you can run an emulator and then see an attached usb key. I'll have to test this out later.
He also said that you can run the visual studio debugger on the UEFI apps since the .pdb file is available.
Lastly, Brian Johnson explained why sometimes you need [LibraryClasses] entries and sometimes you don't. If you use a function in a library and that library is needed by an already specified library then you don't need to specify it, since it is pulled in automatically. However, if you pull in a library which is not already pulled in automatically then it needs to be specified in the [LibraryClasses] entry.
Here are his direct rules regarding a rule of thumb.
The rule of thumb is that if you include a library's header file (#include <Library/SomeLib.h>), you should list that library class in your [LibraryClasses].
The .c code
#include <PiPei.h> #include <Pi/PiFirmwareFile.h> #include <Library/DebugLib.h> // ASSERT_EFI_ERROR #include <Protocol/LoadedImage.h> // EFI_LOADED_IMAGE_PROTOCOL #include <Pi/PiFirmwareVolume.h> #include <Library/PcdLib.h> #include <Library/UefiLib.h> #include <Library/DevicePathLib.h> // ConvertDevicePathToText #include <Library/UefiApplicationEntryPoint.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiRuntimeServicesTableLib.h> #include <Protocol/FirmwareVolume.h> #include <Library/GenericBdsLib.h> // DevicePathToStr //findstr /s EFI_FV_FILETYPE *.h | findstr typedef /** * DEBUG_VS2010x86\FV> volinfo -x Guid.xref FVRECOVERY.fv * guid.xref gives human readable names for the GUIDs in the .fv file. * The result relative to MyBios which is the option rom, i'm trying to find is: * * ============================================================ * File Name: 9BEDA3F9-C15F-47C8-B779-2AA9CFD50ACD MyBios * File Offset: 0x001ADAF8 * File Length: 0x00013E10 * File Attributes: 0x00 * File State: 0xF8 * EFI_FILE_DATA_VALID * File Type: 0x02 EFI_FV_FILETYPE_FREEFORM * ------------------------------------------------------------ * Type: EFI_SECTION_RAW * Size: 0x00013DF8 * There are a total of 96 files in this FV * * And for the actual UEFI App, I'm building here it is not in the .fv file. It resides on the file system. * Here is info for it. * MyFindRom.efi is 24,576 bytes * In the build directory beneath IA32 in the MdeModulePkg eventually you get to the * output and debug directory. In the debug dir is a MyFindRom.dll and a second MyFindRom.efi. PEView * shows that the .efi file is a IMAGE_SUBSYSTEM_EFI_APPLICATION. * **/ EFI_STATUS EFIAPI FindAppFileInfo(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) { EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; EFI_STATUS Status; Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); Print(L"Status = %d\n", Status); ASSERT_EFI_ERROR (Status); Print(L"LoadOptionsSize = 0x%d\n", LoadedImage->LoadOptionsSize); Print(L"ImageSize = %d\n", LoadedImage->ImageSize); Print(L"ImageBase = 0x%x\n", LoadedImage->ImageBase); Print(L"FilePath->Type = %d\n", LoadedImage->FilePath->Type); Print(L"FilePath->SubType = %d\n", LoadedImage->FilePath->SubType); Print(L"FilePath->Length[0] = %d\n", LoadedImage->FilePath->Length[0]); Print(L"FilePath->Length[1] = %d\n", LoadedImage->FilePath->Length[1]); Print(L"This next routines will print the file name. Not the path.\n"); Print(L"Image File (via DevicePathToStr) %s\n", DevicePathToStr(LoadedImage->FilePath)); Print(L"Image File (via ConvertDevicePathToText) %s\n", ConvertDevicePathToText(LoadedImage->FilePath,TRUE,FALSE)); return EFI_SUCCESS; } /** The user entry point for the Application. The user code starts with this functrion * as the real entry point for the application. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI UefiMain (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) { FindAppFileInfo(ImageHandle,SystemTable); return EFI_SUCCESS; }
The .inf file. The protocols stuff below is in progress. I will need to expand this.
## @file [Defines] INF_VERSION = 0x00010005 BASE_NAME = MyFindRome FILE_GUID = 6983936E-E334-446b-AE97-133674ED2116 MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 1.0 ENTRY_POINT = UefiMain [Sources] MyFindRome.c [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec IntelFrameworkPkg/IntelFrameworkPkg.dec IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec [LibraryClasses] UefiApplicationEntryPoint UefiLib PcdLib GenericBdsLib [ Protocols ] gEfiLoadedImageProtocolGuid gEfiDevicePathToTextProtocolGuid
The .bat file to build just the app.
~~~~~~~
@echo *
@echo * Invoke this batch file from the parent directry. Not here.
@echo *
build -m MyFindRome\MyFindRome.inf
~~~~~~