From 03acca6a5ac17372e17336a01069a1d97771d392 Mon Sep 17 00:00:00 2001 From: Konstantin Aladyshev Date: Mon, 26 Sep 2022 15:35:40 +0300 Subject: Use function to set defaults Signed-off-by: Konstantin Aladyshev --- .../Lesson_Configuration_Language_8/README.md | 151 +++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 Lessons_uncategorized/Lesson_Configuration_Language_8/README.md (limited to 'Lessons_uncategorized/Lesson_Configuration_Language_8/README.md') diff --git a/Lessons_uncategorized/Lesson_Configuration_Language_8/README.md b/Lessons_uncategorized/Lesson_Configuration_Language_8/README.md new file mode 100644 index 0000000..b1297b6 --- /dev/null +++ b/Lessons_uncategorized/Lesson_Configuration_Language_8/README.md @@ -0,0 +1,151 @@ +When we load our HII form drivers with `efivarstore` for the first time, we need to create a variable for the form storage. Up until now we've used the following pattern in the driver's entry point code: +```cpp +UINTN BufferSize; +UEFI_VARIABLE_STRUCTURE EfiVarstore; +BufferSize = sizeof(UEFI_VARIABLE_STRUCTURE); +Status = gRT->GetVariable ( + UEFIVariableName, + &UEFIVariableGuid, + NULL, + &BufferSize, + &EfiVarstore); +if (EFI_ERROR(Status)) { + ZeroMem(&EfiVarstore, sizeof(EfiVarstore)); <---- Data structure initialization + Status = gRT->SetVariable( + UEFIVariableName, + &UEFIVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof(EfiVarstore), + &EfiVarstore); + if (EFI_ERROR(Status)) { + Print(L"Error! Can't create variable! %r\n", Status); + } +} +``` +Here we create an UEFI variable for the form storage structure if it is not already present in the system. When we create a new variable we use `ZeroMem` for the structure initialization. This is why when we look at our form for the first time we see that checkbox is unset, numeric is equal to 0, string element is empty and so on. + +If we want to start not from this, but from some sane defaults, instead of `ZeroMem` we can use some custom function that would initialize the structure fields as we want to. But the thing is that we already have a method to declare default values. VFR syntax allow us to set defaults for the form elements with the `default` keyword. You already know how to set elements to these defaults interactively from the Form Browser. But what if we want to start from these values? + +Let's investigate how we can use the `default` data from the VFR to initialize form fields non-interactively. + +Basically we already know that. We need to: +- use `EFI_HII_CONFIG_ROUTING_PROTOCOL.ExtractConfig()` to get Form storage configuration, +- strip default configuration part (`ALTCFG=0000&...`) from the response, +- use it to constuct configuration request for the `EFI_HII_CONFIG_ROUTING_PROTOCOL.RouteConfig()` function to set elements to their default values. + +Stripping the necessary part from the configuration string can be a burden, this is why UEFI specification offers a helper function for that: +``` +EFI_HII_CONFIG_ROUTING_PROTOCOL.GetAltCfg() + +Summary: +This helper function is to be called by drivers to extract portions of a larger configuration string. + +Prototype: +typedef +EFI_STATUS + (EFIAPI * EFI_HII_GET_ALT_CFG ) ( + IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This, + IN CONST EFI_STRING ConfigResp, + IN CONST EFI_GUID *Guid, + IN CONST EFI_STRING Name, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CONST EFI_STRING AltCfgId, + OUT EFI_STRING *AltCfgResp + ); + +Parameters: +This Points to the EFI_HII_CONFIG_ROUTING_PROTOCOL instance +ConfigResp A null-terminated string in format +Guid A pointer to the GUID value to search for in the routing portion of the ConfigResp + string when retrieving the requested data. If Guid is NULL, then all GUID values will be searched for +Name A pointer to the NAME value to search for in the routing portion of the ConfigResp + string when retrieving the requested data. If Name is NULL, then all Name values will be searched for +DevicePath A pointer to the PATH value to search for in the routing portion of the ConfigResp + string when retrieving the requested data. If DevicePath is NULL, then all DevicePath values will be searched for +AltCfgId A pointer to the ALTCFG value to search for in the routing portion of the + ConfigResp string when retrieving the requested data. If this parameter is NULL, then the current setting will be retrieved +AltCfgResp A pointer to a buffer which will be allocated by the function which contains the + retrieved string as requested. This buffer is only allocated if the call was successful. + The null-terminated string will be in format + +Description: +This function retrieves the requested portion of the configuration string from a larger configuration string. This function will use the Guid, Name, and DevicePath parameters to find the appropriate +section of the ConfigResp string. Upon finding this portion of the string, it will use the AltCfgId parameter to find the appropriate instance of data in the ConfigResp string. Once found, the found +data will be copied to a buffer which is allocated by the function so that it can be returned to the caller. The caller is responsible for freeing this allocated buffer. +``` + +With that our steps would be: +- use `EFI_HII_CONFIG_ROUTING_PROTOCOL.ExtractConfig()` to get Form storage configuration +- use `EFI_HII_CONFIG_ROUTING_PROTOCOL.GetAltCfg()` to get necessary default configuration +- use `EFI_HII_CONFIG_ROUTING_PROTOCOL.RouteConfig()` to set elements to their default values + +But fortunately we don't have to do any of that as `HiiLib` offers us the `HiiSetToDefaults` function [https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiLib.c](https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiLib.c): +``` +/** + Reset the default value specified by DefaultId to the driver + configuration got by Request string. + NULL request string support depends on the ExportConfig interface of + HiiConfigRouting protocol in UEFI specification. + @param Request A null-terminated Unicode string in + format. It can be NULL. + If it is NULL, all configuration for the + entirety of the current HII database will be reset. + @param DefaultId Specifies the type of defaults to retrieve. + @retval TRUE The default value is set successfully. + @retval FALSE The default value can't be found and set. +**/ +BOOLEAN +EFIAPI +HiiSetToDefaults ( + IN CONST EFI_STRING Request OPTIONAL, + IN UINT16 DefaultId + ) +``` +So all we need to do is to construct configuration header for the driver storage and call this function. And practically we already know how to do it. + +# `HIIFormDataElementsWithDefaultsSet` + +To keep lessons separate I would create a new driver `HIIFormDataElementsWithDefaultsSet`. But basically it based on our `HIIFormDataElements` driver code, so I would explain only things that differ. + +We need to add new code only if the respective UEFI variable is not set: +```cpp +Status = gRT->GetVariable(...) +if (EFI_ERROR(Status)) { + ZeroMem(...); + Status = gRT->SetVariable(...); + if (EFI_ERROR(Status)) { + Print(L"Error! Can't create variable! %r\n", Status); + } + <...> <------ add code that would set defaults to form elements +} +``` +As in this code we would call `EFI_HII_CONFIG_ROUTING_PROTOCOL` functions expecting output from our form, the code above must be after the `HiiAddPackages` call: +```cpp +mHiiHandle = HiiAddPackages(...); + +Status = gRT->GetVariable(...) +if (EFI_ERROR(Status)) { + ZeroMem(...); + Status = gRT->SetVariable(...); + if (EFI_ERROR(Status)) { + Print(L"Error! Can't create variable! %r\n", Status); + } + <...> +} +``` +I point out that fact, because earlier we did things in the opposite way. + +Now let's get to our initialization code. As in our driver code we already have the `DriverHandle` and storage `GUID` and `Name` we can just call `HiiConstructConfigHdr` function to create necessary confugartion header string. After that we just use it with necessary `DefaultId` number to set defaults. Let's use 0 as `DefaultId` to set standard defaults: +``` +EFI_STRING ConfigStr = HiiConstructConfigHdr(&UEFIVariableGuid, UEFIVariableName, mDriverHandle); +UINT16 DefaultId = 0; +if (!HiiSetToDefaults(ConfigStr, DefaultId)) { + Print(L"Error! Can't set default configuration #%d\n", DefaultId); +} +``` +This is all we need to add. And if we want to set `Manufacture defaults`, all we need to do is replace `DefaultId = 0` to `DefaultId = 1`. + +If you build our driver and load it in UEFI shell, you'll see that now the respective form is filled with default values even at the first launch: + +![1](1.png?raw=true "1") + -- cgit v1.2.3-18-g5258