aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons_uncategorized/Lesson_Configuration_Language_8/README.md
blob: b1297b63578bbefb945f572fc3612d4ad285383a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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 <ConfigAltResp> 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 <ConfigResp> 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
                    <MultiConfigRequest> 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")