aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons_uncategorized/Lesson_Configuration_Language_5/README.md
blob: 6165772eeb5397ac0efca9fb6d76a9a6536d29de (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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
We've implemented various methods to issue `EFI_HII_CONFIG_ROUTING_PROTOCOL.ExtractConfig()` call (aka HTTP GET request) to query the HII subsystem. Let's try to explore how we can issue change commands (aka HTTP POST requests) to the HII subsystem.

For this we would need the `RouteConfig()` function from the `EFI_HII_CONFIG_ROUTING_PROTOCOL`:
```
EFI_HII_CONFIG_ROUTING_PROTOCOL.RouteConfig()

Summary:
This function processes the results of processing forms and routes it to the appropriate handlers or storage.

Prototype:

typedef
EFI_STATUS
 (EFIAPI * EFI_HII_ROUTE_CONFIG ) (
 IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This,
 IN CONST EFI_STRING Configuration,
 OUT EFI_STRING *Progress
 );

Parameters:
This		Points to the EFI_HII_CONFIG_ROUTING_PROTOCOL instance.
Configuration	A null-terminated string in <MultiConfigResp> format.
Progress	A pointer to a string filled in with the offset of the most recent ‘&’ before the first
		failing name / value pair (or the beginning of the string if the failure is in the first
		name / value pair) or the terminating NULL if all was successful.

Description:
This function routes the results of processing forms to the appropriate targets.
```

In our `HIIConfig` application we would add two methods to issue change (=route) requests:
- by providing `<ConfigStr>`
- by providing `<Guid> <Name> <Path> <Offset> <Width> <Value>` combination

```cpp
VOID Usage()
{
  Print(L"Usage:\n");
  ...
  Print(L"HIIConfig.efi route <ConfigStr>\n");
  Print(L"HIIConfig.efi route <Guid> <Name> <Path> <Offset> <Width> <Value>\n");
}
```
This is similar to the inteface that we've added for our `export` command.

# `HIIConfig.efi route <ConfigStr>`

Here is a code that implements `HIIConfig.efi route <ConfigStr>` functionality:
```cpp
EFI_STRING Request;
EFI_STRING Progress;
EFI_STRING Result;
if (!StrCmp(Argv[1], L"dump")) {
  ...
} else if (!StrCmp(Argv[1], L"extract")) {
  ...
} else if (!StrCmp(Argv[1], L"route")) {
  if (Argc == 3) {
    Request = Argv[2];
  } else {
    Print(L"Error! Wrong arguments\n");
    Usage();
    return EFI_INVALID_PARAMETER;
  }
  Print(L"Request: %s\n", Request);
  Status = gHiiConfigRouting->RouteConfig(gHiiConfigRouting,
                                          Request,
                                          &Progress);
  if (StrCmp(Progress, L'\0')) {
    Print(L"Part of string was unparsed: %s\n", Progress);
    if (StrCmp(Progress, Request)) {
      Print(L"IMPORTANT: part of the data was written!\n");
    }
  }
  if (EFI_ERROR(Status)) {
    Print(L"Error! RouteConfig returned %r\n", Status);
    return Status;
  }
  FreePool(Result);
} else {
  Print(L"Error! Wrong arguments\n");
  Usage();
  return EFI_INVALID_PARAMETER;
}
```

The code is similar to the `HIIConfig.efi extract <ConfigStr>` call handling code. Except in this case we don't have any response string to print.

Also like before we check if the `Progress` string is empty after the call. But in case of `route` it is very important, as if the `Progress` is not empty and is not equal to the initial `Request` string it would mean that the request was processed partitially. In this case even if there would be an error for the overall `gHiiConfigRouting->RouteConfig` call, part of the data still was written to the HII subsystem! So in this case we inform a user on this subject.

Let's test our application on the `HIIFormCheckbox.efi` form. Load it's driver into the shell:
```
FS0:\> load HIIFormCheckbox.efi
```

Before we've used the form browser to check the actual form data, but as you know in case of a `efistorage` we can simply use the `dmpstore` command to get the necessary data not leaving the CLI:
```
FS0:\> dmpstore -guid ef2acc91-7b50-4ab9-ab67-2b04f8bc135e
Variable NV+BS 'EF2ACC91-7B50-4AB9-AB67-2B04F8BC135E:CheckboxValue' DataSize = 0x01
  00000000: 01                                               *.*
```

Now let's try to change it to 0 with the `...&OFFSET=0000&WIDTH=0001&VALUE=00` command. The header of the data you can get from the `HIIConfig.efi extract ef2acc91-7b50-4ab9-ab67-2b04f8bc135e CheckboxValue VenHw(ef2acc91-7b50-4ab9-ab67-2b04f8bc135e)` output calls that we've made before. The route command call would look like this:
```
FS0:\> HIIConfig.efi route GUID=91cc2aef507bb94aab672b04f8bc135e&NAME=0043006800650063006b0062006f007800560061006c00750065&PATH=0104140091cc2aef507bb94aab672b04f8bc135e7fff0400&OFFSET=0000&WIDTH=0001&VALUE=00

Request: GUID=91cc2aef507bb94aab672b04f8bc135e&NAME=0043006800650063006b0062006f007800560061006c00750065&PATH=0104140091cc2aef507bb94aab672b04f8bc135e7fff0400&OFFSET=0000&WIDTH=0001&VALUE=00
```

After that you can verify that the data indeed was changed:
```
FS0:\> dmpstore -guid ef2acc91-7b50-4ab9-ab67-2b04f8bc135e
Variable NV+BS 'EF2ACC91-7B50-4AB9-AB67-2B04F8BC135E:CheckboxValue' DataSize = 0x01
  00000000: 00                                               *.*
```

If you want to, you can check that the Form browser also displays the updated value.

Now you can set the checkbox element back with the `...&OFFSET=0000&WIDTH=0001&VALUE=01` call:
```
FS0:\> HIIConfig.efi route GUID=91cc2aef507bb94aab672b04f8bc135e&NAME=0043006800650063006b0062006f007800560061006c00750065&PATH=0104140091cc2aef507bb94aab672b04f8bc135e7fff0400&OFFSET=0000&WIDTH=0001&VALUE=01

Request: GUID=91cc2aef507bb94aab672b04f8bc135e&NAME=0043006800650063006b0062006f007800560061006c00750065&PATH=0104140091cc2aef507bb94aab672b04f8bc135e7fff0400&OFFSET=0000&WIDTH=0001&VALUE=01

FS0:\> dmpstore -guid ef2acc91-7b50-4ab9-ab67-2b04f8bc135e
Variable NV+BS 'EF2ACC91-7B50-4AB9-AB67-2B04F8BC135E:CheckboxValue' DataSize = 0x01
  00000000: 01                                               *.*
```

# `HIIConfig.efi route <Guid> <Name> <Path> <Offset> <Width> <Value>`

Finally let's add a possibility to call the `route` command by supplying the arguments in a human readable format:
```
HIIConfig.efi route <Guid> <Name> <Path> <Offset> <Width> <Value>
```

Here are the necessary modifications to the code:
```cpp
EFI_STRING Request;
EFI_STRING Progress;
EFI_STRING Result;
if (!StrCmp(Argv[1], L"dump")) {
  ...
} else if (!StrCmp(Argv[1], L"extract")) {
  ...
} else if (!StrCmp(Argv[1], L"route")) {
  if (Argc == 3) {
    Request = Argv[2];
  } else if (Argc == 8) {                                                  // <---- HIIConfig.efi route <Guid> <Name> <Path> <Offset> <Width> <Value>
    Status = CreateCfgHeader(Argv[2], Argv[3], Argv[4], &Request);
    if (EFI_ERROR(Status)) {
      return Status;
    }
    EFI_STRING OffsetStr = Argv[5];
    EFI_STRING WidthStr = Argv[6];
    EFI_STRING ValueStr = Argv[7];
    UINTN Size = (StrLen(Request) + StrLen(L"&OFFSET=") + StrLen(OffsetStr) + StrLen(L"&WIDTH=") + StrLen(WidthStr) +
                  StrLen(L"&VALUE=") + StrLen(ValueStr) + 1) * sizeof(CHAR16);
    EFI_STRING TempRequest = AllocateZeroPool(Size);
    UnicodeSPrint(TempRequest, Size, L"%s&OFFSET=%s&WIDTH=%s&VALUE=%s", Request, OffsetStr, WidthStr, ValueStr);
    FreePool(Request);
    Request = TempRequest;
  } else {
    Print(L"Error! Wrong arguments\n");
    Usage();
    return EFI_INVALID_PARAMETER;
  }
  Print(L"Request: %s\n", Request);
  Status = gHiiConfigRouting->RouteConfig(gHiiConfigRouting,
                                          Request,
                                          &Progress);
  if (StrCmp(Progress, L'\0')) {
    Print(L"Part of string was unparsed: %s\n", Progress);
    if (StrCmp(Progress, Request)) {
      Print(L"IMPORTANT: part of the data was written!\n");
    }
  }
  if (Argc == 8) {                                                        // <----- don't forget to free Request if it was allocated dynamically
    FreePool(Request);
  }
  if (EFI_ERROR(Status)) {
    Print(L"Error! RouteConfig returned %r\n", Status);
    return Status;
  }
  FreePool(Result);
} else {
  Print(L"Error! Wrong arguments\n");
  Usage();
  return EFI_INVALID_PARAMETER;
}
```
Like with the similar `extract` case we construct configuration header with the `CreateCfgHeader` function and then append the rest of the elements via the `UnicodeSPrint` call. As in this case we allocate Request string dynamically we need to free it in the end. As the `Progress` is just a pointer to some part of the `Request`, we can do it only after the `Progress` is checked for errors.

And here is a test for the new functionality:
```
FS0:\> load HIIFormCheckbox.efi
Image 'FS0:\HIIFormCheckbox.efi' loaded at 6877000 - Success
FS0:\> dmpstore -guid ef2acc91-7b50-4ab9-ab67-2b04f8bc135e
Variable NV+BS 'EF2ACC91-7B50-4AB9-AB67-2B04F8BC135E:CheckboxValue' DataSize = 0x01
  00000000: 01                                               *.*

FS0:\> HIIConfig.efi route ef2acc91-7b50-4ab9-ab67-2b04f8bc135e CheckboxValue VenHw(ef2acc91-7b50-4ab9-ab67-2b04f8bc135e) 0 1 0
Request: GUID=91cc2aef507bb94aab672b04f8bc135e&NAME=0043006800650063006b0062006f007800560061006c00750065&PATH=0104140091cc2aef507bb94aab672b04f8bc135e7fff0400&OFFSET=0&WIDTH=1&VALUE=0

FS0:\> dmpstore -guid ef2acc91-7b50-4ab9-ab67-2b04f8bc135e
Variable NV+BS 'EF2ACC91-7B50-4AB9-AB67-2B04F8BC135E:CheckboxValue' DataSize = 0x01
  00000000: 00                                               *.*

FS0:\> HIIConfig.efi route ef2acc91-7b50-4ab9-ab67-2b04f8bc135e CheckboxValue VenHw(ef2acc91-7b50-4ab9-ab67-2b04f8bc135e) 0 1 1
Request: GUID=91cc2aef507bb94aab672b04f8bc135e&NAME=0043006800650063006b0062006f007800560061006c00750065&PATH=0104140091cc2aef507bb94aab672b04f8bc135e7fff0400&OFFSET=0&WIDTH=1&VALUE=1

FS0:\> dmpstore -guid ef2acc91-7b50-4ab9-ab67-2b04f8bc135e
Variable NV+BS 'EF2ACC91-7B50-4AB9-AB67-2B04F8BC135E:CheckboxValue' DataSize = 0x01
  00000000: 01                                               *.*

```

If you want to, you can issue route requests to our `HIIFormDataElements.efi` form storage, just remember these things:
- like with the `extract` case it is not mandatory for the OFFSET/WIDTH to be on element boundaries,
- don't forget to pay attention to the byte order in the `VALUE=` string and to the order of bytes in the actual form elemet. The order is reversed. So you will need to pass string element data backbards (i.e `VALUE=<o><l><l><e><H>`).
- changing element via `RouteConfig()` call can bypass some VFR limitations. For example even if numeric element has `minimum` and `maximum` attributes set like this:
```
numeric
  ...
  minimum = 0,
  maximum = 10,
  ...
endnumric
```
it would still possible to set a value outside of these limits.