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
|
In this lesson we would talk about the `DEBUG` macro in edk2 codebase. Example of a `DEBUG` macro use:
```
DEBUG ((EFI_D_ERROR, "Hello Debug! Check this variable: %d\n", MyVar));
```
So the string formatting is similar to the `Print` function, but there are couple of benefits:
- log levels (`EFI_D_*`) help to categorize log messages. With the configuration through PCD we can easily turn on/off different types of debug messages
- there can be several implementations of the `DEBUG` functionality. Different UEFI stages/modules can have different `DEBUG` implementations
All predefined log message categories are defined in the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h
```
#define DEBUG_INIT 0x00000001 // Initialization
#define DEBUG_WARN 0x00000002 // Warnings
#define DEBUG_LOAD 0x00000004 // Load events
#define DEBUG_FS 0x00000008 // EFI File system
#define DEBUG_POOL 0x00000010 // Alloc & Free (pool)
#define DEBUG_PAGE 0x00000020 // Alloc & Free (page)
#define DEBUG_INFO 0x00000040 // Informational debug messages
#define DEBUG_DISPATCH 0x00000080 // PEI/DXE/SMM Dispatchers
#define DEBUG_VARIABLE 0x00000100 // Variable
#define DEBUG_BM 0x00000400 // Boot Manager
#define DEBUG_BLKIO 0x00001000 // BlkIo Driver
#define DEBUG_NET 0x00004000 // Network Io Driver
#define DEBUG_UNDI 0x00010000 // UNDI Driver
#define DEBUG_LOADFILE 0x00020000 // LoadFile
#define DEBUG_EVENT 0x00080000 // Event messages
#define DEBUG_GCD 0x00100000 // Global Coherency Database changes
#define DEBUG_CACHE 0x00200000 // Memory range cachability changes
#define DEBUG_VERBOSE 0x00400000 // Detailed debug messages that may
// significantly impact boot performance
#define DEBUG_ERROR 0x80000000 // Error
```
But generally these alias values are used:
```
//
// Aliases of debug message mask bits
//
#define EFI_D_INIT DEBUG_INIT
#define EFI_D_WARN DEBUG_WARN
#define EFI_D_LOAD DEBUG_LOAD
#define EFI_D_FS DEBUG_FS
#define EFI_D_POOL DEBUG_POOL
#define EFI_D_PAGE DEBUG_PAGE
#define EFI_D_INFO DEBUG_INFO
#define EFI_D_DISPATCH DEBUG_DISPATCH
#define EFI_D_VARIABLE DEBUG_VARIABLE
#define EFI_D_BM DEBUG_BM
#define EFI_D_BLKIO DEBUG_BLKIO
#define EFI_D_NET DEBUG_NET
#define EFI_D_UNDI DEBUG_UNDI
#define EFI_D_LOADFILE DEBUG_LOADFILE
#define EFI_D_EVENT DEBUG_EVENT
#define EFI_D_VERBOSE DEBUG_VERBOSE
#define EFI_D_ERROR DEBUG_ERROR
```
The DEBUG macro itself is also defined in the `DebugLib` library header (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h).
If you'll split all the preprocessing hell it generally looks like this when it is used:
```
if (DebugPrintEnabled ()) {
if (DebugPrintLevelEnabled (PrintLevel)) {
DebugPrint (PrintLevel, ##__VA_ARGS__);
}
}
```
The `DEBUG` macro interface is defined in a `DebugLib` header. But for the actual implementation we need to look at a particular library instance that is used.
If you look at the https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.dsc you'll see that:
- `DebugLib` has different implementations in different UEFI stages
- `DebugLib` has different implementations based on a `DEBUG_ON_SERIAL_PORT` definition
```
[LibraryClasses.common.SEC]
!ifdef $(DEBUG_ON_SERIAL_PORT)
DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
!else
DebugLib|OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPort.inf
!endif
```
```
[LibraryClasses.common.PEI_CORE]
# [LibraryClasses.common.PEIM]
# [LibraryClasses.common.DXE_CORE]
# [LibraryClasses.common.DXE_RUNTIME_DRIVER]
# [LibraryClasses.common.UEFI_DRIVER]
# [LibraryClasses.common.DXE_DRIVER]
# [LibraryClasses.common.UEFI_APPLICATION]
# [LibraryClasses.common.DXE_SMM_DRIVER]
# [LibraryClasses.common.SMM_CORE]
!ifdef $(DEBUG_ON_SERIAL_PORT)
DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
!else
DebugLib|OvmfPkg/Library/PlatformDebugLibIoPort/PlatformDebugLibIoPort.inf
!endif
```
As `DEBUG_ON_SERIAL_PORT` is not defined by default, the main library in our case would be `PlatformDebugLibIoPort` (https://github.com/tianocore/edk2/tree/master/OvmfPkg/Library/PlatformDebugLibIoPort):
```
DebugLib|OvmfPkg/Library/PlatformDebugLibIoPort/PlatformDebugLibIoPort.inf
```
As you remember `DEBUG` macro gets translated to something like:
```
if (DebugPrintEnabled ()) {
if (DebugPrintLevelEnabled (PrintLevel)) {
DebugPrint (PrintLevel, ##__VA_ARGS__);
}
}
```
Let's look at the https://github.com/tianocore/edk2/blob/master/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLib.c to see how `DebugPrintEnabled`, `DebugPrintLevelEnabled` and `DebugPrint` are implemented.
`DEBUG` macro first checks `DebugPrintEnabled` result. This function checks PCD `PcdDebugPropertyMask` if it has `DEBUG_PROPERTY_DEBUG_PRINT_ENABLED` bit set:
```
BOOLEAN
EFIAPI
DebugPrintEnabled (
VOID
)
{
return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0);
}
```
Then `DebugPrintLevelEnabled` function checks if passed `ErrorLevel` is present in the PCD `PcdFixedDebugPrintErrorLevel`:
```
BOOLEAN
EFIAPI
DebugPrintLevelEnabled (
IN CONST UINTN ErrorLevel
)
{
return (BOOLEAN) ((ErrorLevel & PcdGet32(PcdFixedDebugPrintErrorLevel)) != 0);
}
```
Then actual `DebugPrint` function is executed. It simply passes control to `DebugVPrint`:
```
VOID
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Marker;
VA_START (Marker, Format);
DebugVPrint (ErrorLevel, Format, Marker);
VA_END (Marker);
}
```
And `DebugVPrint` in turn passes control to `DebugPrintMarker`:
```
VOID
EFIAPI
DebugVPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
IN VA_LIST VaListMarker
)
{
DebugPrintMarker (ErrorLevel, Format, VaListMarker, NULL);
}
```
`DebugPrintMarker` is the main debug function. It check if passed `ErrorLevel` is present in `GetDebugPrintErrorLevel()` output and in the end performs write of the debug string to the I/O port defined by PCD `PcdDebugIoPort`:
```
VOID
DebugPrintMarker (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
IN VA_LIST VaListMarker,
IN BASE_LIST BaseListMarker
)
{
CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];
UINTN Length;
//
// If Format is NULL, then ASSERT().
//
ASSERT (Format != NULL);
//
// Check if the global mask disables this message or the device is inactive
//
if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0 ||
!PlatformDebugLibIoPortFound ()) {
return;
}
//
// Convert the DEBUG() message to an ASCII String
//
if (BaseListMarker == NULL) {
Length = AsciiVSPrint (Buffer, sizeof (Buffer), Format, VaListMarker);
} else {
Length = AsciiBSPrint (Buffer, sizeof (Buffer), Format, BaseListMarker);
}
//
// Send the print string to the debug I/O port
//
IoWriteFifo8 (PcdGet16 (PcdDebugIoPort), Length, Buffer);
}
```
`GetDebugPrintErrorLevel()` is a function from `DebugPrintErrorLevelLib` library. Our `OvmfPkgX64.dsc` defines this implementation for it:
```
[LibraryClasses]
...
DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
```
If you'll look at it sources you'll see that it simply checks for another PCD `PcdDebugPrintErrorLevel` (https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.c):
```
UINT32
EFIAPI
GetDebugPrintErrorLevel (
VOID
)
{
//
// Retrieve the current debug print error level mask from PcdDebugPrintErrorLevel.
//
return PcdGet32 (PcdDebugPrintErrorLevel);
}
```
## Summary:
The code `DEBUG (( ErrorLevel, String, ... ))` does the following:
- checks if `PcdDebugPropertyMask` has `DEBUG_PROPERTY_DEBUG_PRINT_ENABLED`
- checks if passed `ErrorLevel` is set in `PcdFixedDebugPrintErrorLevel`
- checks if passed `ErrorLevel` is set in `PcdDebugPrintErrorLevel`
- writes formatted String to I/O port defined by `PcdDebugIoPort`
# Check if `PcdDebugPropertyMask` has `DEBUG_PROPERTY_DEBUG_PRINT_ENABLED`
OVMF DSC file defines this PCD PcdDebugPropertyMask like this https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.dsc:
```
[PcdsFixedAtBuild]
!if $(SOURCE_DEBUG_ENABLE) == TRUE
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x17
!else
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F
!endif
```
It also redefines this PCD for Shell:
```
[Components]
ShellPkg/Application/Shell/Shell.inf {
...
<PcdsFixedAtBuild>
...
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF
}
```
You can check bit definitions for this PCD in a https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec:
```
## The mask is used to control DebugLib behavior.<BR><BR>
# BIT0 - Enable Debug Assert.<BR>
# BIT1 - Enable Debug Print.<BR>
# BIT2 - Enable Debug Code.<BR>
# BIT3 - Enable Clear Memory.<BR>
# BIT4 - Enable BreakPoint as ASSERT.<BR>
# BIT5 - Enable DeadLoop as ASSERT.<BR>
# @Prompt Debug Property.
# @Expression 0x80000002 | (gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask & 0xC0) == 0
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0|UINT8|0x00000005
```
So if the `SOURCE_DEBUG_ENABLE` is:
- TRUE: these features are enabled: Debug Assert/Debug Print/Debug Code/BreakPoint as ASSERT.
- FALSE: these features are enabled: Debug Assert/Debug Print/Debug Code/Clear Memory/DeadLoop as ASSERT.
In a `DEBUG` macro we compare this PCD aginst `DEBUG_PROPERTY_DEBUG_PRINT_ENABLED` (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h):
```
//
// Declare bits for PcdDebugPropertyMask
//
#define DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED 0x01
#define DEBUG_PROPERTY_DEBUG_PRINT_ENABLED 0x02
#define DEBUG_PROPERTY_DEBUG_CODE_ENABLED 0x04
#define DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED 0x08
#define DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED 0x10
#define DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED 0x20
```
As you can see this check would pass no matter of `SOURCE_DEBUG_ENABLE` setting.
# Checks if passed `ErrorLevel` is set in `PcdFixedDebugPrintErrorLevel`
This PCD is declared in https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec:
```
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel|0xFFFFFFFF|UINT32|0x30001016
```
This PCD is not redefined in the OVMF DSC file, so this is a final value. `0xFFFFFFFF` means that `DEBUG` message would pass this check no matter of its `EFI_D_*` print level.
# Checks if passed `ErrorLevel` is set in `PcdDebugPrintErrorLevel`
This PCD is declared in a https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec
```
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000|UINT32|0x00000006
```
By default it is defined in the `MdePkg.dec` and in the `MdePkg.dsc` as `0x80000000` https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dsc:
```
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000
```
The `0x80000000` value means that only error messages (`EFI_D_ERROR`) would be printed (ErrorLevel bit definitions again are in https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h).
Although when we compile OVMF our main DSC is `OvmfPkgX64.dsc` https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.dsc. And this DSC redefines `PcdDebugPrintErrorLevel` PCD:
```
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000004F
```
The `0x8000004F` value is equal to: `EFI_D_ERROR | EFI_D_INFO | EFI_D_FS | EFI_D_LOAD | EFI_D_WARN | EFI_D_INIT`.
So it means that when DEBUG statement is enabled, only these categories of messages would be printed.
# Writes formatted String to I/O port defined by `PcdDebugIoPort`
The `PcdDebugIoPort` PCD is defined in https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkg.dec
```
[PcdsFixedAtBuild]
gUefiOvmfPkgTokenSpaceGuid.PcdDebugIoPort|0x402|UINT16|4
```
This PCD controls the destination I/O port for the debug messages in `PlatformDebugLibIoPort`
# Testing
To see debug messages we need to recompile OVMF in debug mode.
To do this use:
```
build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=DEBUG --tagname=GCC5
```
The build results would be in a folder
```
Build/OvmfX64/DEBUG_GCC5
```
Now let's run QEMU with these parameters:
```
qemu-system-x86_64 \
-drive if=pflash,format=raw,readonly,file=Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd \
-drive format=raw,file=fat:rw:~/UEFI_disk \
-net none \
-nographic \
-global isa-debugcon.iobase=0x402 \
-debugcon file:debug.log
```
This would create `debug.log` file with all log `DEBUG` messages from OVMF. This file would be recreated every time you run this command.
In a separate terminal window you can run:
```
tail -f debug.log
```
to follow this file content runtime.
I've included the profuced `debug.log` to the current folder.
# Replace GUIDs in a log file
If you start looking at the `debug.log` file, you'll see that it is full of GUIDs:
```
$ head debug.log
SecCoreStartupWithStack(0xFFFCC000, 0x820000)
Register PPI Notify: DCD0BE23-9586-40F4-B643-06522CED4EDE
Install PPI: 8C8CE578-8A3D-4F1C-9935-896185C32DD3
Install PPI: 5473C07A-3DCB-4DCA-BD6F-1E9689E7349A
The 0th FV start address is 0x00000820000, size is 0x000E0000, handle is 0x820000
Register PPI Notify: 49EDB1C1-BF21-4761-BB12-EB0031AABB39
Register PPI Notify: EA7CA24B-DED5-4DAD-A389-BF827E8F9B38
Install PPI: B9E0ABFE-5979-4914-977F-6DEE78C278A6
Install PPI: DBE23AA9-A345-4B97-85B6-B226F1617389
DiscoverPeimsAndOrderWithApriori(): Found 0x7 PEI FFS files in the 0th FV
```
It would be nice to replace them with meaningfull text for readability.
Luckily edk2 build system creates a file with names for used GUIDs:
```
$ head Build/OvmfX64/DEBUG_GCC5/FV/Guid.xref
8c1a6b71-0c4b-4497-aaad-07404edf142c PCDLesson
1BA0062E-C779-4582-8566-336AE8F78F09 ResetVector
df1ccef6-f301-4a63-9661-fc6030dcc880 SecMain
52C05B14-0B98-496c-BC3B-04B50211D680 PeiCore
9B3ADA4F-AE56-4c24-8DEA-F03B7558AE50 PcdPeim
A3610442-E69F-4DF3-82CA-2360C4031A23 ReportStatusCodeRouterPei
9D225237-FA01-464C-A949-BAABC02D31D0 StatusCodeHandlerPei
86D70125-BAA3-4296-A62F-602BEBBB9081 DxeIpl
222c386d-5abc-4fb4-b124-fbb82488acf4 PlatformPei
89E549B0-7CFE-449d-9BA3-10D8B2312D71 S3Resume2Pei
```
Let's create a python app that would parse this file, create a `GUID:NAME` dictionary and use it to replace all GUIDs with their names in a `debug.log` file. The result would be written to the `debug_parsed.log` file:
```
from shutil import copyfile
GUIDS_FILE_PATH = "Build/OvmfX64/DEBUG_GCC5/FV/Guid.xref"
EXTRA_GUIDS_FILE_PATH = ""
LOG_IN_FILE_PATH = "debug.log"
LOG_OUT_FILE_PATH = "debug_parsed.log"
guids = {}
with open(GUIDS_FILE_PATH) as p:
for line in p:
l = line.split(" ")
if len(l)==2:
guids[l[0].upper()] = l[1][:-1]
if EXTRA_GUIDS_FILE_PATH:
with open(EXTRA_GUIDS_FILE_PATH) as p:
for line in p:
l = line.split(" ")
if len(l)==2:
guids[l[0].upper()] = l[1][:-1]
copyfile(LOG_IN_FILE_PATH, LOG_OUT_FILE_PATH)
f = open(LOG_OUT_FILE_PATH, 'r')
filedata = f.read()
f.close()
for key,val in guids.items():
filedata = filedata.replace(key, val)
f = open(LOG_OUT_FILE_PATH, 'w')
f.write(filedata)
f.close()
```
I've also added a possibility to include another `GUID:NAME` file with a help of `EXTRA_GUIDS_FILE_PATH` variable. This can be usefull if for some reason any guids are not present in the main `Guid.xref` file.
Put this script in edk2 folder and run it with `python` or `python3`:
```
python replace_guids.py
```
Now look at the created `debug_parsed.log` file:
```
$ head debug_parsed.log
SecCoreStartupWithStack(0xFFFCC000, 0x820000)
Register PPI Notify: gEfiPeiSecurity2PpiGuid
Install PPI: gEfiFirmwareFileSystem2Guid
Install PPI: gEfiFirmwareFileSystem3Guid
The 0th FV start address is 0x00000820000, size is 0x000E0000, handle is 0x820000
Register PPI Notify: gEfiPeiFirmwareVolumeInfoPpiGuid
Register PPI Notify: gEfiPeiFirmwareVolumeInfo2PpiGuid
Install PPI: gEfiPeiLoadFilePpiGuid
Install PPI: gEfiTemporaryRamSupportPpiGuid
DiscoverPeimsAndOrderWithApriori(): Found 0x7 PEI FFS files in the 0th FV
```
It is much easier to read now!
|