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
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
|
The Platform Configuration Database (PCD) is a database that contains a variety of current platform settings or directives that can be accessed by a driver or application.
You can checkout edk2 specification https://edk2-docs.gitbook.io/edk-ii-pcd-specification/ or https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/edkii-platform-config-database-entries-paper.pdf for more explanation on PCD.
PCD entry is also called PCD, so we will use this term further.
The PCD entry is defined in a DEC file in a format:
```
<TokenSpaceGuidCName>.<PcdCName>|<DefaultValue>|<DatumType>|<Token>
```
`<TokenSpaceGuidCName>` is a GUID value, `<Token>` is a 32-bit value. Together they are used to uniqely identify PCD.
First let's declare a Token Space that would contain all our PCDs.
Usually in is defined as a `g<PackageName>TokenSpaceGuid`, so add this to our `UefiLessonsPkg/UefiLessonsPkg.dec`:
```
[Guids]
...
gUefiLessonsPkgTokenSpaceGuid = {0x150cab53, 0xad47, 0x4385, {0xb5, 0xdd, 0xbc, 0xfc, 0x76, 0xba, 0xca, 0xf0}}
```
As for the `<Token>` values, usually the package creators just start to write them sequentially from `0x00000001`.
Also very often when they want to indicate that some PCDs are belong to one logical group, they can start using the token numbers to indicate that. For example the token groups can be `0x1XXXXXXX`, `0x2XXXXXXX`, and so on, or something similar.
In any way as the package evolve some PCD are getting added and some are getting removed. And if you use such sequential numbering this can give you headache. For example at one point you can end up in situation when you have PCDs with the tokens `0x0000000A` and `0x0000000B` and the most logical way to put your new PCD is after the one with a `0x0000000A` token. Off course you can assign PCD token to `0x0000000C` and do it, but what is the point of a sequential numbering then?
Because of that I've created `./scripts/genToken.sh` script that generates random 4-byte token number:
```
#!/bin/bash
##
# This is a simple script that generates a random 4-byte hex value for a PCD Token
##
hexdump -vn4 -e'"0x%08X\n"' /dev/urandom
```
The usage is simple as this:
```
$ ./scripts/genToken.sh
0x3B81CDF1
```
Now we can define our PCD in the same `*.dec` file that we've used to define `gUefiLessonsPkgTokenSpaceGuid`. Let's start with a PCD `UINT8 PcdInt8 = 0x88`:
```
[PcdsFixedAtBuild]
gEfiUefiLessonsPkgTokenSpaceGuid.PcdInt8|0x88|UINT8|0x3B81CDF1
```
Now create an app `PCDLesson` with the following code in its entry point function:
```
Print(L"PcdInt8=0x%x\n", FixedPcdGet8(PcdInt8));
```
To use `FixedPcdGet8` in our code we need to add the necessary include:
```
#include <Library/PcdLib.h>
```
If you check out this file (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h) you'll see that `FixedPcdGet8` is simply a define statement:
```
#define FixedPcdGet8(TokenName) _PCD_VALUE_##TokenName
```
If we try to build our app now, build will fail, as we don't have such define in our app:
```
/home/kostr/tiano/edk2/MdePkg/Include/Library/PcdLib.h:97:45: error: ‘_PCD_VALUE_PcdInt8’ undeclared (first use in this function)
97 | #define FixedPcdGet8(TokenName) _PCD_VALUE_##TokenName
| ^~~~~~~~~~~
```
To fix this we need to add this PCD to our app `*.inf` file:
```
[FixedPcd]
gUefiLessonsPkgTokenSpaceGuid.PcdInt8
```
Also we need to include "dec" file that defines this PCD:
```
[Packages]
...
UefiLessonsPkg/UefiLessonsPkg.dec
```
Now compilation would succeed.
If you check out the content of autogenerated files `AutoGen.h`/`AutoGen.c`, you'll see, that our PCD is there:
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h`
```
// Definition of PCDs used in this module
#define _PCD_TOKEN_PcdInt8 0U
#define _PCD_SIZE_PcdInt8 1
#define _PCD_GET_MODE_SIZE_PcdInt8 _PCD_SIZE_PcdInt8
#define _PCD_VALUE_PcdInt8 0x88U
extern const UINT8 _gPcd_FixedAtBuild_PcdInt8;
#define _PCD_GET_MODE_8_PcdInt8 _gPcd_FixedAtBuild_PcdInt8
//#define _PCD_SET_MODE_8_PcdInt8 ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
```
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c`
```
// Definition of PCDs used in this module
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdInt8 = _PCD_VALUE_PcdInt8;
```
So in our case the preprocessor expands code like this:
```
FixedPcdGet8(PcdInt8) -> _PCD_VALUE_PcdInt8 -> 0x88U
```
If you execute app code under OVMF you would get correct value printed:
```
FS0:\> PCDLesson.efi
PcdInt8=0x88
```
There are multiple types of PCDs. `FixedAtBuild` PCD is only one of them. In our code we've used `FixedPcdGet8` call to get PCD value, this call would only work if PCD is `FixedAtBuild`. However there is a generic `PcdGet8` call that can be used to get a value of PCD regardless its PCD type.
[https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h):
```
#define PcdGet8(TokenName) _PCD_GET_MODE_8_##TokenName
```
In our case this would expand to:
```
PcdGet8(PcdInt8) -> _PCD_GET_MODE_8_PcdInt8 -> _gPcd_FixedAtBuild_PcdInt8
```
The latter one is a variable that is defined in a `AutoGen.c` file:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdInt8 = _PCD_VALUE_PcdInt8 // =0x88U;
```
So as you can see result would be the same. The difference is that `PcdGet` would work with other PCD types, that we would cover in the next lessons.
You can verify that this code:
```
Print(L"PcdInt8=%d\n", FixedPcdGet8(PcdInt8));
Print(L"PcdInt8=%d\n", PcdGet8(PcdInt8));
```
Would produce the output:
```
FS0:\> PCDLesson.efi
PcdInt8=0x88
PcdInt8=0x88
```
# Other simple PCD types
In the example above we've used `UINT8` as a `<DatumType>` of our PCD. Along with this type EDK2 also allows you to use other integer data types `UINT16`, `UINT32` `UINT64` integer data types and a `BOOLEAN` type.
Add these PCDs to the `UefiLessonsPkg/UefiLessonsPkg.dec`:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdInt16|0x1616|UINT16|0x77DFB6E6
gUefiLessonsPkgTokenSpaceGuid.PcdInt32|0x32323232|UINT32|0xF2A48130
gUefiLessonsPkgTokenSpaceGuid.PcdInt64|0x6464646464646464|UINT64|0x652F4E29
gUefiLessonsPkgTokenSpaceGuid.PcdBool|TRUE|BOOLEAN|0x69E88A63
```
And include them in our module `UefiLessonsPkg/PCDLesson/PCDLesson.inf`:
```
[FixedPcd]
...
gUefiLessonsPkgTokenSpaceGuid.PcdInt16
gUefiLessonsPkgTokenSpaceGuid.PcdInt32
gUefiLessonsPkgTokenSpaceGuid.PcdInt64
gUefiLessonsPkgTokenSpaceGuid.PcdBool
```
This would populate them to the `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h`:
```
#define _PCD_TOKEN_PcdInt16 0U
#define _PCD_SIZE_PcdInt16 2
#define _PCD_GET_MODE_SIZE_PcdInt16 _PCD_SIZE_PcdInt16
#define _PCD_VALUE_PcdInt16 0x1616U
extern const UINT16 _gPcd_FixedAtBuild_PcdInt16;
#define _PCD_GET_MODE_16_PcdInt16 _gPcd_FixedAtBuild_PcdInt16
//#define _PCD_SET_MODE_16_PcdInt16 ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
#define _PCD_TOKEN_PcdInt32 0U
#define _PCD_SIZE_PcdInt32 4
#define _PCD_GET_MODE_SIZE_PcdInt32 _PCD_SIZE_PcdInt32
#define _PCD_VALUE_PcdInt32 0x32323232U
extern const UINT32 _gPcd_FixedAtBuild_PcdInt32;
#define _PCD_GET_MODE_32_PcdInt32 _gPcd_FixedAtBuild_PcdInt32
//#define _PCD_SET_MODE_32_PcdInt32 ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
#define _PCD_TOKEN_PcdInt64 0U
#define _PCD_SIZE_PcdInt64 8
#define _PCD_GET_MODE_SIZE_PcdInt64 _PCD_SIZE_PcdInt64
#define _PCD_VALUE_PcdInt64 0x6464646464646464ULL
extern const UINT64 _gPcd_FixedAtBuild_PcdInt64;
#define _PCD_GET_MODE_64_PcdInt64 _gPcd_FixedAtBuild_PcdInt64
//#define _PCD_SET_MODE_64_PcdInt64 ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
#define _PCD_TOKEN_PcdBool 0U
#define _PCD_SIZE_PcdBool 1
#define _PCD_GET_MODE_SIZE_PcdBool _PCD_SIZE_PcdBool
#define _PCD_VALUE_PcdBool 1U
extern const BOOLEAN _gPcd_FixedAtBuild_PcdBool;
#define _PCD_GET_MODE_BOOL_PcdBool _gPcd_FixedAtBuild_PcdBool
//#define _PCD_SET_MODE_BOOL_PcdBool ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
```
And to the `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c`:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT16 _gPcd_FixedAtBuild_PcdInt16 = _PCD_VALUE_PcdInt16;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 _gPcd_FixedAtBuild_PcdInt32 = _PCD_VALUE_PcdInt32;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT64 _gPcd_FixedAtBuild_PcdInt64 = _PCD_VALUE_PcdInt64;
GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdBool = _PCD_VALUE_PcdBool;
```
Everything is similar to the `UINT8` case.
Like before you can use either `FixedPcdGet` or `PcdGet` API to get the PCD values:
```
Print(L"PcdInt16=0x%x\n", FixedPcdGet16(PcdInt16));
Print(L"PcdInt32=0x%x\n", FixedPcdGet32(PcdInt32));
Print(L"PcdInt64=0x%x\n", FixedPcdGet64(PcdInt64));
Print(L"PcdBool=0x%x\n", FixedPcdGetBool(PcdBool));
Print(L"PcdInt16=0x%x\n", PcdGet16(PcdInt16));
Print(L"PcdInt32=0x%x\n", PcdGet32(PcdInt32));
Print(L"PcdInt64=0x%x\n", PcdGet64(PcdInt64));
Print(L"PcdIntBool=0x%x\n", PcdGetBool(PcdBool));
```
Once again the values would be the same:
```
FS0:\> PCDLesson.efi
...
PcdInt16=0x1616
PcdInt32=0x32323232
PcdInt64=0x64646464
PcdBool=0x1
PcdInt16=0x1616
PcdInt32=0x32323232
PcdInt64=0x64646464
PcdIntBool=0x1
```
# Expressions in initialization values
It is possible to use expressions in initialization values. Keep in mind that when you use `|` character in you operations, you must put the expression inside the brackets `(...)`.
Here are some examples:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdExpression|0xFF000000 + 0x00FFFFFF|UINT32|0x9C405222
gUefiLessonsPkgTokenSpaceGuid.PcdExpression_1|((0xFFFFFFFF & 0x000000FF) << 8) + 0x33|UINT32|0x5911C44B
gUefiLessonsPkgTokenSpaceGuid.PcdExpression_2|(0x00000000 | 0x00100000)|UINT32|0xAD880207
gUefiLessonsPkgTokenSpaceGuid.PcdExpression_3|(56 < 78) || !(23 > 44)|BOOLEAN|0x45EDE955
```
Populate them to the INF file:
```
[FixedPcd]
...
gUefiLessonsPkgTokenSpaceGuid.PcdExpression
gUefiLessonsPkgTokenSpaceGuid.PcdExpression_1
gUefiLessonsPkgTokenSpaceGuid.PcdExpression_2
gUefiLessonsPkgTokenSpaceGuid.PcdExpression_3
```
And if you build and look at the `AutoGen.h` you'll see that values are calculated correctly:
```
...
#define _PCD_VALUE_PcdExpression 4294967295U // =0xffffffff
...
#define _PCD_VALUE_PcdExpression_1 65331U // =0xff33
...
#define _PCD_VALUE_PcdExpression_2 1048576U // =0x100000
...
#define _PCD_VALUE_PcdExpression_3 1U
...
```
# `VOID*` PCD data type
Besides the simple types `UINT8`/`UINT16`/`UINT32`/`UINT64`/`BOOLEAN` EDKII allows to use `VOID*` type. Let's look at the ways how it can be used.
## String PCDs
Add these PCDs to the `UefiLessonsPkg/UefiLessonsPkg.dec`:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdAsciiStr|"hello"|VOID*|0xB29914B5
gUefiLessonsPkgTokenSpaceGuid.PcdUCS2Str|L"hello"|VOID*|0xF22124E5
```
In case you didn't notice the difference is in that in the first case the value provided as `"<...>"`, and in the second case as `L"<...>"`.
Now add the PCDs to the `UefiLessonsPkg/PCDLesson/PCDLesson.inf`:
```
[FixedPcd]
...
gUefiLessonsPkgTokenSpaceGuid.PcdAsciiStr
gUefiLessonsPkgTokenSpaceGuid.PcdUCS2Str
```
Build and investigate generated AutoGen files:
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h`:
```
#define _PCD_TOKEN_PcdAsciiStr 0U
#define _PCD_VALUE_PcdAsciiStr _gPcd_FixedAtBuild_PcdAsciiStr
extern const UINT8 _gPcd_FixedAtBuild_PcdAsciiStr[6];
#define _PCD_GET_MODE_PTR_PcdAsciiStr _gPcd_FixedAtBuild_PcdAsciiStr
#define _PCD_SIZE_PcdAsciiStr 6
#define _PCD_GET_MODE_SIZE_PcdAsciiStr _PCD_SIZE_PcdAsciiStr
//#define _PCD_SET_MODE_PTR_PcdAsciiStr ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
#define _PCD_TOKEN_PcdUCS2Str 0U
#define _PCD_VALUE_PcdUCS2Str _gPcd_FixedAtBuild_PcdUCS2Str
extern const UINT16 _gPcd_FixedAtBuild_PcdUCS2Str[6];
#define _PCD_GET_MODE_PTR_PcdUCS2Str _gPcd_FixedAtBuild_PcdUCS2Str
#define _PCD_SIZE_PcdUCS2Str 12
#define _PCD_GET_MODE_SIZE_PcdUCS2Str _PCD_SIZE_PcdUCS2Str
//#define _PCD_SET_MODE_PTR_PcdUCS2Str ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
```
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c`:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdAsciiStr[6] = {104, 101, 108, 108, 111, 0 };
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdAsciiStr = 6;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT16 _gPcd_FixedAtBuild_PcdUCS2Str[6] = {104, 101, 108, 108, 111, 0 };
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdUCS2Str = 12;
```
As you can see the array for the `PcdAsciiStr` consists from the UINT8 elements, while the `PcdUCS2Str` array consists of `UINT16` elements.
Another important difference is that the constants for the string sizes in bytes are generated (effectively it is the `sizeof()` value). These values are calculated dynamically by the build system.
To get the `VOID*` fixed type PCD value you can use `FixedPcdGetPtr`/`PcdGetPtr` APIs and to get its size in bytes you can use `FixedPcdGetSize`/`PcdGetSize` APIs:
```cpp
Print(L"PcdAsciiStr=%a\n", FixedPcdGetPtr(PcdAsciiStr));
Print(L"PcdAsciiStrSize=%d\n", FixedPcdGetSize(PcdAsciiStr));
Print(L"PcdUCS2Str=%s\n", PcdGetPtr(PcdUCS2Str));
Print(L"PcdUCS2StrSize=%d\n", PcdGetSize(PcdUCS2Str));
```
Here are results:
```
FS0:\> PCDLesson.efi
...
PcdAsciiStr=hello
PcdAsciiStrSize=6
PcdUCS2Str=hello
PcdUCS2StrSize=12
```
If you want to understand preprocessor substitutions, you can unravel its logic like we did before using the defines from the [https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PcdLib.h):
```cpp
#define FixedPcdGetPtr(TokenName) ((VOID *)_PCD_VALUE_##TokenName)
#define FixedPcdGetSize(TokenName) _PCD_SIZE_##TokenName
#define PcdGetPtr(TokenName) _PCD_GET_MODE_PTR_##TokenName
#define PcdGetSize(TokenName) _PCD_GET_MODE_SIZE_##TokenName
```
In our example we've used `"..."/L"..."` syntax for string initialization, but it is also possible to use `'...'/L'...'` syntax.
## Byte Array PCDs
The more general usage for `VOID*` is byte arrays.
As an example add this PCD to the `UefiLessonsPkg/UefiLessonsPkg.dec`:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdArray|{0xA5, 0xA6, 0xA7}|VOID*|0xD5DB9A27
```
Populate it to the `UefiLessonsPkg/PCDLesson/PCDLesson.inf`:
```
[FixedPcd]
...
gUefiLessonsPkgTokenSpaceGuid.PcdArray
```
And look at the AutoGen files:
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h`:
```
#define _PCD_TOKEN_PcdArray 0U
#define _PCD_VALUE_PcdArray (VOID *)_gPcd_FixedAtBuild_PcdArray
extern const UINT8 _gPcd_FixedAtBuild_PcdArray[3];
#define _PCD_GET_MODE_PTR_PcdArray (VOID *)_gPcd_FixedAtBuild_PcdArray
#define _PCD_SIZE_PcdArray 3
#define _PCD_GET_MODE_SIZE_PcdArray _PCD_SIZE_PcdArray
//#define _PCD_SET_MODE_PTR_PcdArray ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
```
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c`:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArray[3] = {0xA5, 0xA6, 0xA7};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArray = 3;
```
Here is a one way of how we can print elements of our array. We need the `(UINT8*)` cast as the data is cast to `(VOID *)` in `AutoGen.h`.
```
for (UINTN i=0; i<FixedPcdGetSize(PcdArray); i++) {
Print(L"PcdArray[%d]=0x%02x\n", i, ((UINT8*)FixedPcdGetPtr(PcdArray))[i]);
}
```
Verify the output:
```
FS0:\> PCDLesson.efi
...
PcdArray[0]=0xA5
PcdArray[1]=0xA6
PcdArray[2]=0xA7
```
## GUID PCDs
With the byte array initialization syntax you can initialize any custom structure, as soon as you understand its type. Let's take `EFI_GUID` (`=GUID`) structure:
```
typedef struct {
UINT32 Data1;
UINT16 Data2;
UINT16 Data3;
UINT8 Data4[8];
} GUID;
typedef GUID EFI_GUID;
```
If you want to encode GUID "f1740707-691d-4203-bfab-99e132fa4166" you can do it like this:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdGuidInBytes|{0x07, 0x07, 0x74, 0xF1, 0x1D, 0x69, 0x03, 0x42, 0xBF, 0xAB, 0x99, 0xE1, 0x32, 0xFA, 0x41, 0x66}|VOID*|0xB9E0CDC0
```
So as you can see you need to reshuffle some bytes to get a correct representation. This is necessary because x86 is little-endian, and in such systems bytes of a number are placed in memory from highest to lowest.
So this is the thing that you need to keep in mind when you would encode byte array initializations for you custom structures.
As GUIDs are excessively used in UEFI code, EDK2 has a helper syntax for GUID PCD initialization. Let's define another PCD via this helper syntax:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdGuid|{GUID("f1740707-691d-4203-bfab-99e132fa4166")}|VOID*|0x7F2066F7
```
Now populate both values to INF:
```
[FixedPcd]
...
gUefiLessonsPkgTokenSpaceGuid.PcdGuidInBytes
gUefiLessonsPkgTokenSpaceGuid.PcdGuid
```
Now build and look at the AutoGen files:
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h`
```
#define _PCD_TOKEN_PcdGuidInBytes 0U
#define _PCD_VALUE_PcdGuidInBytes (VOID *)_gPcd_FixedAtBuild_PcdGuidInBytes
extern const UINT8 _gPcd_FixedAtBuild_PcdGuidInBytes[16];
#define _PCD_GET_MODE_PTR_PcdGuidInBytes (VOID *)_gPcd_FixedAtBuild_PcdGuidInBytes
#define _PCD_SIZE_PcdGuidInBytes 16
#define _PCD_GET_MODE_SIZE_PcdGuidInBytes _PCD_SIZE_PcdGuidInBytes
//#define _PCD_SET_MODE_PTR_PcdGuidInBytes ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
#define _PCD_TOKEN_PcdGuid 0U
#define _PCD_VALUE_PcdGuid (VOID *)_gPcd_FixedAtBuild_PcdGuid
extern const UINT8 _gPcd_FixedAtBuild_PcdGuid[16];
#define _PCD_GET_MODE_PTR_PcdGuid (VOID *)_gPcd_FixedAtBuild_PcdGuid
#define _PCD_SIZE_PcdGuid 16
#define _PCD_GET_MODE_SIZE_PcdGuid _PCD_SIZE_PcdGuid
//#define _PCD_SET_MODE_PTR_PcdGuid ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD
```
- `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.c`
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuidInBytes[16] = {0x07, 0x07, 0x74, 0xF1, 0x1D, 0x69, 0x03, 0x42, 0xBF, 0xAB, 0x99, 0xE1, 0x32, 0xFA, 0x41, 0x66};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuidInBytes = 16;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuid[16] = {0x07, 0x07, 0x74, 0xF1, 0x1D, 0x69, 0x03, 0x42, 0xBF, 0xAB, 0x99, 0xE1, 0x32, 0xFA, 0x41, 0x66};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuid = 16;
```
As you can see in the output, the PCD encoding and initialization are the same in both cases.
If we want to use GUIDs in code, we still need not forget to cast them from `(VOID *)` to `(EFI_GUID*)`:
```cpp
Print(L"PcdGuidInBytes=%g\n", *(EFI_GUID*)FixedPcdGetPtr(PcdGuidInBytes));
Print(L"PcdGuid=%g\n", *(EFI_GUID*)FixedPcdGetPtr(PcdGuid));
```
The result of both `Print` statements would be the same:
```
FS0:\> PCDLesson.efi
...
PcdGuidInBytes=F1740707-691D-4203-BFAB-99E132FA4166
PcdGuid=F1740707-691D-4203-BFAB-99E132FA4166
```
There are two more more methods how you can initialize PCD containing GUID. You can either use another GUID for initialization or standard C syntax for `EFI_GUID` structure initialization:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdGuidByPCD|{GUID(gUefiLessonsPkgTokenSpaceGuid)}|VOID*|0x0860CCD5
gUefiLessonsPkgTokenSpaceGuid.PcdGuidByEfiGuid|{GUID({0x150cab53, 0xad47, 0x4385, {0xb5, 0xdd, 0xbc, 0xfc, 0x76, 0xba, 0xca, 0xf0}})}|VOID*|0x613506D5
```
This would `AutoGen.c`:
```cpp
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuidByPCD[16] = {0x53, 0xAB, 0x0C, 0x15, 0x47, 0xAD, 0x85, 0x43, 0xB5, 0xDD, 0xBC, 0xFC, 0x76, 0xBA, 0xCA, 0xF0};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuidByPCD = 16;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdGuidByEfiGuid[16] = {0x53, 0xAB, 0x0C, 0x15, 0x47, 0xAD, 0x85, 0x43, 0xB5, 0xDD, 0xBC, 0xFC, 0x76, 0xBA, 0xCA, 0xF0};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdGuidByEfiGuid = 16;
```
## `DEVICE_PATH`
Besides the `GUID(...)` helper, EDKII also has `DEVICE_PATH(...)` helper, to initialize another special UEFI structure - device path. We'll investigate this structure another time.
But for an example, the paths that are printed at the start of UEFI shell are text representation of UEFI device paths:
```
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
FS0: Alias(s):HD0a1:;BLK1:
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
BLK0: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
BLK2: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
Press ESC in 5 seconds to skip startup.nsh or any other key to continue.
```
Let's use one of them for PCD initialization:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdDevicePath|{DEVICE_PATH("PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)")}|VOID*|0xC56EE1E2
```
This would produce the following array in the `AutoGen.c`:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdDevicePath[30] = {0x02,0x01,0x0c,0x00,0xd0,0x41,0x03,0x0a,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x01,0x01,0x03,0x01,0x08,0x00,0x00,0x00,0x00,0x00,0x7f,0xff,0x04,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdDevicePath = 30;
```
To verify that the array are correct, let's print created device path in our program. For this we need to include `DevicePathLib.h` header:
```cpp
#include <Library/DevicePathLib.h>
```
And use `ConvertDevicePathToText` function:
```cpp
Print(L"PcdDevicePath: %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) FixedPcdGetPtr(PcdDevicePath), FALSE, FALSE));
```
Here is the output:
```
FS0:\> PCDLesson.efi
...
PcdDevicePath: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
```
## Using integer casts
It is possible to initialize `VOID*` array using `UINT8(...)`, `UINT16(...)`, `UINT32(...)`, `UINT64(...)` cast helpers:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdIntCasts|{UINT16(0x1122), UINT32(0x33445566), UINT8(0x77), UINT64(0x8899aabbccddeeff)}|VOID*|0x647456A6
```
If you look at the `AutoGen.c` you'll see that array is initialized with respect to little-endian architecture:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdIntCasts[15] = {0x22, 0x11, 0x66, 0x55, 0x44, 0x33, 0x77, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdIntCasts = 15;
```
## `LABEL` and `OFFSET_OF`
It is possible to assign labels to some elemets and get offsets of these elements via `LABEL(...)/OFFSET_OF(...)` syntax. Example:
```
[PcdsFixedAtBuild]
...
UefiLessonsPkgTokenSpaceGuid.PcdWithLabels|{ 0x0A, 0x0B, OFFSET_OF(End), 0x0C, LABEL(Start) 0x0D, LABEL(End) 0x0E, 0x0F, OFFSET_OF(Start) }|VOID*|0xD91A8BF6
```
This will give you this in `AutoGen.c`:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdWithLabels[8] = {0x0A, 0x0B, 0x05, 0x0C, 0x0D, 0x0E, 0x0F, 0x04};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdWithLabels = 8;
```
## Combined array initialization
It is possible to combine different methods of byte array initialization:
```
gUefiLessonsPkgTokenSpaceGuid.PcdArrayExt|{0x11, UINT16(0x2233), UINT32(0x44556677), L"hello", "world!", GUID("09b9b358-70bd-421e-bafb-4f97e2ac7d44")}|VOID*|0x7200C5DF
```
This will give you:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayExt[42] = {0x11, 0x33, 0x22, 0x77, 0x66, 0x55, 0x44, 0x68, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0 0, 0x58, 0xB3, 0xB9, 0x09, 0xBD, 0x70, 0x1E, 0x42, 0xBA, 0xFB, 0x4F, 0x97, 0xE2, 0xAC, 0x7D, 0x44};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayExt = 42;
```
# Custom types
It is possible to create custom types for you PCDs.
For example create this file `UefiLessonsPkg/Include/CustomPcdTypes.h`:
```
#ifndef CUSTOM_PCD_TYPES_H
#define CUSTOM_PCD_TYPES_H
typedef struct {
EFI_GUID Guid;
CHAR16 Name[6];
} InnerCustomStruct;
typedef struct {
UINT8 Val8;
UINT32 Val32[2];
InnerCustomStruct ValStruct;
union {
struct {
UINT8 Field1:1;
UINT8 Field2:4;
UINT8 Filed3:3;
} BitFields;
UINT8 Byte;
} ValUnion;
} CustomStruct;
#endif
```
Add the `Include` folder to the DEC file:
```
[Includes]
Include
```
You can use the created `CustomStruct` structure type and initialize its values via this sytnax:
```
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct|{0}|CustomStruct|0x535D4CB5 {
<Packages>
UefiLessonsPkg/UefiLessonsPkg.dec
<HeaderFiles>
CustomPcdTypes.h
}
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.Val8|0x11
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.Val32[0]|0x22334455
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.Val32[1]|0x66778899
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.ValStruct.Guid|{GUID("f1740707-691d-4203-bfab-99e132fa4166")}
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.ValStruct.Name|L'Hello'
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct.ValUnion.BitFields.Field2|0xF
```
`AutoGen.c` will be created with the following data:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdCustomStruct[44] = {0x11,0x00,0x00,0x00,0x55,0x44,0x33,0x22,0x99,0x88,0x77,0x66,0x07,0x07,0x74,0xf1,0x1d,0x69,0x03,0x42,0xbf,0xab,0x99,0xe1,0x32,0xfa,0x41,0x66,0x48,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x6f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdCustomStruct = 44;
```
If you group you fields, you'll see that all of them were initialized as intendend:
```
{
0x11, // UINT8 Val8
0x00,0x00,0x00, // alignment
0x55,0x44,0x33,0x22, // UINT32 Val32[0]
0x99,0x88,0x77,0x66, // UINT32 Val32[1]
0x07,0x07,0x74,0xf1,0x1d,0x69,0x03,0x42,0xbf,0xab,0x99,0xe1,0x32,0xfa,0x41,0x66, // InnerCustomStruct.Guid
0x48,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x6f,0x00,0x00,0x00, // InnerCustomStruct.Name
0x1e, // ValUnion
0x00,0x00,0x00 // alignment
}
```
If a field-by-filed initialization seem too long, you can use in-place C style array initialization with the help of a special `CODE(...)` syntax. But in this case you can't use helpers like `GUID(...)` or `L'...'` initialization. So the same initialization for our structure would look like this:
```
gUefiLessonsPkgTokenSpaceGuid.PcdCustomStruct_1|{CODE(
{
0x11,
{0x22334455, 0x66778899},
{
{0xf1740707, 0x691d, 0x4203, {0xbf, 0xab, 0x99, 0xe1, 0x32, 0xfa, 0x41, 0x66}},
{0x0048, 0x0065, 0x006c, 0x006c, 0x006f, 0x0000}
},
{{0x0, 0xf, 0x0}}
}
)}|CustomStruct|0xC1D6B9A7 {
<Packages>
UefiLessonsPkg/UefiLessonsPkg.dec
<HeaderFiles>
CustomPcdTypes.h
}
```
You can verify that the result is the same data, if you look at the `AutoGen.c` file:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdCustomStruct_1[44] = {0x11,0x00,0x00,0x00,0x55,0x44,0x33,0x22,0x99,0x88,0x77,0x66,0x07,0x07,0x74,0xf1,0x1d,0x69,0x03,0x42,0xbf,0xab,0x99,0xe1,0x32,0xfa,0x41,0x66,0x48,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x6f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdCustomStruct_1 = 44;
```
# Array with fixed sizes
It is possible to fix PCD array size. Keep in mind that if you use arrays for types with sizes more that 1 byte like `UINT32[3]`, you need to cast every initialization value to the type`. To avoid this it is possible to use special `CODE(...)` syntax:
```
[PcdsFixedAtBuild]
...
gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize|{0x0}|UINT8[12]|0x4C4CB9A3
gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_1|{0x0}|UINT32[3]|0x285DAD21
gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_2|{UINT32(0x11223344), UINT32(0x55667788), UINT32(0x99aabbcc)}|UINT32[3]|0x25D6ED26
gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_3|{CODE({0x11223344, 0x55667788, 0x99aabbcc})}|UINT32[3]|0xE5BC424D
```
This will give you this in `AutoGen.c`:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize[8] = {0xee,0xff,0x00,0x00,0x00,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize = 8;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize_1[12] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_1 = 12;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize_2[12] = {0x44,0x33,0x22,0x11,0x88,0x77,0x66,0x55,0xcc,0xbb,0xaa,0x99};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_2 = 12;
GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdArrayWithFixedSize_3[12] = {0x44,0x33,0x22,0x11,0x88,0x77,0x66,0x55,0xcc,0xbb,0xaa,0x99};
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_3 = 12;
```
You can also create fixed size arrays for you custom types:
```
gUefiLessonsPkgTokenSpaceGuid.PcdArrayWithFixedSize_4|{0x0}|CustomStruct[2]|0x0D00EE44 {
<Packages>
UefiLessonsPkg/UefiLessonsPkg.dec
<HeaderFiles>
CustomPcdTypes.h
}
```
Here we don't do any field initialization, but for the proof of syntax you can look at the `AutoGen.c` and verify that the size of the final data array is twice of the usual one for our structure:
```
GLOBAL_REMOVE_IF_UNREFERENCED const UINTN _gPcd_FixedAtBuild_Size_PcdArrayWithFixedSize_4 = 88;
```
# `PcdValueInit`
One interesting observation. When you use PCD with custom types, or fixed size arrays, the build system would create `PcdValueInit` folder in your `Build` directory. This directory would contain a special C program `PcdValueInit` that the build system will use to get the data for the `AutoGen.c` file:
```
$ find ./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueCommon.c
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit.o
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit.c
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/Input.txt
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit.d
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueCommon.d
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/Output.txt
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueCommon.o
./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/Makefile
```
You can check the help message of the program:
```
$ ./Build/UefiLessonsPkg/RELEASE_GCC5/PcdValueInit/PcdValueInit -h
Usage: -i <input_file> -o <output_file>
optional arguments:
-h, --help Show this help message and exit
-i INPUT_FILENAME, --input INPUT_FILENAME
PCD Database Input file name
-o OUTPUT_FILENAME, --output OUTPUT_FILENAME
PCD Database Output file name
```
From this you can guess that this programm creates `Output.txt` file from the `Input.txt` file in the same directory. If you curious about the build system internals you can check these `*.txt` files or check the program sources.
# Links
- [https://edk2-docs.gitbook.io/edk-ii-pcd-specification/](https://edk2-docs.gitbook.io/edk-ii-pcd-specification/)
- [https://edk2-docs.gitbook.io/edk-ii-dec-specification/2_dec_file_overview/210_pcd_usage](https://edk2-docs.gitbook.io/edk-ii-dec-specification/2_dec_file_overview/210_pcd_usage)
|