From bf1ec7c34e789a82b362adede1c96049c32d3d27 Mon Sep 17 00:00:00 2001 From: Konstantin Aladyshev Date: Mon, 8 Nov 2021 14:38:13 +0300 Subject: Add lesson 51 Signed-off-by: Konstantin Aladyshev --- Lessons/Lesson_51/Man1.png | Bin 0 -> 54872 bytes Lessons/Lesson_51/Man2.png | Bin 0 -> 86585 bytes Lessons/Lesson_51/README.md | 359 +++++++++++++++++++++ .../UefiLessonsPkg/HIIStringsMan/HIIStringsMan.c | 14 + .../UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf | 19 ++ .../UefiLessonsPkg/HIIStringsMan/Strings.uni | 30 ++ .../Lesson_51/UefiLessonsPkg/UefiLessonsPkg.dsc | 69 ++++ 7 files changed, 491 insertions(+) create mode 100644 Lessons/Lesson_51/Man1.png create mode 100644 Lessons/Lesson_51/Man2.png create mode 100644 Lessons/Lesson_51/README.md create mode 100644 Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.c create mode 100644 Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf create mode 100644 Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/Strings.uni create mode 100644 Lessons/Lesson_51/UefiLessonsPkg/UefiLessonsPkg.dsc (limited to 'Lessons/Lesson_51') diff --git a/Lessons/Lesson_51/Man1.png b/Lessons/Lesson_51/Man1.png new file mode 100644 index 0000000..0cb8292 Binary files /dev/null and b/Lessons/Lesson_51/Man1.png differ diff --git a/Lessons/Lesson_51/Man2.png b/Lessons/Lesson_51/Man2.png new file mode 100644 index 0000000..2488668 Binary files /dev/null and b/Lessons/Lesson_51/Man2.png differ diff --git a/Lessons/Lesson_51/README.md b/Lessons/Lesson_51/README.md new file mode 100644 index 0000000..350114c --- /dev/null +++ b/Lessons/Lesson_51/README.md @@ -0,0 +1,359 @@ +Every command in UEFI Shell has a help message which you can read if you supply command with a `-?` argument. For example: +``` +Shell> reset -? +Resets the system. + +RESET [-w [string]] +RESET [-s [string]] +RESET [-c [string]] + + -s - Performs a shutdown. + -w - Performs a warm boot. + -c - Performs a cold boot. + string - Describes a reason for the reset. + +NOTES: + 1. This command resets the system. + 2. The default is to perform a cold reset unless the -w parameter is + specified. + 3. If a reset string is specified, it is passed into the Reset() + function, and the system records the reason for the system reset. +``` + +In this lesson we would investigate how we can add this help/man functionality to our application. + +The Shell module responsible for the man finding and parsing is `ShellManParser`: +- https://github.com/tianocore/edk2/blob/master/ShellPkg/Application/Shell/ShellManParser.h +- https://github.com/tianocore/edk2/blob/master/ShellPkg/Application/Shell/ShellManParser.c + +If you look close at the module `ProcessManFile` function you'll see that besides everything it tries to: +- open image protocol by `gEfiHiiPackageListProtocolGuid` +- if found, register recieved Package list with `gHiiDatabase->NewPackageList` +- go through all possible string IDs with a help of `HiiGetString` +- if `ManFileFindTitleSection` function returns true for some string, execute `ManFileFindSections` + +`ManFileFindTitleSection` basically searches for a string that has a special man formatting. + +You could learn more about this formatting from the UEFI Shell specification (https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf) + +![Manual Page Syntax1](Man1.png?raw=true "Manual page1") +![Manual Page Syntax2](Man2.png?raw=true "Manual page2") + +# Create an app with a minimal manual + +Let's create an application with a manual. + +Use shell script to create app from template: +``` +./createNewApp.sh HIIStringsMan +``` + +Add newly created app to the UefiLessonsPkg/UefiLessonsPkg.dsc: +``` +[Components] + UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf +``` + +As you remember `ShellManParser` searched for the manual strings in the `gEfiHiiPackageListProtocolGuid` protocol data. Therefore our manual strings we need to include directly in the image resource section UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf: +``` +[Defines] + ... + UEFI_HII_RESOURCE_SECTION = TRUE + +[Sources] + ... + Strings.uni +``` + +Here is a minimal content for our strings file UefiLessonsPkg/HIIStringsMan/Strings.uni: +``` +#langdef en-US "English" + +#string STR_HELP #language en-US "" +".TH HIIStringsMan 0 "Simple application with a manual inside."\r\n" +".SH NAME\r\n" +"HIIStringsMan application.\r\n" +``` + +If you build our application and try to execute it, there wouldn't be any help now: +``` +FS0:\> HIIStringsMan.efi -? +No help could be found for command 'HIIStringsMan.efi'. +FS0:\> +``` + +This is because our program don't reference string tokens and they got optimized in the build process `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsMan/HIIStringsMan/DEBUG/HIIStringsManStrDefs.h`: +``` +// +//Unicode String ID +// +// #define $LANGUAGE_NAME 0x0000 // not referenced +// #define $PRINTABLE_LANGUAGE_NAME 0x0001 // not referenced +// #define STR_HELLO_WORLD_HELP_INFORMATION 0x0002 // not referenced +``` + +To fix it add this string to our `UefiLessonsPkg/HIIStringsMan/HIIStringsMan.c` file: +``` +GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringHelpTokenId = STRING_TOKEN(STR_HELP); +``` + +You can verify after the build that now string is not optimized `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsMan/HIIStringsMan/DEBUG/HIIStringsManStrDefs.h`: +``` +// +//Unicode String ID +// +// #define $LANGUAGE_NAME 0x0000 // not referenced +// #define $PRINTABLE_LANGUAGE_NAME 0x0001 // not referenced +#define STR_HELP 0x0002 +``` + +If you execute our application you would get: +``` +FS0:\> HIIStringsMan.efi -? +HIIStringsMan application. +``` + +# Expand our manual + +Let's try to add all possible sections to our manual: +``` +#langdef en-US "English" + +#string STR_HELP #language en-US "" +".TH HIIStringsMan 0 "Simple application with a manual inside."\r\n" +".SH NAME\r\n" +"HIIStringsMan application.\r\n" +".SH SYNOPSIS\r\n" +"This is the synopsis section.\r\n" +".SH DESCRIPTION\r\n" +"This is the description section.\r\n" +".SH OPTIONS\r\n" +"This is the options section.\r\n" +".SH RETURN VALUES\r\n" +"This is the return values section.\r\n" +".SH ENVIRONMENT VARIABLES\r\n" +"This is the section for used environment variables\r\n" +".SH FILES\r\n" +"This is the section for files associated with the subject.\r\n" +".SH EXAMPLES\r\n" +"This is the section for examples and suggestions.\r\n" +".SH ERRORS\r\n" +"This is the section for errors reported by the command.\r\n" +".SH STANDARDS\r\n" +"This is the section for conformance to applicable standards.\r\n" +".SH BUGS\r\n" +"This is the section for errors and caveats.\r\n" +".SH CATEGORY\r\n" +"This is the section for categories.\r\n" +".SH CUSTOMSECTION\r\n" +"This is an example of a custom section.\r\n" +``` + +If you build and execute our app now, you would get: +``` +FS0:\> HIIStringsMan.efi -? +HIIStringsMan application. +This is the synopsis section. +This is the description section. +This is the options section. +This is the section for examples and suggestions. +``` + +As you can see not all section were printed. Let's find out why. + +First of all let's investigate what happens when we add `-?` to our command. If you look at the shell sources you'll see that if shell find `-?` as one of the command arguments it redirects command and all the rest arguments to the `help` command https://github.com/tianocore/edk2/blob/master/ShellPkg/Application/Shell/Shell.c: +``` +/** + Reprocess the command line to direct all -? to the help command. + if found, will add "help" as argv[0], and move the rest later. + @param[in,out] CmdLine pointer to the command line to update +**/ +EFI_STATUS +DoHelpUpdate( + IN OUT CHAR16 **CmdLine + ) +{ + ... + if (StrStr(CurrentParameter, L"-?") == CurrentParameter) { + CurrentParameter[0] = L' '; + CurrentParameter[1] = L' '; + NewCmdLineSize = StrSize(L"help ") + StrSize(*CmdLine); + NewCommandLine = AllocateZeroPool(NewCmdLineSize); + if (NewCommandLine == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // We know the space is sufficient since we just calculated it. + // + StrnCpyS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), L"help ", 5); + StrnCatS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), *CmdLine, StrLen(*CmdLine)); + SHELL_FREE_NON_NULL(*CmdLine); + *CmdLine = NewCommandLine; + break; + } + ... +} +``` + +You can verify that result would be the same if you use `help` command directly to our program: +``` +FS0:\> help HIIStringsMan.efi +HIIStringsMan application. +This is the synopsis section. +This is the description section. +This is the options section. +This is the section for examples and suggestions. +``` + +Now look at the `help` command source code https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellLevel3CommandsLib/Help.c: +``` +SHELL_STATUS +EFIAPI +ShellCommandRunHelp ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) + ... + // + // Get the section name for the given command name + // + if (ShellCommandLineGetFlag(Package, L"-section")) { + StrnCatGrow(&SectionToGetHelpOn, NULL, ShellCommandLineGetValue(Package, L"-section"), 0); + } else if (ShellCommandLineGetFlag(Package, L"-usage")) { + StrnCatGrow(&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS", 0); + } else if (ShellCommandLineGetFlag(Package, L"-verbose") || ShellCommandLineGetFlag(Package, L"-v")) { + } else { + // + // The output of help will display NAME, SYNOPSIS, OPTIONS, DESCRIPTION, and EXAMPLES sections. + // + StrnCatGrow (&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS,OPTIONS,DESCRIPTION,EXAMPLES", 0); + } + ... +} +``` + +Here you can see how `help` parses its incoming arguments: +- by default the sections NAME, SYNOPSIS, OPTIONS, DESCRIPTION, EXAMPLES are printed, +- it is possible to print particular section with a `-section
` argument, +- it is possible to print all sections by supplying `-verbose` or `-v` argument + +You can verify this in shell: +``` +FS0:\> help HIIStringsMan.efi -v +HIIStringsMan application. +This is the synopsis section. +This is the description section. +This is the options section. +This is the return values section. +This is the section for used environment variables +This is the section for files associated with the subject. +This is the section for examples and suggestions. +This is the section for errors reported by the command. +This is the section for conformance to applicable standards. +This is the section for errors and caveats. +This is the section for categories. +This is an example of a custom section. +FS0:\> help HIIStringsMan.efi -section BUGS +This is the section for errors and caveats. +``` +The same goes for the `-?`: +``` +FS0:\> HIIStringsMan.efi -? -section "RETURN VALUES" +This is the return values section. +FS0:\> HIIStringsMan.efi -? -verbose +HIIStringsMan application. +This is the synopsis section. +This is the description section. +This is the options section. +This is the return values section. +This is the section for used environment variables +This is the section for files associated with the subject. +This is the section for examples and suggestions. +This is the section for errors reported by the command. +This is the section for conformance to applicable standards. +This is the section for errors and caveats. +This is the section for categories. +This is an example of a custom section. +``` + +# How the ShellManParser is called + +We've started this lesson with the assumption that `ShellManParser` will parse our application strings. Let's investigate how `help` program would end up using this module. + + +In the end `ShellCommandRunHelp` would call `ShellPrintHelp` function https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellLevel3CommandsLib/Help.c: +``` +SHELL_STATUS +EFIAPI +ShellCommandRunHelp ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) + ... + Status = ShellPrintHelp(CommandToGetHelpOn, SectionToGetHelpOn, FALSE); + ... +} +``` + +This function is defined https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellLib/UefiShellLib.c. It mainly call the `GetHelpText` function from the `EFI_SHELL_PROTOCOL`: +``` +EFI_STATUS +EFIAPI +ShellPrintHelp ( + IN CONST CHAR16 *CommandToGetHelpOn, + IN CONST CHAR16 *SectionToGetHelpOn, + IN BOOLEAN PrintCommandText + ) +{ + ... + Status = gEfiShellProtocol->GetHelpText (CommandToGetHelpOn, SectionToGetHelpOn, &OutText); + ... +} +``` + + +Here is a description for this function from the UEFI Shell Specification: +``` +EFI_SHELL_PROTOCOL.GetHelpText() + +Summary: +Return help information about a specific command. + +Prototype: +typedef +EFI_STATUS +(EFIAPI *EFI_SHELL_GET_HELP_TEXT) ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Sections, + OUT CHAR16 **HelpText + ); + +Parameters: +Command Points to the null-terminated UEFI Shell command name. +Sections Points to the null-terminated comma-delimited section names to return. If NULL, then all sections will be returned. +HelpText On return, points to a callee-allocated buffer containing all specified help text. +``` + +Prototype for the `EFI_SHELL_PROTOCOL` structure in edk2 is in the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Shell.h +``` +typedef struct _EFI_SHELL_PROTOCOL { + ... + EFI_SHELL_GET_HELP_TEXT GetHelpText; + ... +} EFI_SHELL_PROTOCOL; +``` + +And initialization for this protocol is in the https://github.com/tianocore/edk2/blob/master/ShellPkg/Application/Shell/ShellProtocol.c +``` +EFI_SHELL_PROTOCOL mShellProtocol = { + ... + EfiShellGetHelpText, + ... +} +``` + +This function is defined in the same file above, and if you look at its definition you'll see that it is calling function from the `ProcessManFile` module. + + diff --git a/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.c b/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.c new file mode 100644 index 0000000..569f2d7 --- /dev/null +++ b/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.c @@ -0,0 +1,14 @@ +#include +#include + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringHelpTokenId = STRING_TOKEN(STR_HELP); + +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_SUCCESS; +} diff --git a/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf b/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf new file mode 100644 index 0000000..bf2afd2 --- /dev/null +++ b/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf @@ -0,0 +1,19 @@ +[Defines] + INF_VERSION = 1.25 + BASE_NAME = HIIStringsMan + FILE_GUID = 55fd4de5-0f19-4a23-a001-72bef56f8966 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + UEFI_HII_RESOURCE_SECTION = TRUE + +[Sources] + HIIStringsMan.c + Strings.uni + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + UefiLib diff --git a/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/Strings.uni b/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/Strings.uni new file mode 100644 index 0000000..b6e0635 --- /dev/null +++ b/Lessons/Lesson_51/UefiLessonsPkg/HIIStringsMan/Strings.uni @@ -0,0 +1,30 @@ +#langdef en-US "English" + +#string STR_HELP #language en-US "" +".TH HIIStringsMan 0 "Simple application with a manual inside."\r\n" +".SH NAME\r\n" +"HIIStringsMan application.\r\n" +".SH SYNOPSIS\r\n" +"This is the synopsis section.\r\n" +".SH DESCRIPTION\r\n" +"This is the description section.\r\n" +".SH OPTIONS\r\n" +"This is the options section.\r\n" +".SH RETURN VALUES\r\n" +"This is the return values section.\r\n" +".SH ENVIRONMENT VARIABLES\r\n" +"This is the section for used environment variables\r\n" +".SH FILES\r\n" +"This is the section for files associated with the subject.\r\n" +".SH EXAMPLES\r\n" +"This is the section for examples and suggestions.\r\n" +".SH ERRORS\r\n" +"This is the section for errors reported by the command.\r\n" +".SH STANDARDS\r\n" +"This is the section for conformance to applicable standards.\r\n" +".SH BUGS\r\n" +"This is the section for errors and caveats.\r\n" +".SH CATEGORY\r\n" +"This is the section for categories.\r\n" +".SH CUSTOMSECTION\r\n" +"This is an example of a custom section.\r\n" diff --git a/Lessons/Lesson_51/UefiLessonsPkg/UefiLessonsPkg.dsc b/Lessons/Lesson_51/UefiLessonsPkg/UefiLessonsPkg.dsc new file mode 100644 index 0000000..c409dfa --- /dev/null +++ b/Lessons/Lesson_51/UefiLessonsPkg/UefiLessonsPkg.dsc @@ -0,0 +1,69 @@ +[Defines] + DSC_SPECIFICATION = 0x0001001C + PLATFORM_GUID = 3db7270f-ffac-4139-90a4-0ae68f3f8167 + PLATFORM_VERSION = 0.01 + PLATFORM_NAME = UefiLessonsPkg + SKUID_IDENTIFIER = DEFAULT + SUPPORTED_ARCHITECTURES = X64 + BUILD_TARGETS = RELEASE + + +[LibraryClasses] + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + #PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf + ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf + FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf + HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf + SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf + UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf + #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf + SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.inf + +[Components] + UefiLessonsPkg/SimplestApp/SimplestApp.inf + UefiLessonsPkg/HelloWorld/HelloWorld.inf + UefiLessonsPkg/ImageHandle/ImageHandle.inf + UefiLessonsPkg/ImageInfo/ImageInfo.inf + UefiLessonsPkg/MemoryInfo/MemoryInfo.inf + UefiLessonsPkg/SimpleShellApp/SimpleShellApp.inf + UefiLessonsPkg/ListVariables/ListVariables.inf + UefiLessonsPkg/ShowBootVariables/ShowBootVariables.inf + UefiLessonsPkg/InteractiveApp/InteractiveApp.inf + UefiLessonsPkg/PCDLesson/PCDLesson.inf + UefiLessonsPkg/SmbiosInfo/SmbiosInfo.inf + UefiLessonsPkg/ShowTables/ShowTables.inf + UefiLessonsPkg/AcpiInfo/AcpiInfo.inf + UefiLessonsPkg/SaveBGRT/SaveBGRT.inf + UefiLessonsPkg/ListPCI/ListPCI.inf + UefiLessonsPkg/SimpleDriver/SimpleDriver.inf + UefiLessonsPkg/PCIRomInfo/PCIRomInfo.inf + UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf + UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf + UefiLessonsPkg/SimpleLibraryUser/SimpleLibraryUser.inf + UefiLessonsPkg/SimpleClassProtocol/SimpleClassProtocol.inf + UefiLessonsPkg/SimpleClassUser/SimpleClassUser.inf + UefiLessonsPkg/HotKeyDriver/HotKeyDriver.inf + UefiLessonsPkg/ShowHII/ShowHII.inf + UefiLessonsPkg/HIIStringsC/HIIStringsC.inf + UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI.inf + UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC.inf + UefiLessonsPkg/HIIStringsMan/HIIStringsMan.inf + +[PcdsFixedAtBuild] + gUefiLessonsPkgTokenSpaceGuid.PcdMyVar32_2|44 + -- cgit v1.2.3-18-g5258