aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons_uncategorized/Lesson_Varstore_3/README.md
blob: 65b99d2fbc4b1fcbfa9b4b0776aba57132a31351 (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
# `BlockToConfig()`/`ConfigToBlock()`

With the `Buffer Storage` you have a freedom about what you do on store/update operations, but still your form storage is just some data packed in a structure.

So add it as a global variable to our `UefiLessonsPkg/HIIFormDataElementsVarstore/HIIFormDataElementsVarstore.c` file:
```cpp
VARIABLE_STRUCTURE FormStorage;
```

Now let's understand our task in the `Extractonfig()` function: we need to parse incoming request, get some parts of our structure and return them in a configuration response.

In other words we need to correspond `OFFSET=<...>&WIDTH=<...>` combinations from the request to the data in the structure. But you've already know that there can be many such pairs in the request, and parsing all of them manually can be a real burden. I'm not even speaking about creating appropriate response string.

Therefore UEFI specification offers us a helper function to ease our life:
```
EFI_HII_CONFIG_ROUTING_PROTOCOL.BlockToConfig()

Summary:
This helper function is to be called by drivers to map configuration data stored in byte array (“block”) formats such as UEFI Variables into current configuration strings.

Prototype:

typedef
EFI_STATUS
 (EFIAPI * EFI_HII_BLOCK_TO_CONFIG ) (
 IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This,
 IN CONST EFI_STRING ConfigRequest,
 IN CONST UINT8 *Block,
 IN CONST UINTN BlockSize,
 OUT EFI_STRING *Config,
 OUT EFI_STRING *Progress
 );

Parameters:
This		Points to the EFI_HII_CONFIG_ROUTING_PROTOCOL instance
ConfigRequest	A null-terminated string in <ConfigRequest> format
Block		Array of bytes defining the block’s configuration
BlockSize	Length in bytes of Block
Config		Filled-in configuration string. String allocated by the function. Returned only if call is
		successful. The null-terminated string will be in <ConfigResp> 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 extracts the current configuration from a block of bytes. To do so, it requires that the ConfigRequest string consists of a list of <BlockName> formatted names. It uses the offset in the
name to determine the index into the Block to start the extraction and the width of each name to determine the number of bytes to extract. These are mapped to a string using the equivalent of the C
“%x” format (with optional leading spaces). The call fails if, for any (offset, width) pair in ConfigRequest, offset+value >= BlockSize
```

With it you can finish our `ExtractConfig` function like this:
```cpp
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
)
{
  if (Progress == NULL || Results == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  // Here can be additional custom operations to get you VARIABLE_STRUCTURE data

  EFI_STATUS Status = gHiiConfigRouting->BlockToConfig(gHiiConfigRouting,
                                                       Request,
                                                       (UINT8*)&FormStorage,
                                                       sizeof(VARIABLE_STRUCTURE),
                                                       Results,
                                                       Progress);

  return Status;
}
```
To use `BlockToConfig` we need to get `EFI_HII_CONFIG_ROUTING_PROTOCOL` pointer. You can receive it via the `LocateProtocol` call in the driver's entry point function. After that just save the pointer as a global variable, so you could use it in other places like `ExtractConfig` function. Although here I've decided to take another approach. Here I just use `UefiHiiServicesLib` that locates several HII related protocols for us and provides them in pointers like `gHiiConfigRouting` for our usage. I've mentioned it in the earlier lessons. To use it add `UefiHiiServicesLib` to the `[LibraryClasses]` in the `INF` file and `#include <Library/UefiHiiServicesLib.h>` to the start of the `*.c` file.

In the code above I've marked a place where can be additional custom operations to get you storage data structure. In our simple driver we don't have anything like that, but speaking in terms of our previous examples, this is the place where you would read you PCIe device memory, get state of GPIO LEDs, etc. We can even mimic `efivarstore` behaviour if here we would load our structure from some variable in the global UEFI variable storage.


Now let's get to our `RouteConfig()` function. In this function we need to parse incoming request and modify some parts of our storage structure based on the request. Once again a process of parsing `OFFSET=<...>&WIDTH=<...>&VALUE=<...>` combinations from the configuration request and cooresponding them to the real data is not easy. Luckily UEFI specification offers us a helper function for that case too:
```
EFI_HII_CONFIG_ROUTING_PROTOCOL.ConfigToBlock()

Summary:
This helper function is to be called by drivers to map configuration strings to configurations stored in byte array (“block”) formats such as UEFI Variables.

Prototype:

typedef
EFI_STATUS
 (EFIAPI * EFI_HII_CONFIG_TO_BLOCK ) (
 IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This,
 IN CONST EFI_STRING *ConfigResp,
 IN OUT CONST UINT8 *Block,
 IN OUT UINTN *BlockSize,
 OUT EFI_STRING *Progress
 );

Parameters:
This		Points to the EFI_HII_CONFIG_ROUTING_PROTOCOL instance.
ConfigResp	A null-terminated string in <ConfigResp> format.
Block		A possibly null array of bytes representing the current block. Only bytes referenced in the ConfigResp string in the block are modified. If this parameter is null or if the
		*BlockSize parameter is (on input) shorter than required by the Configuration string, only the BlockSize parameter is updated and an appropriate status (see below) is returned.
BlockSize	The length of the Block in units of UINT8. On input, this is the size of the Block. On output, if successful, contains the largest index of the modified byte in the Block, or
		the required buffer size if the Block is not large enough.
Progress	On return, points to an element of the ConfigResp 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 maps a configuration containing a series of <BlockConfig> formatted name value pairs in ConfigResp into a Block so it may be stored in a linear mapped storage such as a UEFI Variable. If
present, the function skips GUID, NAME, and PATH in <ConfigResp>. It stops when it finds a non-<BlockConfig> name / value pair (after skipping the routing header) or when it reaches the end of the string.
```

So let's use it in our `RouteConfig()` function:
```cpp
STATIC
EFI_STATUS
EFIAPI
RouteConfig (
  IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL  *This,
  IN CONST  EFI_STRING                      Configuration,
  OUT       EFI_STRING                      *Progress
)
{
  if (Configuration == NULL || Progress == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  UINTN BlockSize = sizeof(VARIABLE_STRUCTURE);
  EFI_STATUS Status = gHiiConfigRouting->ConfigToBlock(gHiiConfigRouting,
                                                       Configuration,
                                                       (UINT8*)&FormStorage,
                                                       &BlockSize,
                                                       Progress);

  // Here can be additional custom operations to update you storage data structure

  return Status;
}
```
Once again I've marked a place where can be additional functionality for your storage. Speaking in terms of our previous examples, this is the place where you would update you PCIe device memory, set state of GPIO LEDs, etc. If you want to mimic `efivarstore` behaviour here you would update the variable in the global UEFI variable storage.

Now we are ready to build and test our driver.

Load it in the UEFI shell:
```
FS0:\> load HIIFormDataElementsVarstore.efi
```

Check the form browser. Now our form is filled with zeros rather than default values. We didn't give any data to our `VARIABLE_STRUCTURE FormStorage` variable, so it is expected output:

![1](1.png?raw=true "1")

And now if you'll try to set the checkbox element and submit a form, you'll see that submit also works fine:

![2](2.png?raw=true "2")

As in our case the `VARIABLE_STRUCTURE FormStorage` is a simple local driver variable the changes are pesistent only in the context of a current boot. If you'll reboot QEMU, you'll start from 0 values in the storage again.

# Setting defaults for the form elements

As now our `ExtractConfig`/`RouteConfig` functions are working we can use `HiiSetToDefaults` helper to set defaults to our form elements non-interactively, like we did it in the `efivarstore` case earlier. For example let's initialize our form from the manufacture defaults:
```cpp
EFI_GUID StorageGuid = STORAGE_GUID;
EFI_STRING StorageName = L"FormData";

<...>

EFI_STATUS
EFIAPI
HIIFormDataElementsVarstoreEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  <...>

  EFI_STRING ConfigStr = HiiConstructConfigHdr(&StorageGuid, StorageName, mDriverHandle);
  UINT16 DefaultId = 1;
  if (!HiiSetToDefaults(ConfigStr, DefaultId)) {
    Print(L"Error! Can't set default configuration #%d\n", DefaultId);
  }

  return EFI_SUCCESS;
}
```

With that our form will start with the manufacture default values:

![3](3.png?raw=true "3")


# Checking the form with `HIIConfig.efi`

As another test let's try to use our `HIIConfig.efi` program to work with our `varstorage` driver.

Output for the request for all of the driver configuration data looks like this:
```
FS0:\> HIIConfig.efi extract 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)

Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400
Response: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=0000&WIDTH=0001&VALUE=00&OFFSET=0001&WIDTH=0002&VALUE=0008&OFFSET=0003&WIDTH=0014&VALUE=006f0072007000200067006e0069007200740053&OFFSET=0019&WIDTH=0004&VALUE=160507e5&OFFSET=001<...>


GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
OFFSET=0000  WIDTH=0001  VALUE=00
00                                               | .
OFFSET=0001  WIDTH=0002  VALUE=0008
08 00                                            | ..
OFFSET=0003  WIDTH=0014  VALUE=006f0072007000200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00  | S.t.r.i.n.g. .p.
72 00 6F 00                                      | r.o.
OFFSET=0019  WIDTH=0004  VALUE=160507e5
E5 07 05 16                                      | ....
OFFSET=001d  WIDTH=0003  VALUE=213717
17 37 21                                         | .7!
OFFSET=0020  WIDTH=0001  VALUE=33
33                                               | 3
OFFSET=0021  WIDTH=0003  VALUE=0a0b0c
0C 0B 0A                                         | ...

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0000
OFFSET=0000  WIDTH=0001  VALUE=01
01                                               | .
OFFSET=0001  WIDTH=0002  VALUE=0007
07 00                                            | ..
OFFSET=0003  WIDTH=0014  VALUE=00660065006400200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 64 00  | S.t.r.i.n.g. .d.
65 00 66 00                                      | e.f.
OFFSET=0019  WIDTH=0004  VALUE=160507e5
E5 07 05 16                                      | ....
OFFSET=001d  WIDTH=0003  VALUE=213717
17 37 21                                         | .7!
OFFSET=0020  WIDTH=0001  VALUE=55
55                                               | U
OFFSET=0021  WIDTH=0003  VALUE=0a0b0c
0C 0B 0A                                         | ...

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0001
OFFSET=0000  WIDTH=0001  VALUE=00
00                                               | .
OFFSET=0001  WIDTH=0002  VALUE=0008
08 00                                            | ..
OFFSET=0003  WIDTH=0014  VALUE=006f0072007000200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00  | S.t.r.i.n.g. .p.
72 00 6F 00                                      | r.o.
OFFSET=0019  WIDTH=0004  VALUE=160507e5
E5 07 05 16                                      | ....
OFFSET=001d  WIDTH=0003  VALUE=213717
17 37 21                                         | .7!
OFFSET=0020  WIDTH=0001  VALUE=33
33                                               | 3
OFFSET=0021  WIDTH=0003  VALUE=0a0b0c
0C 0B 0A                                         | ...
```

Here is example that accesses individual element (`numeric` in this case):
```
FS0:\> HIIConfig.efi extract 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8) 3 14

Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=3&WIDTH=14
Response: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=3&WIDTH=14&VALUE=006f0072007000200067006e0069007200740053&GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5<...>


GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
OFFSET=3  WIDTH=14  VALUE=006f0072007000200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00  | S.t.r.i.n.g. .p.
72 00 6F 00                                      | r.o.

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0000
OFFSET=0003  WIDTH=0014  VALUE=00660065006400200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 64 00  | S.t.r.i.n.g. .d.
65 00 66 00                                      | e.f.

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
NAME=0041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0001
OFFSET=0003  WIDTH=0014  VALUE=006f0072007000200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00  | S.t.r.i.n.g. .p.
72 00 6F 00                                      | r.o.
```

We can change this element value:
```
FS0:\> HIIConfig.efi route 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8) 1 2 1234
Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=1&WIDTH=2&VALUE=1234
```

And verify that it indeed was changed:
```
FS0:\> HIIConfig.efi extract 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)
                                        1 2
Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=1&WIDTH=2
Response: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=1&WIDTH=2&VALUE=1234&GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&ALTCFG=0000&OFFSET=0001&WI<...>


GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
OFFSET=1  WIDTH=2  VALUE=1234
34 12                                            | 4.

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0000
OFFSET=0001  WIDTH=0002  VALUE=0007
07 00                                            | ..

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0001
OFFSET=0001  WIDTH=0002  VALUE=0008
08 00                                            | ..
```

`ExportConfig` call also works correctly. Yes, the `varstore` configurations is seen in the `ExportConfig` output opposed to the `efivarstore` configurations:
```
FS0:\> HIIConfig.efi dump
Full configuration for the HII Database (Size = 43266):

<...>

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
OFFSET=0000  WIDTH=0001  VALUE=00
00                                               | .
OFFSET=0001  WIDTH=0002  VALUE=0008
34 12                                            | ..
OFFSET=0003  WIDTH=0014  VALUE=006f0072007000200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00  | S.t.r.i.n.g. .p.
72 00 6F 00                                      | r.o.
OFFSET=0019  WIDTH=0004  VALUE=160507e5
E5 07 05 16                                      | ....
OFFSET=001d  WIDTH=0003  VALUE=213717
17 37 21                                         | .7!
OFFSET=0020  WIDTH=0001  VALUE=33
33                                               | 3
OFFSET=0021  WIDTH=0003  VALUE=0a0b0c
0C 0B 0A                                         | ...

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0000
OFFSET=0000  WIDTH=0001  VALUE=01
01                                               | .
OFFSET=0001  WIDTH=0002  VALUE=0007
07 00                                            | ..
OFFSET=0003  WIDTH=0014  VALUE=00660065006400200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 64 00  | S.t.r.i.n.g. .d.
65 00 66 00                                      | e.f.
OFFSET=0019  WIDTH=0004  VALUE=160507e5
E5 07 05 16                                      | ....
OFFSET=001d  WIDTH=0003  VALUE=213717
17 37 21                                         | .7!
OFFSET=0020  WIDTH=0001  VALUE=55
55                                               | U
OFFSET=0021  WIDTH=0003  VALUE=0a0b0c
0C 0B 0A                                         | ...

GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2)
NAME=0046006f0072006d0044006100740061 (FormData)
PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8))
ALTCFG=0001
OFFSET=0000  WIDTH=0001  VALUE=00
00                                               | .
OFFSET=0001  WIDTH=0002  VALUE=0008
08 00                                            | ..
OFFSET=0003  WIDTH=0014  VALUE=006f0072007000200067006e0069007200740053
53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00  | S.t.r.i.n.g. .p.
72 00 6F 00                                      | r.o.
OFFSET=0019  WIDTH=0004  VALUE=160507e5
E5 07 05 16                                      | ....
OFFSET=001d  WIDTH=0003  VALUE=213717
17 37 21                                         | .7!
OFFSET=0020  WIDTH=0001  VALUE=33
33                                               | 3
OFFSET=0021  WIDTH=0003  VALUE=0a0b0c
0C 0B 0A                                         | ...
```

So as you can see everything works correctly. But there is still some room to improve.