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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
|
# `oneof` element
`oneof` element allows to select an option from the predefined set (https://edk2-docs.gitbook.io/edk-ii-vfr-specification/2_vfr_description_in_bnf/211_vfr_form_definition#2.11.6.6.2-vfr-oneof-statement-definition)
Add the folowing code to the `UefiLessonsPkg/HIIFormDataElements/Form.vfr`
```
oneof
varid = FormData.OneOfValue,
prompt = STRING_TOKEN(ONEOF_PROMPT),
help = STRING_TOKEN(ONEOF_HELP),
option text = STRING_TOKEN(ONEOF_OPTION1), value = 0x00, flags = DEFAULT;
option text = STRING_TOKEN(ONEOF_OPTION2), value = 0x33, flags = 0;
option text = STRING_TOKEN(ONEOF_OPTION3), value = 0x55, flags = 0;
endoneof;
```
Add new string tokens to the `UefiLessonsPkg/HIIFormDataElements/Strings.uni`:
```
#string ONEOF_PROMPT #language en-US "OneOf list prompt"
#string ONEOF_HELP #language en-US "OneOf list help"
#string ONEOF_OPTION1 #language en-US "OneOf list option 1"
#string ONEOF_OPTION2 #language en-US "OneOf list option 2"
#string ONEOF_OPTION3 #language en-US "OneOf list option 3"
```
As for data, add this to the `UefiLessonsPkg/HIIFormDataElements/Data.h`:
```
typedef struct {
...
UINT8 OneOf;
} UEFI_VARIABLE_STRUCTURE;
```
Once you load the form, you would get:
![OneOf1](OneOf1.png?raw=true "OneOf1")
You can select one of the available options:
![OneOf2](OneOf2.png?raw=true "OneOf2")
If you select the option 2:
![OneOf3](OneOf3.png?raw=true "OneOf3")
The variable field would get the value `0x33`. If you want to, you can verify it with the `dmpstore` command.
## IFR
IFR code would look like this (`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements/DEBUG/Form.lst`):
```
oneof
>000000B4: 05 91 0F 00 10 00 06 00 01 00 20 00 00 10 00 55 00
varid = FormData.OneOfValue,
prompt = STRING_TOKEN(0x000F),
help = STRING_TOKEN(0x0010),
option text = STRING_TOKEN(0x0011), value = 0x00, flags = DEFAULT;
>000000C5: 09 07 11 00 10 00 00
option text = STRING_TOKEN(0x0012), value = 0x33, flags = 0;
>000000CC: 09 07 12 00 00 00 33
option text = STRING_TOKEN(0x0013), value = 0x55, flags = 0;
>000000D3: 09 07 13 00 00 00 55
endoneof;
>000000DA: 29 02
```
The first opcodes are:
```
EFI_IFR_ONE_OF
Summary:
Creates a select-one-of question.
Prototype:
#define EFI_IFR_ONE_OF_OP 0x05
typedef struct _EFI_IFR_ONE_OF {
EFI_IFR_OP_HEADER Header;
EFI_IFR_QUESTION_HEADER Question;
UINT8 Flags;
union {
struct {
UINT8 MinValue;
UINT8 MaxValue;
UINT8 Step;
} u8;
struct {
UINT16 MinValue;
UINT16 MaxValue;
UINT16 Step;
} u16;
struct {
UINT32 MinValue;
UINT32 MaxValue;
UINT32 Step;
} u32;
struct {
UINT64 MinValue;
UINT64 MaxValue;
UINT64 Step;
} u64;
} data;
} EFI_IFR_ONE_OF;
Members:
Header The sequence that defines the type of opcode as well as the length of the opcode being defined.
Header.OpCode = EFI_IFR_ONE_OF_OP.
Question The standard question header.
Flags Specifies flags related to the numeric question.
MinValue The minimum value to be accepted by the browser for this opcode.
The size of the data field may vary from 8 to 64 bits, depending on the size specified in Flags
MaxValue The maximum value to be accepted by the browser for this opcode.
The size of the data field may vary from 8 to 64 bits, depending on the size specified in Flags
Step Defines the amount to increment or decrement the value each time a user requests a value change.
If the step value is 0, then the input mechanism for the numeric value is to be free-form
and require the user to type in the actual value.
The size of the data field may vary from 8 to 64 bits, depending on the size specified in Flags
Description:
This opcode creates a select-on-of object, where the user must select from one of the nested options.
This is identical to EFI_IFR_NUMERIC.
```
And here is a definition for the options opcodes:
```
EFI_IFR_ONE_OF_OPTION
Summary:
Creates a pre-defined option for a question.
Prototype:
#define EFI_IFR_ONE_OF_OPTION_OP 0x09
typedef struct _EFI_IFR_ONE_OF_OPTION {
EFI_IFR_OP_HEADER Header;
EFI_STRING_ID Option;
UINT8 Flags;
UINT8 Type;
EFI_IFR_TYPE_VALUE Value;
} EFI_IFR_ONE_OF_OPTION;
Members:
Header The sequence that defines the type of opcode as well as the length of the opcode being defined.
Header.OpCode = EFI_IFR_ONE_OF_OPTION_OP.
Option The string token reference to the option description string for this particular opcode.
Flags Specifies the flags associated with the current option (EFI_IFR_OPTION_x)
Type Specifies the type of the option’s value (See EFI_IFR_TYPE)
Value The union of all of the different possible values. The actual contents (and size)
of the field depends on Type.
```
Description says that the content of the `Value` field is dependent on the value of the `Type` field. Here are all the possible value types with comments of their type flags:
```
typedef union {
UINT8 u8; // EFI_IFR_TYPE_NUM_SIZE_8
UINT16 u16; // EFI_IFR_TYPE_NUM_SIZE_16
UINT32 u32; // EFI_IFR_TYPE_NUM_SIZE_32
UINT64 u64; // EFI_IFR_TYPE_NUM_SIZE_64
BOOLEAN b; // EFI_IFR_TYPE_BOOLEAN
EFI_HII_TIME time; // EFI_IFR_TYPE_TIME
EFI_HII_DATE date; // EFI_IFR_TYPE_DATE
EFI_STRING_ID string; // EFI_IFR_TYPE_STRING, EFI_IFR_TYPE_ACTION
EFI_HII_REF ref; // EFI_IFR_TYPE_REF
// UINT8 buffer[]; // EFI_IFR_TYPE_BUFFER
} EFI_IFR_TYPE_VALUE;
```
And here are the actual values for the `EFI_IFR_TYPE_x` defines:
```
#define EFI_IFR_TYPE_NUM_SIZE_8 0x00
#define EFI_IFR_TYPE_NUM_SIZE_16 0x01
#define EFI_IFR_TYPE_NUM_SIZE_32 0x02
#define EFI_IFR_TYPE_NUM_SIZE_64 0x03
#define EFI_IFR_TYPE_BOOLEAN 0x04
#define EFI_IFR_TYPE_TIME 0x05
#define EFI_IFR_TYPE_DATE 0x06
#define EFI_IFR_TYPE_STRING 0x07
#define EFI_IFR_TYPE_OTHER 0x08
#define EFI_IFR_TYPE_UNDEFINED 0x09
#define EFI_IFR_TYPE_ACTION 0x0A
#define EFI_IFR_TYPE_BUFFER 0x0B
#define EFI_IFR_TYPE_REF 0x0C
```
In our structure we've declared data as `UINT8 OneOf`. Therefore compiler automatically have deducted our type flag as `EFI_IFR_TYPE_NUM_SIZE_8`. If we change that to `UINT16 OneOf`, compiler would change the type field value to the `EFI_IFR_TYPE_NUM_SIZE_16`.
Compare this output with the one that we had before:
```
oneof
>000000B4: 05 94 0F 00 10 00 06 00 01 00 20 00 00 11 00 00 55 00 00 00
varid = FormData.OneOfValue,
prompt = STRING_TOKEN(0x000F),
help = STRING_TOKEN(0x0010),
option text = STRING_TOKEN(0x0011), value = 0x00, flags = DEFAULT;
>000000C8: 09 08 11 00 11 01 00 00
option text = STRING_TOKEN(0x0012), value = 0x33, flags = 0;
>000000D0: 09 08 12 00 01 01 33 00
option text = STRING_TOKEN(0x0013), value = 0x55, flags = 0;
>000000D8: 09 08 13 00 01 01 55 00
endoneof;
>000000E0: 29 02
```
Although `EFI_IFR_TYPE_VALUE` can take many values besides numeric if you try to set its type to the `EFI_HII_DATE OneOfValue` for example, you would get an error:
```
ERROR 12288: OneOf question only support UINT8, UINT16, UINT32 and UINT64 data type
```
So only subtype of `EFI_IFR_TYPE_VALUE` is supported.
# `orderedlist` element
Another element with options is the `orderedlist` element (https://edk2-docs.gitbook.io/edk-ii-vfr-specification/2_vfr_description_in_bnf/211_vfr_form_definition#2.11.6.8-vfr-orderedlist-statement-definition)
Add this to the `UefiLessonsPkg/HIIFormDataElements/Form.vfr`:
```
orderedlist
varid = FormData.OrderedListValue,
prompt = STRING_TOKEN(ORDERED_LIST_PROMPT),
help = STRING_TOKEN(ORDERED_LIST_HELP),
option text = STRING_TOKEN(ORDERED_LIST_OPTION1), value = 0x0A, flags = 0;
option text = STRING_TOKEN(ORDERED_LIST_OPTION2), value = 0x0B, flags = 0;
option text = STRING_TOKEN(ORDERED_LIST_OPTION3), value = 0x0C, flags = 0;
endlist;
```
Add strings to the `UefiLessonsPkg/HIIFormDataElements/Strings.uni`:
```
#string ORDERED_LIST_PROMPT #language en-US "Ordered list prompt"
#string ORDERED_LIST_HELP #language en-US "Ordered list help"
#string ORDERED_LIST_OPTION1 #language en-US "Ordered list option 1"
#string ORDERED_LIST_OPTION2 #language en-US "Ordered list option 2"
#string ORDERED_LIST_OPTION3 #language en-US "Ordered list option 3"
```
If we have 3 options, we need to declare array of 3 elements. Let's declare them as `UINT8`:
```
typedef struct {
...
UINT8 OrderedListValue[3];
} UEFI_VARIABLE_STRUCTURE;
```
On load our form would look like this:
![Orderedlist1](Orderedlist1.png?raw=true "Orderedlist1")
If you select the element:
![Orderedlist2](Orderedlist2.png?raw=true "Orderedlist2")
You can change its order by moving it up or down with the help of `+` and `-` keys:
![Orderedlist3](Orderedlist3.png?raw=true "Orderedlist3")
Change order to `2 1 3` and save it with `F10`:
![Orderedlist4](Orderedlist4.png?raw=true "Orderedlist4")
This would get the following data in the `OrderedListValue` field of our UEFI variable:
```
0B 0A 0C
```
## IFR
Let's look at the IFR code (`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements/DEBUG/Form.lst`):
```
orderedlist
>000000DC: 23 8F 14 00 15 00 07 00 01 00 21 00 00 03 00
varid = FormData.OrderedListValue,
prompt = STRING_TOKEN(0x0014),
help = STRING_TOKEN(0x0015),
option text = STRING_TOKEN(0x0016), value = 0x0A, flags = 0;
>000000EB: 09 07 16 00 00 00 0A
option text = STRING_TOKEN(0x0017), value = 0x0B, flags = 0;
>000000F2: 09 07 17 00 00 00 0B
option text = STRING_TOKEN(0x0018), value = 0x0C, flags = 0;
>000000F9: 09 07 18 00 00 00 0C
endlist;
>00000100: 29 02
```
First code element is `EFI_IFR_ORDERED_LIST`:
```
EFI_IFR_ORDERED_LIST
Summary:
Creates a set question using an ordered list.
#define EFI_IFR_ORDERED_LIST_OP 0x23
typedef struct _EFI_IFR_ORDERED_LIST {
EFI_IFR_OP_HEADER Header;
EFI_IFR_QUESTION_HEADER Question;
UINT8 MaxContainers;
UINT8 Flags;
} EFI_IFR_ORDERED_LIST;
Members:
Header The byte sequence that defines the type of opcode as well as the length of the opcode being defined.
Header.OpCode = EFI_IFR_ORDERED_LIST_OP.
Question The standard question header.
MaxContainers The maximum number of entries for which this tag will maintain an order.
This value also identifies the size of the storage associated with this tag’s ordering array.
Flags A bit-mask that determines which unique settings are active for this opcode.
Description:
Create an ordered list question in the current form. One thing to note is that valid values for the options
in ordered lists should never be a 0.
```
If you match data to the fields, you would get the following data for the new fields:
```
UINT8 MaxContainers; // 0x03
UINT8 Flags; // 0x00
```
Everything is in order, our element has 3 options, and therefore `MaxContainers=0x03`.
If you look at the options IFR code you could see that the options are encoded with the same opcode `EFI_IFR_ONE_OF_OPTION` that was used in the `oneof` element.
Here the principle is the same. If you encode our data as `UINT16` instead of `UINT8`
```
UINT16 OrderedListValue[3];
```
The compiler would encode options as `EFI_IFR_TYPE_NUM_SIZE_16` (=`UINT16`) and in the storage they would look as:
```
0B 00 0A 00 0C 00
```
With the `orderedlist` it is even possible to use non-numeric data types. For example you can encode variable as array of dates:
```
EFI_HII_DATE OrderedListValue[3];
```
For this off course you need to change the code in VFR:
```
orderedlist
varid = FormData.OrderedListValue,
prompt = STRING_TOKEN(ORDERED_LIST_PROMPT),
help = STRING_TOKEN(ORDERED_LIST_HELP),
option text = STRING_TOKEN(ORDERED_LIST_OPTION1), value = 2021/7/4, flags = 0;
option text = STRING_TOKEN(ORDERED_LIST_OPTION2), value = 2022/8/5, flags = 0;
option text = STRING_TOKEN(ORDERED_LIST_OPTION3), value = 2023/9/6, flags = 0;
endlist;
```
If you parse IFR data for such code, you could see that every option is encoded with `#define EFI_IFR_TYPE_DATE 0x06` type:
```
orderedlist
>000000DC: 23 8F 14 00 15 00 07 00 01 00 21 00 00 0C 00
varid = FormData.OrderedListValue,
prompt = STRING_TOKEN(0x0014),
help = STRING_TOKEN(0x0015),
option text = STRING_TOKEN(0x0016), value = 2021/7/4, flags = 0;
>000000EB: 09 0A 16 00 06 06 E5 07 07 04
option text = STRING_TOKEN(0x0017), value = 2022/8/5, flags = 0;
>000000F5: 09 0A 17 00 06 06 E6 07 08 05
option text = STRING_TOKEN(0x0018), value = 2023/9/6, flags = 0;
>000000FF: 09 0A 18 00 06 06 E7 07 09 06
endlist;
>00000109: 29 02
```
If the data array has a size lower than amount of available options, everything would compile, but in the HII, you would see only the first `array size` options. For example `UINT8 OrderedListValue[2]` would result to:
![Orderedlistr5](Orderedlist5.png?raw=true "Orderedlist5")
|