aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons_uncategorized/Lesson_Varstore_5/README.md
blob: 589263583dd72415dbbf2b76988f4537abb541df (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
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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
In all our experments the driver `Callback()` function was never called.

This has happend because none of the driver form elements had `EFI_IFR_FLAG_CALLBACK` flag set.

The `EFI_IFR_FLAG_CALLBACK` is one of the flags for the `EFI_IFR_QUESTION_HEADER.flags` field [https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h):
```cpp
//
// Flag values of EFI_IFR_QUESTION_HEADER
//
#define EFI_IFR_FLAG_READ_ONLY           0x01
#define EFI_IFR_FLAG_CALLBACK            0x04
#define EFI_IFR_FLAG_RESET_REQUIRED      0x10
#define EFI_IFR_FLAG_REST_STYLE          0x20
#define EFI_IFR_FLAG_RECONNECT_REQUIRED  0x40
#define EFI_IFR_FLAG_OPTIONS_ONLY        0x80
```
The `EFI_IFR_QUESTION_HEADER` in turn is a structure present in the IFR code of all our data elements:
```
EFI_IFR_CHECKBOX     - checkbox
EFI_IFR_NUMERIC      - numeric
EFI_IFR_STRING       - string
EFI_IFR_DATE         - date
EFI_IFR_TIME         - time
EFI_IFR_ONE_OF       - oneof
EFI_IFR_ORDERED_LIST - orderedlist
```

So let's add `EFI_IFR_FLAG_CALLBACK` to one of our form elements. For example let's add it to the `numeric` element. In the VFR code the necessary flag is indicated by the `INTERACTIVE` keyword:
```
 numeric
   name = NumericQuestion,
   varid = FormData.NumericValue,
   prompt = STRING_TOKEN(NUMERIC_PROMPT),
   help = STRING_TOKEN(NUMERIC_HELP),
-  flags = NUMERIC_SIZE_2 | DISPLAY_UINT_HEX,
+  flags = NUMERIC_SIZE_2 | DISPLAY_UINT_HEX | INTERACTIVE,
   minimum = 0,
   maximum = 10,
   step = 1,
   default = 7, defaultstore = StandardDefault,
   default = 8, defaultstore = ManufactureDefault,
endnumeric;
```

# `EFI_HII_CONFIG_ACCESS_PROTOCOL.CallBack()`

Here is a description for the `EFI_HII_CONFIG_ACCESS_PROTOCOL.CallBack()` function from the UEFI specification:
```
EFI_HII_CONFIG_ACCESS_PROTOCOL.CallBack()

Summary:
This function is called to provide results data to the driver.

Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_HII_ACCESS_FORM_CALLBACK) (
 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
 IN EFI_BROWSER_ACTION Action,
 IN EFI_QUESTION_ID QuestionId,
 IN UINT8 Type
 IN OUT EFI_IFR_TYPE_VALUE *Value,
 OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest,
);

Parameters:
This		Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL
Action		Specifies the type of action taken by the browser
QuestionId	A unique value which is sent to the original exporting driver so that it can identify the
		type of data to expect. The format of the data tends to vary based on the opcode that generated the callback
Type		The type of value for the question
Value		A pointer to the data being sent to the original exporting driver. The type is specified by Type
ActionRequest	On return, points to the action requested by the callback function

Description
This function is called by the forms browser in response to a user action on a question which has the
EFI_IFR_FLAG_CALLBACK bit set in the EFI_IFR_QUESTION_HEADER. The user action is specified by
Action. Depending on the action, the browser may also pass the question value using Type and Value.
Upon return, the callback function may specify the desired browser action.
```

To better understand when and how this function is called let's add a debug statement that would print incoming arguments like we did with `RouteConfig()` and `ExtractConfig()`. But first let's try to understand what all these arguments mean.

## `EFI_BROWSER_ACTION Action`

This argument describes an operation that is currently performed by the browser. UEFI specification defines a list of possible browser operations with the `EFI_BROWSER_ACTION` type. The `EFI_BROWSER_ACTION` is `UINTN` with possible values described in the [https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/HiiConfigAccess.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/HiiConfigAccess.h):
```cpp
typedef UINTN EFI_BROWSER_ACTION;

#define EFI_BROWSER_ACTION_CHANGING               0		// Called after the user have changed the element value, but before the browser updated display
#define EFI_BROWSER_ACTION_CHANGED                1		// Called after the user have changed the element value, after the browser updated display
#define EFI_BROWSER_ACTION_RETRIEVE               2		// Called after the browser has read the value, but before displayed it
#define EFI_BROWSER_ACTION_FORM_OPEN              3		// Called on form open before retrieve
#define EFI_BROWSER_ACTION_FORM_CLOSE             4		// Called on form exit
#define EFI_BROWSER_ACTION_SUBMITTED              5		// Called after submit
#define EFI_BROWSER_ACTION_DEFAULT_STANDARD       0x1000	// Called on setting standard default (default value is equal 0x0000)
#define EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING  0x1001	// Called on setting manufacture default (default value is equal 0x0001)
#define EFI_BROWSER_ACTION_DEFAULT_SAFE           0x1002	// Called on setting safe defaults (default value is equal 0x0002)
#define EFI_BROWSER_ACTION_DEFAULT_PLATFORM       0x2000	// Called on setting platform defaults (default values in range 0x4000-0x7fff)
#define EFI_BROWSER_ACTION_DEFAULT_HARDWARE       0x3000	// Called on setting hardware defaults (default values in range 0x8000-0xbfff)
#define EFI_BROWSER_ACTION_DEFAULT_FIRMWARE       0x4000	// Called on setting firmware defaults (default values in range 0xc000-0xffff)
```
On each of these operations the Form Browser calls `Callback()` function for each of the form elements with the `EFI_IFR_FLAG_CALLBACK` flag.

To print `EFI_BROWSER_ACTION` value as a string we'll define this simple function:
```cpp
EFI_STRING ActionToStr(EFI_BROWSER_ACTION Action)
{
  switch (Action) {
    case EFI_BROWSER_ACTION_CHANGING:
      return L"EFI_BROWSER_ACTION_CHANGING";
    case EFI_BROWSER_ACTION_CHANGED:
      return L"EFI_BROWSER_ACTION_CHANGED";
    case EFI_BROWSER_ACTION_RETRIEVE:
      return L"EFI_BROWSER_ACTION_RETRIEVE";
    case EFI_BROWSER_ACTION_FORM_OPEN:
      return L"EFI_BROWSER_ACTION_FORM_OPEN";
    case EFI_BROWSER_ACTION_FORM_CLOSE:
      return L"EFI_BROWSER_ACTION_FORM_CLOSE";
    case EFI_BROWSER_ACTION_SUBMITTED:
      return L"EFI_BROWSER_ACTION_SUBMITTED";
    case EFI_BROWSER_ACTION_DEFAULT_STANDARD:
      return L"EFI_BROWSER_ACTION_DEFAULT_STANDARD";
    case EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING:
      return L"EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING";
    case EFI_BROWSER_ACTION_DEFAULT_SAFE:
      return L"EFI_BROWSER_ACTION_DEFAULT_SAFE";
    case EFI_BROWSER_ACTION_DEFAULT_PLATFORM:
      return L"EFI_BROWSER_ACTION_DEFAULT_PLATFORM";
    case EFI_BROWSER_ACTION_DEFAULT_HARDWARE:
      return L"EFI_BROWSER_ACTION_DEFAULT_HARDWARE";
    case EFI_BROWSER_ACTION_DEFAULT_FIRMWARE:
      return L"EFI_BROWSER_ACTION_DEFAULT_FIRMWARE";
    default:
      return L"Unknown";
  }
}
```

## `EFI_QUESTION_ID QuestionId`

The `EFI_QUESTION_ID` type is defined in the [https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h)
```
typedef UINT16  EFI_QUESTION_ID;
```
It is just a `UINT16` value identificator to indicate for which of the form elements the `Callback()` function is called.

It is possible to explicitly set values for question id's of your form elements with the VFR `questionid` keyword. If you don't do it, the `VfrCompiler` will implicitly assign unique question identifiers to all the form elements without it. The numbering in this case starts from `0x0001`.

## `UINT8 Type` and `EFI_IFR_TYPE_VALUE *Value`

The Form Browser also sends an actual form element data to the `Callback()` function.

But each element store the data in it's own type. Therefore the Form Browser in one argument sends the type of value [https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h):
```
//
// Types of the option's value.
//
#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
```
And in in another argument it sends the actual value as a union type:
```
typedef union {
  UINT8            u8;
  UINT16           u16;
  UINT32           u32;
  UINT64           u64;
  BOOLEAN          b;
  EFI_HII_TIME     time;
  EFI_HII_DATE     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;
```

Therefore to get the actual value we first look at the `UINT8 Type` argument and then use the necessary type on the `EFI_IFR_TYPE_VALUE` union. Below I've written two functions: `TypeToStr` simply prints a type of the callback element, and `DebugCallbackValue` prints an element value based on it's type:
```cpp
EFI_STRING TypeToStr(UINT8 Type)
{
  switch (Type) {
    case EFI_IFR_TYPE_NUM_SIZE_8:
      return L"EFI_IFR_TYPE_NUM_SIZE_8";
    case EFI_IFR_TYPE_NUM_SIZE_16:
      return L"EFI_IFR_TYPE_NUM_SIZE_16";
    case EFI_IFR_TYPE_NUM_SIZE_32:
      return L"EFI_IFR_TYPE_NUM_SIZE_32";
    case EFI_IFR_TYPE_NUM_SIZE_64:
      return L"EFI_IFR_TYPE_NUM_SIZE_64";
    case EFI_IFR_TYPE_BOOLEAN:
      return L"EFI_IFR_TYPE_BOOLEAN";
    case EFI_IFR_TYPE_TIME:
      return L"EFI_IFR_TYPE_TIME";
    case EFI_IFR_TYPE_DATE:
      return L"EFI_IFR_TYPE_DATE";
    case EFI_IFR_TYPE_STRING:
      return L"EFI_IFR_TYPE_STRING";
    case EFI_IFR_TYPE_OTHER:
      return L"EFI_IFR_TYPE_OTHER";
    case EFI_IFR_TYPE_UNDEFINED:
      return L"EFI_IFR_TYPE_UNDEFINED";
    case EFI_IFR_TYPE_ACTION:
      return L"EFI_IFR_TYPE_ACTION";
    case EFI_IFR_TYPE_BUFFER:
      return L"EFI_IFR_TYPE_BUFFER";
    case EFI_IFR_TYPE_REF:
      return L"EFI_IFR_TYPE_REF";
    default:
      return L"Unknown";
  }
}

VOID DebugCallbackValue(UINT8 Type, EFI_IFR_TYPE_VALUE *Value)
{
  switch (Type) {
    case EFI_IFR_TYPE_NUM_SIZE_8:
      DEBUG ((EFI_D_INFO, "%d\n", Value->u8));
      break;
    case EFI_IFR_TYPE_NUM_SIZE_16:
      DEBUG ((EFI_D_INFO, "%d\n", Value->u16));
      break;
    case EFI_IFR_TYPE_NUM_SIZE_32:
      DEBUG ((EFI_D_INFO, "%d\n", Value->u32));
      break;
    case EFI_IFR_TYPE_NUM_SIZE_64:
      DEBUG ((EFI_D_INFO, "%ld\n", Value->u64));
      break;
    case EFI_IFR_TYPE_BOOLEAN:
      DEBUG ((EFI_D_INFO, "%d\n", Value->b));
      break;
    case EFI_IFR_TYPE_TIME:
      DEBUG ((EFI_D_INFO, "%02d:%02d:%02d\n", Value->time.Hour, Value->time.Minute, Value->time.Second));
      break;
    case EFI_IFR_TYPE_DATE:
      DEBUG ((EFI_D_INFO, "%04d/%02d/%02d\n", Value->date.Year, Value->date.Month, Value->date.Day));
      break;
    case EFI_IFR_TYPE_STRING:
      DEBUG ((EFI_D_INFO, "%s\n", HiiGetString(mHiiHandle, Value->string, "en-US") ));
      break;
    default:
      DEBUG ((EFI_D_INFO, "Unknown\n" ));
      break;
  }
}
```

# Putting it all together

Let's get all of our helper functions and create a debug print statement for the `Callback()` code:
```cpp
STATIC
EFI_STATUS
EFIAPI
Callback (
  IN     CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN     EFI_BROWSER_ACTION                     Action,
  IN     EFI_QUESTION_ID                        QuestionId,
  IN     UINT8                                  Type,
  IN OUT EFI_IFR_TYPE_VALUE                     *Value,
  OUT    EFI_BROWSER_ACTION_REQUEST             *ActionRequest
  )
{
  DEBUG ((EFI_D_INFO, "Callback: Action=%s, QuestionId=0x%04x, Type=%s, Value=", ActionToStr(Action), QuestionId, TypeToStr(Type)));
  DebugCallbackValue(Type, Value);

  return EFI_UNSUPPORTED;
}
```

Besides the `Callback()` let's add the `DEBUG` statements to the `RouteConfig()` and `ExtractConfig()` functions as well. This way we will know the order of how the Form Browser calls our functions:
```
STATIC
EFI_STATUS
EFIAPI
RouteConfig (
  IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL  *This,
  IN CONST  EFI_STRING                      Configuration,
  OUT       EFI_STRING                      *Progress
)
{
  DEBUG ((EFI_D_INFO, "RouteConfig: Configuration=%s\n", Configuration));

  ...
}

STATIC
EFI_STATUS
EFIAPI
ExtractConfig (
  IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL  *This,
  IN CONST  EFI_STRING                      Request,
  OUT       EFI_STRING                      *Progress,
  OUT       EFI_STRING                      *Results
)
{
  DEBUG ((EFI_D_INFO, "ExtractConfig: Request=%s\n", Request));

  ...
}
```

Now it is time to test our driver. Build our driver, copy it to the shared folder and run QEMU with debug log. For the last operation you can use the first stage of the `run_gdb_ovmf.sh` script:
```
./run_gdb_ovmf.sh -1
```

Load our driver to the UEFI shell.
```
FS0:\> load HIIFormDataElementsVarstore.efi
```

The `Callback()` code is run only by the Form Browser. Therefore if we would issue requests with our `HIIConfig.efi` application, the `Callback()` would never run. So let's type `exit` to run the Form Browser.

When we enter our form, the following strings would be printed to the debug log (by default the value for the numeric element is 8):
```
ExtractConfig: Request=GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=0&WIDTH=0024
Callback: Action=EFI_BROWSER_ACTION_FORM_OPEN, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=0
Callback: Action=EFI_BROWSER_ACTION_RETRIEVE, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=8
```
As you see here our element got `QuestionId=0x0002`. This is because we didn't explicitly set any questionId's for our form elements and the `numeric` element is second on the form (the first is the `checkbox` element). We've set `NUMERIC_SIZE_2` flag in the element VFR code, therefore the value is interpreted as `Value->u16`.

Change the numeric element value to 3:
```
Callback: Action=EFI_BROWSER_ACTION_CHANGING, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
Callback: Action=EFI_BROWSER_ACTION_CHANGED, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
```
As you know, in this case the Form Browser doesn't call `ExtractConfig`/`RouteConfig` functions. But as you see you still have control in that case since the `Callback()` code is called.

Submit the form with updated value:
```
RouteConfig: Configuration=GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=0000&WIDTH=0001&VALUE=00&OFFSET=0001&WIDTH=0002&VALUE=0003&OFFSET=0003&WIDTH=0014&VALUE=006f0072007000200067006e0069007200740053&OFFSET=0019&WIDTH=0004&VALUE=160507e5&OFFSET=001d&WIDTH=0003&VALUE=213717&OFFSET=0020&WIDTH=0001&VALUE=33&OFFSET=0021&WIDTH=0003&VALUE=0a0b0c
Callback: Action=EFI_BROWSER_ACTION_SUBMITTED, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
```

Press reset to default button (this sets value to 7):
```
ExtractConfig: Request=GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=0000&WIDTH=0001&OFFSET=0001&WIDTH=0002&OFFSET=0003&WIDTH=0014&OFFSET=0019&WIDTH=0004&OFFSET=001d&WIDTH=0003&OFFSET=0020&WIDTH=0001&OFFSET=0021&WIDTH=0003
Callback: Action=EFI_BROWSER_ACTION_DEFAULT_STANDARD, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
```

Press reset to manufacture default button (this sets value to 8):
```
ExtractConfig: Request=GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=0000&WIDTH=0001&OFFSET=0001&WIDTH=0002&OFFSET=0003&WIDTH=0014&OFFSET=0019&WIDTH=0004&OFFSET=001d&WIDTH=0003&OFFSET=0020&WIDTH=0001&OFFSET=0021&WIDTH=0003
Callback: Action=EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=7
```

Exit without submit
```
Callback: Action=EFI_BROWSER_ACTION_CHANGED, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
Callback: Action=EFI_BROWSER_ACTION_FORM_CLOSE, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
```
Here you see that 2 callbacks were executed: `EFI_BROWSER_ACTION_CHANGED` and `EFI_BROWSER_ACTION_FORM_CLOSE`. As another experiment enter the form again, set manufacture defaults and submit them. And only then exit our form. This way the exit action would print only the `EFI_BROWSER_ACTION_FORM_CLOSE` callback:
```
Callback: Action=EFI_BROWSER_ACTION_FORM_CLOSE, QuestionId=0x0002, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=8
```
So the `EFI_BROWSER_ACTION_CHANGED` in this case is called only if an element has unsubmitted changes.


# `QuestionId` and the order of callbacks

In the last example the `QuestionId` for our element was set implicitly by the `VfrCompiler`. But as we've mentioned it earlier it is possible to set it yourself with a help of a `quiestionid` keyword.

For another experiment let's add `INTERACTIVE` flag to the `checkbox` and `string` elements. But set the `quiestionid` explicitly only for the `checkbox` and `numeric` elements:
```
...

checkbox
  varid = FormData.CheckboxValue,
  questionid = 0x5555,
  prompt = STRING_TOKEN(CHECKBOX_PROMPT),
  help = STRING_TOKEN(CHECKBOX_HELP),
  flags = INTERACTIVE,
  default = TRUE, defaultstore = StandardDefault,
  default = FALSE, defaultstore = ManufactureDefault,
endcheckbox;

numeric
  name = NumericQuestion,
  varid = FormData.NumericValue,
  questionid = 0x4444,
  prompt = STRING_TOKEN(NUMERIC_PROMPT),
  help = STRING_TOKEN(NUMERIC_HELP),
  flags = NUMERIC_SIZE_2 | DISPLAY_UINT_HEX | INTERACTIVE,
  minimum = 0,
  maximum = 10,
  step = 1,
  default = 7, defaultstore = StandardDefault,
  default = 8, defaultstore = ManufactureDefault,
endnumeric;

string
  name = StringQuestion,
  varid = FormData.StringValue,
  prompt = STRING_TOKEN(STRING_PROMPT),
  help = STRING_TOKEN(STRING_HELP),
  flags = INTERACTIVE,
  minsize = 5,
  maxsize = 10,
  default = STRING_TOKEN(STRING_DEFAULT), defaultstore = StandardDefault,
  default = STRING_TOKEN(STRING_PROMPT), defaultstore = ManufactureDefault,
endstring

...
```

This would give us this output on the form open:
```
ExtractConfig: Request=GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=0&WIDTH=0024
Callback: Action=EFI_BROWSER_ACTION_FORM_OPEN, QuestionId=0x5555, Type=EFI_IFR_TYPE_BOOLEAN, Value=0
Callback: Action=EFI_BROWSER_ACTION_FORM_OPEN, QuestionId=0x4444, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=0
Callback: Action=EFI_BROWSER_ACTION_FORM_OPEN, QuestionId=0x0001, Type=EFI_IFR_TYPE_STRING, Value=
Callback: Action=EFI_BROWSER_ACTION_RETRIEVE, QuestionId=0x5555, Type=EFI_IFR_TYPE_BOOLEAN, Value=0
Callback: Action=EFI_BROWSER_ACTION_RETRIEVE, QuestionId=0x4444, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=8
Callback: Action=EFI_BROWSER_ACTION_RETRIEVE, QuestionId=0x0001, Type=EFI_IFR_TYPE_STRING, Value=String pro
```
As you see here each action is called on every interactive form element. This is true for all other cases when the action affects all the form elements. This includes all the actions except `EFI_BROWSER_ACTION_CHANGING` and `EFI_BROWSER_ACTION_CHANGED`.

Another thing to point out is that the calls in the output above are happening not by the order of `QuestionId`'s, but in the order of element placement in the VFR. Also as we've said it before, the `VfrCompiler` implicitly assigns `QuestionId`'s to all the form elements which don't have any explicit setting for that. Even to those which don't have any `INTERACTIVE` flag. The implicit assignment starts from the `0x0001` value, and this is the value that our `string` element got.

Now if we change the `numeric` element value to 3, only `numeric` callback will be executed:
```
Callback: Action=EFI_BROWSER_ACTION_CHANGING, QuestionId=0x4444, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
Callback: Action=EFI_BROWSER_ACTION_CHANGED, QuestionId=0x4444, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=3
```
And if we exit the form without any submit, the following callbacks will be called:
```
Callback: Action=EFI_BROWSER_ACTION_CHANGED, QuestionId=0x4444, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=8
Callback: Action=EFI_BROWSER_ACTION_FORM_CLOSE, QuestionId=0x5555, Type=EFI_IFR_TYPE_BOOLEAN, Value=0
Callback: Action=EFI_BROWSER_ACTION_FORM_CLOSE, QuestionId=0x4444, Type=EFI_IFR_TYPE_NUM_SIZE_16, Value=8
Callback: Action=EFI_BROWSER_ACTION_FORM_CLOSE, QuestionId=0x0001, Type=EFI_IFR_TYPE_STRING, Value=String pro
```

I hope all of these experiments have got you some understanding about when and how the `Callback()` function is executed.

To finish this lesson here is a logic diagram for an individual numeric element with the current value of 8:
```
  Open form (even before the form is displayed)
EFI_BROWSER_ACTION_FORM_OPEN (Value=0)
EFI_BROWSER_ACTION_RETRIEVE  (Value=8)
    |
    -------------------------------------------------
    |                                               |
  Change value from 8 to 8                        Close form
EFI_BROWSER_ACTION_CHANGING   (Value=8)         EFI_BROWSER_ACTION_FORM_CLOSE (Value=8)
    |
    -------------------------------------------------
    |                                               |
  Change value from 8 to 9                        Close form
EFI_BROWSER_ACTION_CHANGING   (Value=9)         EFI_BROWSER_ACTION_FORM_CLOSE (Value=8)
EFI_BROWSER_ACTION_CHANGED    (Value=9)
    |
    -------------------------------------------------------------------------------------------------
    |                                               |                                               |
  Submit                                          Close form with sumbit                          Close form without submit
EFI_BROWSER_ACTION_SUBMITTED  (Value=9)         EFI_BROWSER_ACTION_SUBMITTED  (Value=9)         EFI_BROWSER_ACTION_CHANGED    (Value=8)
    |                                           EFI_BROWSER_ACTION_FORM_CLOSE (Value=9)         EFI_BROWSER_ACTION_FORM_CLOSE (Value=8)
  Close
EFI_BROWSER_ACTION_FORM_CLOSE (Value=9)
```