]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Scripts/efi_debugging.py
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / BaseTools / Scripts / efi_debugging.py
1 #!/usr/bin/python3
2 '''
3 Copyright (c) Apple Inc. 2021
4 SPDX-License-Identifier: BSD-2-Clause-Patent
5
6 Class that abstracts PE/COFF debug info parsing via a Python file like
7 object. You can port this code into an arbitrary debugger by invoking
8 the classes and passing in a file like object that abstracts the debugger
9 reading memory.
10
11 If you run this file directly it will parse the passed in PE/COFF files
12 for debug info:
13 python3 ./efi_pefcoff.py DxeCore.efi
14 IA32`<path...>/DxeCore.dll load = 0x00000000
15 EntryPoint = 0x000030d2 TextAddress = 0x00000240 DataAddress = 0x000042c0
16 .text 0x00000240 (0x04080) flags:0x60000020
17 .data 0x000042C0 (0x001C0) flags:0xC0000040
18 .reloc 0x00004480 (0x00240) flags:0x42000040
19
20 Note: PeCoffClass uses virtual addresses and not file offsets.
21 It needs to work when images are loaded into memory.
22 as long as virtual address map to file addresses this
23 code can process binary files.
24
25 Note: This file can also contain generic worker functions (like GuidNames)
26 that abstract debugger agnostic services to the debugger.
27
28 This file should never import debugger specific modules.
29 '''
30
31 import sys
32 import os
33 import uuid
34 import struct
35 import re
36 from ctypes import c_char, c_uint8, c_uint16, c_uint32, c_uint64, c_void_p
37 from ctypes import ARRAY, sizeof
38 from ctypes import Structure, LittleEndianStructure
39
40 #
41 # The empty LittleEndianStructure must have _fields_ assigned prior to use or
42 # sizeof(). Anything that is size UINTN may need to get adjusted.
43 #
44 # The issue is ctypes matches our local machine, not the machine we are
45 # trying to debug. Call patch_ctypes() passing in the byte width from the
46 # debugger python to make sure you are in sync.
47 #
48 # Splitting out the _field_ from the Structure (LittleEndianStructure) class
49 # allows it to be patched.
50 #
51
52
53 class EFI_LOADED_IMAGE_PROTOCOL(LittleEndianStructure):
54 pass
55
56
57 EFI_LOADED_IMAGE_PROTOCOL_fields_ = [
58 ('Revision', c_uint32),
59 ('ParentHandle', c_void_p),
60 ('SystemTable', c_void_p),
61 ('DeviceHandle', c_void_p),
62 ('FilePath', c_void_p),
63 ('Reserved', c_void_p),
64 ('LoadOptionsSize', c_uint32),
65 ('LoadOptions', c_void_p),
66 ('ImageBase', c_void_p),
67 ('ImageSize', c_uint64),
68 ('ImageCodeType', c_uint32),
69 ('ImageDataType', c_uint32),
70 ('Unload', c_void_p),
71 ]
72
73
74 class EFI_GUID(LittleEndianStructure):
75 _fields_ = [
76 ('Data1', c_uint32),
77 ('Data2', c_uint16),
78 ('Data3', c_uint16),
79 ('Data4', ARRAY(c_uint8, 8))
80 ]
81
82
83 class EFI_SYSTEM_TABLE_POINTER(LittleEndianStructure):
84 _fields_ = [
85 ('Signature', c_uint64),
86 ('EfiSystemTableBase', c_uint64),
87 ('Crc32', c_uint32)
88 ]
89
90
91 class EFI_DEBUG_IMAGE_INFO_NORMAL(LittleEndianStructure):
92 pass
93
94
95 EFI_DEBUG_IMAGE_INFO_NORMAL_fields_ = [
96 ('ImageInfoType', c_uint32),
97 ('LoadedImageProtocolInstance', c_void_p),
98 ('ImageHandle', c_void_p)
99 ]
100
101
102 class EFI_DEBUG_IMAGE_INFO(LittleEndianStructure):
103 pass
104
105
106 EFI_DEBUG_IMAGE_INFO_fields_ = [
107 ('NormalImage', c_void_p),
108 ]
109
110
111 class EFI_DEBUG_IMAGE_INFO_TABLE_HEADER(LittleEndianStructure):
112 pass
113
114
115 EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_ = [
116 ('UpdateStatus', c_uint32),
117 ('TableSize', c_uint32),
118 ('EfiDebugImageInfoTable', c_void_p),
119 ]
120
121
122 class EFI_TABLE_HEADER(LittleEndianStructure):
123 _fields_ = [
124 ('Signature', c_uint64),
125 ('Revision', c_uint32),
126 ('HeaderSize', c_uint32),
127 ('CRC32', c_uint32),
128 ('Reserved', c_uint32),
129 ]
130
131
132 class EFI_CONFIGURATION_TABLE(LittleEndianStructure):
133 pass
134
135
136 EFI_CONFIGURATION_TABLE_fields_ = [
137 ('VendorGuid', EFI_GUID),
138 ('VendorTable', c_void_p)
139 ]
140
141
142 class EFI_SYSTEM_TABLE(LittleEndianStructure):
143 pass
144
145
146 EFI_SYSTEM_TABLE_fields_ = [
147 ('Hdr', EFI_TABLE_HEADER),
148 ('FirmwareVendor', c_void_p),
149 ('FirmwareRevision', c_uint32),
150 ('ConsoleInHandle', c_void_p),
151 ('ConIn', c_void_p),
152 ('ConsoleOutHandle', c_void_p),
153 ('ConOut', c_void_p),
154 ('StandardErrHandle', c_void_p),
155 ('StdErr', c_void_p),
156 ('RuntimeService', c_void_p),
157 ('BootService', c_void_p),
158 ('NumberOfTableEntries', c_void_p),
159 ('ConfigurationTable', c_void_p),
160 ]
161
162
163 class EFI_IMAGE_DATA_DIRECTORY(LittleEndianStructure):
164 _fields_ = [
165 ('VirtualAddress', c_uint32),
166 ('Size', c_uint32)
167 ]
168
169
170 class EFI_TE_IMAGE_HEADER(LittleEndianStructure):
171 _fields_ = [
172 ('Signature', ARRAY(c_char, 2)),
173 ('Machine', c_uint16),
174 ('NumberOfSections', c_uint8),
175 ('Subsystem', c_uint8),
176 ('StrippedSize', c_uint16),
177 ('AddressOfEntryPoint', c_uint32),
178 ('BaseOfCode', c_uint32),
179 ('ImageBase', c_uint64),
180 ('DataDirectoryBaseReloc', EFI_IMAGE_DATA_DIRECTORY),
181 ('DataDirectoryDebug', EFI_IMAGE_DATA_DIRECTORY)
182 ]
183
184
185 class EFI_IMAGE_DOS_HEADER(LittleEndianStructure):
186 _fields_ = [
187 ('e_magic', c_uint16),
188 ('e_cblp', c_uint16),
189 ('e_cp', c_uint16),
190 ('e_crlc', c_uint16),
191 ('e_cparhdr', c_uint16),
192 ('e_minalloc', c_uint16),
193 ('e_maxalloc', c_uint16),
194 ('e_ss', c_uint16),
195 ('e_sp', c_uint16),
196 ('e_csum', c_uint16),
197 ('e_ip', c_uint16),
198 ('e_cs', c_uint16),
199 ('e_lfarlc', c_uint16),
200 ('e_ovno', c_uint16),
201 ('e_res', ARRAY(c_uint16, 4)),
202 ('e_oemid', c_uint16),
203 ('e_oeminfo', c_uint16),
204 ('e_res2', ARRAY(c_uint16, 10)),
205 ('e_lfanew', c_uint16)
206 ]
207
208
209 class EFI_IMAGE_FILE_HEADER(LittleEndianStructure):
210 _fields_ = [
211 ('Machine', c_uint16),
212 ('NumberOfSections', c_uint16),
213 ('TimeDateStamp', c_uint32),
214 ('PointerToSymbolTable', c_uint32),
215 ('NumberOfSymbols', c_uint32),
216 ('SizeOfOptionalHeader', c_uint16),
217 ('Characteristics', c_uint16)
218 ]
219
220
221 class EFI_IMAGE_OPTIONAL_HEADER32(LittleEndianStructure):
222 _fields_ = [
223 ('Magic', c_uint16),
224 ('MajorLinkerVersion', c_uint8),
225 ('MinorLinkerVersion', c_uint8),
226 ('SizeOfCode', c_uint32),
227 ('SizeOfInitializedData', c_uint32),
228 ('SizeOfUninitializedData', c_uint32),
229 ('AddressOfEntryPoint', c_uint32),
230 ('BaseOfCode', c_uint32),
231 ('BaseOfData', c_uint32),
232 ('ImageBase', c_uint32),
233 ('SectionAlignment', c_uint32),
234 ('FileAlignment', c_uint32),
235 ('MajorOperatingSystemVersion', c_uint16),
236 ('MinorOperatingSystemVersion', c_uint16),
237 ('MajorImageVersion', c_uint16),
238 ('MinorImageVersion', c_uint16),
239 ('MajorSubsystemVersion', c_uint16),
240 ('MinorSubsystemVersion', c_uint16),
241 ('Win32VersionValue', c_uint32),
242 ('SizeOfImage', c_uint32),
243 ('SizeOfHeaders', c_uint32),
244 ('CheckSum', c_uint32),
245 ('Subsystem', c_uint16),
246 ('DllCharacteristics', c_uint16),
247 ('SizeOfStackReserve', c_uint32),
248 ('SizeOfStackCommit', c_uint32),
249 ('SizeOfHeapReserve', c_uint32),
250 ('SizeOfHeapCommit', c_uint32),
251 ('LoaderFlags', c_uint32),
252 ('NumberOfRvaAndSizes', c_uint32),
253 ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
254 ]
255
256
257 class EFI_IMAGE_NT_HEADERS32(LittleEndianStructure):
258 _fields_ = [
259 ('Signature', c_uint32),
260 ('FileHeader', EFI_IMAGE_FILE_HEADER),
261 ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER32)
262 ]
263
264
265 class EFI_IMAGE_OPTIONAL_HEADER64(LittleEndianStructure):
266 _fields_ = [
267 ('Magic', c_uint16),
268 ('MajorLinkerVersion', c_uint8),
269 ('MinorLinkerVersion', c_uint8),
270 ('SizeOfCode', c_uint32),
271 ('SizeOfInitializedData', c_uint32),
272 ('SizeOfUninitializedData', c_uint32),
273 ('AddressOfEntryPoint', c_uint32),
274 ('BaseOfCode', c_uint32),
275 ('BaseOfData', c_uint32),
276 ('ImageBase', c_uint32),
277 ('SectionAlignment', c_uint32),
278 ('FileAlignment', c_uint32),
279 ('MajorOperatingSystemVersion', c_uint16),
280 ('MinorOperatingSystemVersion', c_uint16),
281 ('MajorImageVersion', c_uint16),
282 ('MinorImageVersion', c_uint16),
283 ('MajorSubsystemVersion', c_uint16),
284 ('MinorSubsystemVersion', c_uint16),
285 ('Win32VersionValue', c_uint32),
286 ('SizeOfImage', c_uint32),
287 ('SizeOfHeaders', c_uint32),
288 ('CheckSum', c_uint32),
289 ('Subsystem', c_uint16),
290 ('DllCharacteristics', c_uint16),
291 ('SizeOfStackReserve', c_uint64),
292 ('SizeOfStackCommit', c_uint64),
293 ('SizeOfHeapReserve', c_uint64),
294 ('SizeOfHeapCommit', c_uint64),
295 ('LoaderFlags', c_uint32),
296 ('NumberOfRvaAndSizes', c_uint32),
297 ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
298 ]
299
300
301 class EFI_IMAGE_NT_HEADERS64(LittleEndianStructure):
302 _fields_ = [
303 ('Signature', c_uint32),
304 ('FileHeader', EFI_IMAGE_FILE_HEADER),
305 ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER64)
306 ]
307
308
309 class EFI_IMAGE_DEBUG_DIRECTORY_ENTRY(LittleEndianStructure):
310 _fields_ = [
311 ('Characteristics', c_uint32),
312 ('TimeDateStamp', c_uint32),
313 ('MajorVersion', c_uint16),
314 ('MinorVersion', c_uint16),
315 ('Type', c_uint32),
316 ('SizeOfData', c_uint32),
317 ('RVA', c_uint32),
318 ('FileOffset', c_uint32),
319 ]
320
321
322 class EFI_IMAGE_SECTION_HEADER(LittleEndianStructure):
323 _fields_ = [
324 ('Name', ARRAY(c_char, 8)),
325 ('VirtualSize', c_uint32),
326 ('VirtualAddress', c_uint32),
327 ('SizeOfRawData', c_uint32),
328 ('PointerToRawData', c_uint32),
329 ('PointerToRelocations', c_uint32),
330 ('PointerToLinenumbers', c_uint32),
331 ('NumberOfRelocations', c_uint16),
332 ('NumberOfLinenumbers', c_uint16),
333 ('Characteristics', c_uint32),
334 ]
335
336
337 EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
338 EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
339
340 DIRECTORY_DEBUG = 6
341
342
343 image_machine_dict = {
344 0x014c: "IA32",
345 0x0200: "IPF",
346 0x0EBC: "EBC",
347 0x8664: "X64",
348 0x01c2: "ARM",
349 0xAA64: "AArch64",
350 0x5032: "RISC32",
351 0x5064: "RISC64",
352 0x5128: "RISCV128",
353 }
354
355
356 def patch_void_p_to_ctype(patch_type, to_patch):
357 '''Optionally patch c_void_p in the Structure._fields_'''
358 if patch_type is None:
359 return to_patch
360
361 result = []
362 for name, c_type in to_patch:
363 if type(c_type) == type(c_void_p):
364 result.append((name, c_uint32))
365 else:
366 result.append((name, c_type))
367 return result
368
369
370 def patch_ctypes(pointer_width=8):
371 '''
372 Pass in the pointer width of the system being debugged. If it is not
373 the same as c_void_p then patch the _fields_ with the correct type.
374 For any ctypes Structure that has a c_void_p this function needs to be
375 called prior to use or sizeof() to initialize _fields_.
376 '''
377
378 if sizeof(c_void_p) == pointer_width:
379 patch_type = None
380 elif pointer_width == 16:
381 assert False
382 elif pointer_width == 8:
383 patch_type = c_uint64
384 elif pointer_width == 4:
385 patch_type = c_uint32
386 else:
387 raise Exception(f'ERROR: Unkown pointer_width = {pointer_width}')
388
389 # If you add a ctypes Structure class with a c_void_p you need to add
390 # it to this list. Note: you should use c_void_p for UINTN values.
391 EFI_LOADED_IMAGE_PROTOCOL._fields_ = patch_void_p_to_ctype(
392 patch_type, EFI_LOADED_IMAGE_PROTOCOL_fields_)
393 EFI_DEBUG_IMAGE_INFO_NORMAL._fields_ = patch_void_p_to_ctype(
394 patch_type, EFI_DEBUG_IMAGE_INFO_NORMAL_fields_)
395 EFI_DEBUG_IMAGE_INFO._fields_ = patch_void_p_to_ctype(
396 patch_type, EFI_DEBUG_IMAGE_INFO_fields_)
397 EFI_DEBUG_IMAGE_INFO_TABLE_HEADER._fields_ = patch_void_p_to_ctype(
398 patch_type, EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_)
399 EFI_CONFIGURATION_TABLE._fields_ = patch_void_p_to_ctype(
400 patch_type, EFI_CONFIGURATION_TABLE_fields_)
401 EFI_SYSTEM_TABLE._fields_ = patch_void_p_to_ctype(
402 patch_type, EFI_SYSTEM_TABLE_fields_)
403
404 # patch up anything else that needs to know pointer_width
405 EfiStatusClass(pointer_width)
406
407
408 def ctype_to_str(ctype, indent='', hide_list=[]):
409 '''
410 Given a ctype object print out as a string by walking the _fields_
411 in the cstring Class
412 '''
413 result = ''
414 for field in ctype._fields_:
415 attr = getattr(ctype, field[0])
416 tname = type(attr).__name__
417 if field[0] in hide_list:
418 continue
419
420 result += indent + f'{field[0]} = '
421 if tname == 'EFI_GUID':
422 result += GuidNames.to_name(GuidNames.to_uuid(attr)) + '\n'
423 elif issubclass(type(attr), Structure):
424 result += f'{tname}\n' + \
425 ctype_to_str(attr, indent + ' ', hide_list)
426 elif isinstance(attr, int):
427 result += f'0x{attr:x}\n'
428 else:
429 result += f'{attr}\n'
430
431 return result
432
433
434 def hexline(addr, data):
435 hexstr = ''
436 printable = ''
437 for i in range(0, len(data)):
438 hexstr += f'{data[i]:02x} '
439 printable += chr(data[i]) if data[i] > 0x20 and data[i] < 0x7f else '.'
440 return f'{addr:04x} {hexstr:48s} |{printable:s}|'
441
442
443 def hexdump(data, indent=''):
444 if not isinstance(data, bytearray):
445 data = bytearray(data)
446
447 result = ''
448 for i in range(0, len(data), 16):
449 result += indent + hexline(i, data[i:i+16]) + '\n'
450 return result
451
452
453 class EfiTpl:
454 ''' Return string for EFI_TPL'''
455
456 def __init__(self, tpl):
457 self.tpl = tpl
458
459 def __str__(self):
460 if self.tpl < 4:
461 result = f'{self.tpl:d}'
462 elif self.tpl < 8:
463 result = "TPL_APPLICATION"
464 if self.tpl - 4 > 0:
465 result += f' + {self.tpl - 4:d}'
466 elif self.tpl < 16:
467 result = "TPL_CALLBACK"
468 if self.tpl - 8 > 0:
469 result += f' + {self.tpl - 8:d}'
470 elif self.tpl < 31:
471 result = "TPL_NOTIFY"
472 if self.tpl - 16 > 0:
473 result += f' + {self.tpl - 16:d}'
474 elif self.tpl == 31:
475 result = "TPL_HIGH_LEVEL"
476 else:
477 result = f'Invalid TPL = {self.tpl:d}'
478 return result
479
480
481 class EfiBootMode:
482 '''
483 Class to return human readable string for EFI_BOOT_MODE
484
485 Methods
486 -----------
487 to_str(boot_mode, default)
488 return string for boot_mode, and return default if there is not a
489 match.
490 '''
491
492 EFI_BOOT_MODE_dict = {
493 0x00: "BOOT_WITH_FULL_CONFIGURATION",
494 0x01: "BOOT_WITH_MINIMAL_CONFIGURATION",
495 0x02: "BOOT_ASSUMING_NO_CONFIGURATION_CHANGES",
496 0x03: "BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS",
497 0x04: "BOOT_WITH_DEFAULT_SETTINGS",
498 0x05: "BOOT_ON_S4_RESUME",
499 0x06: "BOOT_ON_S5_RESUME",
500 0x07: "BOOT_WITH_MFG_MODE_SETTINGS",
501 0x10: "BOOT_ON_S2_RESUME",
502 0x11: "BOOT_ON_S3_RESUME",
503 0x12: "BOOT_ON_FLASH_UPDATE",
504 0x20: "BOOT_IN_RECOVERY_MODE",
505 }
506
507 def __init__(self, boot_mode):
508 self._boot_mode = boot_mode
509
510 def __str__(self):
511 return self.to_str(self._boot_mode)
512
513 @classmethod
514 def to_str(cls, boot_mode, default=''):
515 return cls.EFI_BOOT_MODE_dict.get(boot_mode, default)
516
517
518 class EfiStatusClass:
519 '''
520 Class to decode EFI_STATUS to a human readable string. You need to
521 pass in pointer_width to get the corret value since the EFI_STATUS
522 code values are different based on the sizeof UINTN. The default is
523 sizeof(UINTN) == 8.
524
525 Attributes
526 ??????
527 _dict_ : dictionary
528 dictionary of EFI_STATUS that has beed updated to match
529 pointer_width.
530
531 Methods
532 -----------
533 patch_dictionary(pointer_width)
534
535 to_str(status, default)
536 '''
537
538 _dict_ = {}
539 _EFI_STATUS_UINT32_dict = {
540 0: "Success",
541 1: "Warning Unknown Glyph",
542 2: "Warning Delete Failure",
543 3: "Warning Write Failure",
544 4: "Warning Buffer Too Small",
545 5: "Warning Stale Data",
546 6: "Warngin File System",
547 (0x20000000 | 0): "Warning interrupt source pending",
548 (0x20000000 | 1): "Warning interrupt source quiesced",
549
550 (0x80000000 | 1): "Load Error",
551 (0x80000000 | 2): "Invalid Parameter",
552 (0x80000000 | 3): "Unsupported",
553 (0x80000000 | 4): "Bad Buffer Size",
554 (0x80000000 | 5): "Buffer Too Small",
555 (0x80000000 | 6): "Not Ready",
556 (0x80000000 | 7): "Device Error",
557 (0x80000000 | 8): "Write Protected",
558 (0x80000000 | 9): "Out of Resources",
559 (0x80000000 | 10): "Volume Corrupt",
560 (0x80000000 | 11): "Volume Full",
561 (0x80000000 | 12): "No Media",
562 (0x80000000 | 13): "Media changed",
563 (0x80000000 | 14): "Not Found",
564 (0x80000000 | 15): "Access Denied",
565 (0x80000000 | 16): "No Response",
566 (0x80000000 | 17): "No mapping",
567 (0x80000000 | 18): "Time out",
568 (0x80000000 | 19): "Not started",
569 (0x80000000 | 20): "Already started",
570 (0x80000000 | 21): "Aborted",
571 (0x80000000 | 22): "ICMP Error",
572 (0x80000000 | 23): "TFTP Error",
573 (0x80000000 | 24): "Protocol Error",
574 (0x80000000 | 25): "Incompatible Version",
575 (0x80000000 | 26): "Security Violation",
576 (0x80000000 | 27): "CRC Error",
577 (0x80000000 | 28): "End of Media",
578 (0x80000000 | 31): "End of File",
579 (0x80000000 | 32): "Invalid Language",
580 (0x80000000 | 33): "Compromised Data",
581 (0x80000000 | 35): "HTTP Error",
582
583 (0xA0000000 | 0): "Interrupt Pending",
584 }
585
586 def __init__(self, status=None, pointer_width=8):
587 self.status = status
588 # this will convert to 64-bit version if needed
589 self.patch_dictionary(pointer_width)
590
591 def __str__(self):
592 return self.to_str(self.status)
593
594 @classmethod
595 def to_str(cls, status, default=''):
596 return cls._dict_.get(status, default)
597
598 @classmethod
599 def patch_dictionary(cls, pointer_width):
600 '''Patch UINTN upper bits like values '''
601
602 if cls._dict_:
603 # only patch the class variable once
604 return False
605
606 if pointer_width == 4:
607 cls._dict = cls._EFI_STATUS_UINT32_dict
608 elif pointer_width == 8:
609 for key, value in cls._EFI_STATUS_UINT32_dict.items():
610 mask = (key & 0xE0000000) << 32
611 new_key = (key & 0x1FFFFFFF) | mask
612 cls._dict_[new_key] = value
613 return True
614 else:
615 return False
616
617
618 class GuidNames:
619 '''
620 Class to expose the C names of EFI_GUID's. The _dict_ starts with
621 common EFI System Table entry EFI_GUID's. _dict_ can get updated with the
622 build generated Guid.xref file if a path to a module is passed
623 into add_build_guid_file(). If symbols are loaded for any module
624 in the build the path the build product should imply the
625 relative location of that builds Guid.xref file.
626
627 Attributes
628 ??????----
629 _dict_ : dictionary
630 dictionary of EFI_GUID (uuid) strings to C global names
631
632 Methods
633 -------
634 to_uuid(uuid)
635 convert a hex UUID string or bytearray to a uuid.UUID
636 to_name(uuid)
637 convert a UUID string to a C global constant name.
638 to_guid(guid_name)
639 convert a C global constant EFI_GUID name to uuid hex string.
640 is_guid_str(name)
641 name is a hex UUID string.
642 Example: 49152E77-1ADA-4764-B7A2-7AFEFED95E8B
643
644 to_c_guid(value)
645 convert a uuid.UUID or UUID string to a c_guid string
646 (see is_c_guid())
647 from_c_guid(value)
648 covert a C guid string to a hex UUID string.
649 is_c_guid(name)
650 name is the C initialization value for an EFI_GUID. Example:
651 { 0x414e6bdd, 0xe47b, 0x47cc, { 0xb2, 0x44, 0xbb, 0x61,
652 0x02, 0x0c, 0xf5, 0x16 }}
653
654 add_build_guid_file(module_path, custom_file):
655 assume module_path is an edk2 build product and load the Guid.xref
656 file from that build to fill in _dict_. If you know the path and
657 file name of a custom Guid.xref you can pass it in as custom_file.
658
659 '''
660 _dict_ = { # Common EFI System Table values
661 '05AD34BA-6F02-4214-952E-4DA0398E2BB9':
662 'gEfiDxeServicesTableGuid',
663 '7739F24C-93D7-11D4-9A3A-0090273FC14D':
664 'gEfiHobListGuid',
665 '4C19049F-4137-4DD3-9C10-8B97A83FFDFA':
666 'gEfiMemoryTypeInformationGuid',
667 '49152E77-1ADA-4764-B7A2-7AFEFED95E8B':
668 'gEfiDebugImageInfoTableGuid',
669 '060CC026-4C0D-4DDA-8F41-595FEF00A502':
670 'gMemoryStatusCodeRecordGuid',
671 'EB9D2D31-2D88-11D3-9A16-0090273FC14D':
672 'gEfiSmbiosTableGuid',
673 'EB9D2D30-2D88-11D3-9A16-0090273FC14D':
674 'gEfiAcpi10TableGuid',
675 '8868E871-E4F1-11D3-BC22-0080C73C8881':
676 'gEfiAcpi20TableGuid',
677 }
678
679 guid_files = []
680
681 def __init__(self, uuid=None, pointer_width=8):
682 self.uuid = None if uuid is None else self.to_uuid(uuid)
683
684 def __str__(self):
685 if self.uuid is None:
686 result = ''
687 for key, value in GuidNames._dict_.items():
688 result += f'{key}: {value}\n'
689 else:
690 result = self.to_name(self.uuid)
691
692 return result
693
694 @classmethod
695 def to_uuid(cls, obj):
696 try:
697 return uuid.UUID(bytes_le=bytes(obj))
698 except (ValueError, TypeError):
699 try:
700 return uuid.UUID(bytes_le=obj)
701 except (ValueError, TypeError):
702 return uuid.UUID(obj)
703
704 @classmethod
705 def to_name(cls, uuid):
706 if not isinstance(uuid, str):
707 uuid = str(uuid)
708 if cls.is_c_guid(uuid):
709 uuid = cls.from_c_guid(uuid)
710 return cls._dict_.get(uuid.upper(), uuid.upper())
711
712 @classmethod
713 def to_guid(cls, guid_name):
714 for key, value in cls._dict_.items():
715 if guid_name == value:
716 return key.upper()
717 else:
718 raise KeyError(key)
719
720 @classmethod
721 def is_guid_str(cls, name):
722 if not isinstance(name, str):
723 return False
724 return name.count('-') >= 4
725
726 @classmethod
727 def to_c_guid(cls, value):
728 if isinstance(value, uuid.UUID):
729 guid = value
730 else:
731 guid = uuid.UUID(value)
732
733 (data1, data2, data3,
734 data4_0, data4_1, data4_2, data4_3,
735 data4_4, data4_5, data4_6, data4_7) = struct.unpack(
736 '<IHH8B', guid.bytes_le)
737 return (f'{{ 0x{data1:08X}, 0x{data2:04X}, 0x{data3:04X}, '
738 f'{{ 0x{data4_0:02X}, 0x{data4_1:02X}, 0x{data4_2:02X}, '
739 f'0x{data4_3:02X}, 0x{data4_4:02X}, 0x{data4_5:02X}, '
740 f'0x{data4_6:02X}, 0x{data4_7:02X} }} }}')
741
742 @ classmethod
743 def from_c_guid(cls, value):
744 try:
745 hex = [int(x, 16) for x in re.findall(r"[\w']+", value)]
746 return (f'{hex[0]:08X}-{hex[1]:04X}-{hex[2]:04X}'
747 + f'-{hex[3]:02X}{hex[4]:02X}-{hex[5]:02X}{hex[6]:02X}'
748 + f'{hex[7]:02X}{hex[8]:02X}{hex[9]:02X}{hex[10]:02X}')
749 except ValueError:
750 return value
751
752 @ classmethod
753 def is_c_guid(cls, name):
754 if not isinstance(name, str):
755 return False
756 return name.count('{') == 2 and name.count('}') == 2
757
758 @ classmethod
759 def add_build_guid_file(cls, module_path, custom_file=None):
760 if custom_file is not None:
761 xref = custom_file
762 else:
763 # module_path will look like:
764 # <repo>/Build/OvmfX64/DEBUG_XCODE5/X64/../DxeCore.dll
765 # Walk backwards looking for a toolchain like name.
766 # Then look for GUID database:
767 # Build/OvmfX64//DEBUG_XCODE5/FV/Guid.xref
768 for i in reversed(module_path.split(os.sep)):
769 if (i.startswith('DEBUG_') or
770 i.startswith('RELEASE_') or
771 i.startswith('NOOPT_')):
772 build_root = os.path.join(
773 module_path.rsplit(i, 1)[0], i)
774 break
775
776 xref = os.path.join(build_root, 'FV', 'Guid.xref')
777
778 if xref in cls.guid_files:
779 # only processes the file one time
780 return True
781
782 with open(xref) as f:
783 content = f.readlines()
784 cls.guid_files.append(xref)
785
786 for lines in content:
787 try:
788 if cls.is_guid_str(lines):
789 # a regex would be more pedantic
790 words = lines.split()
791 cls._dict_[words[0].upper()] = words[1].strip('\n')
792 except ValueError:
793 pass
794
795 return True
796
797 return False
798
799
800 class EFI_HOB_GENERIC_HEADER(LittleEndianStructure):
801 _fields_ = [
802 ('HobType', c_uint16),
803 ('HobLength', c_uint16),
804 ('Reserved', c_uint32)
805 ]
806
807
808 class EFI_HOB_HANDOFF_INFO_TABLE(LittleEndianStructure):
809 _fields_ = [
810 ('Header', EFI_HOB_GENERIC_HEADER),
811 ('Version', c_uint32),
812 ('BootMode', c_uint32),
813 ('EfiMemoryTop', c_uint64),
814 ('EfiMemoryBottom', c_uint64),
815 ('EfiFreeMemoryTop', c_uint64),
816 ('EfiFreeMemoryBottom', c_uint64),
817 ('EfiEndOfHobList', c_uint64),
818 ]
819
820
821 class EFI_HOB_MEMORY_ALLOCATION(LittleEndianStructure):
822 _fields_ = [
823 ('Header', EFI_HOB_GENERIC_HEADER),
824 ('Name', EFI_GUID),
825 ('MemoryBaseAddress', c_uint64),
826 ('MemoryLength', c_uint64),
827 ('MemoryType', c_uint32),
828 ('Reserved', c_uint32),
829 ]
830
831
832 class EFI_HOB_RESOURCE_DESCRIPTOR(LittleEndianStructure):
833 _fields_ = [
834 ('Header', EFI_HOB_GENERIC_HEADER),
835 ('Owner', EFI_GUID),
836 ('ResourceType', c_uint32),
837 ('ResourceAttribute', c_uint32),
838 ('PhysicalStart', c_uint64),
839 ('ResourceLength', c_uint64),
840 ]
841
842
843 class EFI_HOB_GUID_TYPE(LittleEndianStructure):
844 _fields_ = [
845 ('Header', EFI_HOB_GENERIC_HEADER),
846 ('Name', EFI_GUID),
847 ]
848
849
850 class EFI_HOB_FIRMWARE_VOLUME(LittleEndianStructure):
851 _fields_ = [
852 ('Header', EFI_HOB_GENERIC_HEADER),
853 ('BaseAddress', c_uint64),
854 ('Length', c_uint64),
855 ]
856
857
858 class EFI_HOB_CPU(LittleEndianStructure):
859 _fields_ = [
860 ('Header', EFI_HOB_GENERIC_HEADER),
861 ('SizeOfMemorySpace', c_uint8),
862 ('SizeOfIoSpace', c_uint8),
863 ('Reserved', ARRAY(c_uint8, 6)),
864 ]
865
866
867 class EFI_HOB_MEMORY_POOL(LittleEndianStructure):
868 _fields_ = [
869 ('Header', EFI_HOB_GENERIC_HEADER),
870 ]
871
872
873 class EFI_HOB_FIRMWARE_VOLUME2(LittleEndianStructure):
874 _fields_ = [
875 ('Header', EFI_HOB_GENERIC_HEADER),
876 ('BaseAddress', c_uint64),
877 ('Length', c_uint64),
878 ('FvName', EFI_GUID),
879 ('FileName', EFI_GUID)
880 ]
881
882
883 class EFI_HOB_FIRMWARE_VOLUME3(LittleEndianStructure):
884 _fields_ = [
885 ('HobType', c_uint16),
886 ('HobLength', c_uint16),
887 ('Reserved', c_uint32),
888 ('BaseAddress', c_uint64),
889 ('Length', c_uint64),
890 ('AuthenticationStatus', c_uint32),
891 ('ExtractedFv', c_uint8),
892 ('FvName', EFI_GUID),
893 ('FileName', EFI_GUID),
894 ]
895
896
897 class EFI_HOB_UEFI_CAPSULE(LittleEndianStructure):
898 _fields_ = [
899 ('HobType', c_uint16),
900 ('HobLength', c_uint16),
901 ('Reserved', c_uint32),
902 ('BaseAddress', c_uint64),
903 ('Length', c_uint64),
904 ]
905
906
907 class EfiHob:
908 '''
909 Parse EFI Device Paths based on the edk2 C Structures defined above.
910 In the context of this class verbose means hexdump extra data.
911
912
913 Attributes
914 ??????
915 Hob : list
916 List of HOBs. Each entry contains the name, HOB type, HOB length,
917 the ctype struct for the HOB, and any extra data.
918
919 Methods
920 -----------
921 get_hob_by_type(hob_type)
922 return string that decodes the HOBs of hob_type. If hob_type is
923 None then return all HOBs.
924 '''
925
926 Hob = []
927 verbose = False
928
929 hob_dict = {
930 1: EFI_HOB_HANDOFF_INFO_TABLE,
931 2: EFI_HOB_MEMORY_ALLOCATION,
932 3: EFI_HOB_RESOURCE_DESCRIPTOR,
933 4: EFI_HOB_GUID_TYPE,
934 5: EFI_HOB_FIRMWARE_VOLUME,
935 6: EFI_HOB_CPU,
936 7: EFI_HOB_MEMORY_POOL,
937 9: EFI_HOB_FIRMWARE_VOLUME2,
938 0xb: EFI_HOB_UEFI_CAPSULE,
939 0xc: EFI_HOB_FIRMWARE_VOLUME3,
940 0xffff: EFI_HOB_GENERIC_HEADER,
941 }
942
943 def __init__(self, file, address=None, verbose=False, count=1000):
944 self._file = file
945 EfiHob.verbose = verbose
946
947 if len(EfiHob.Hob) != 0 and address is None:
948 return
949
950 if address is not None:
951 hob_ptr = address
952 else:
953 hob_ptr = EfiConfigurationTable(file).GetConfigTable(
954 '7739F24C-93D7-11D4-9A3A-0090273FC14D')
955
956 self.read_hobs(hob_ptr)
957
958 @ classmethod
959 def __str__(cls):
960 return cls.get_hob_by_type(None)
961
962 @ classmethod
963 def get_hob_by_type(cls, hob_type):
964 result = ""
965 for (Name, HobType, HobLen, chob, extra) in cls.Hob:
966 if hob_type is not None:
967 if hob_type != HobType:
968 continue
969
970 result += f'Type: {Name:s} (0x{HobType:01x}) Len: 0x{HobLen:03x}\n'
971 result += ctype_to_str(chob, ' ', ['Reserved'])
972 if cls.verbose:
973 if extra is not None:
974 result += hexdump(extra, ' ')
975
976 return result
977
978 def read_hobs(self, hob_ptr, count=1000):
979 if hob_ptr is None:
980 return
981
982 try:
983 for _ in range(count): # while True
984 hdr, _ = self._ctype_read_ex(EFI_HOB_GENERIC_HEADER, hob_ptr)
985 if hdr.HobType == 0xffff:
986 break
987
988 type_str = self.hob_dict.get(
989 hdr.HobType, EFI_HOB_GENERIC_HEADER)
990 hob, extra = self._ctype_read_ex(
991 type_str, hob_ptr, hdr.HobLength)
992 EfiHob.Hob.append(
993 (type(hob).__name__,
994 hdr.HobType,
995 hdr.HobLength,
996 hob,
997 extra))
998 hob_ptr += hdr.HobLength
999 except ValueError:
1000 pass
1001
1002 def _ctype_read_ex(self, ctype_struct, offset=0, rsize=None):
1003 if offset != 0:
1004 self._file.seek(offset)
1005
1006 type_size = sizeof(ctype_struct)
1007 size = rsize if rsize else type_size
1008 data = self._file.read(size)
1009 cdata = ctype_struct.from_buffer(bytearray(data))
1010
1011 if size > type_size:
1012 return cdata, data[type_size:]
1013 else:
1014 return cdata, None
1015
1016
1017 class EFI_DEVICE_PATH(LittleEndianStructure):
1018 _pack_ = 1
1019 _fields_ = [
1020 ('Type', c_uint8),
1021 ('SubType', c_uint8),
1022
1023 # UINT8 Length[2]
1024 # Cheat and use c_uint16 since we don't care about alignment
1025 ('Length', c_uint16)
1026 ]
1027
1028
1029 class PCI_DEVICE_PATH(LittleEndianStructure):
1030 _pack_ = 1
1031 _fields_ = [
1032 ('Header', EFI_DEVICE_PATH),
1033 ('Function', c_uint8),
1034 ('Device', c_uint8)
1035 ]
1036
1037
1038 class PCCARD_DEVICE_PATH(LittleEndianStructure):
1039 _pack_ = 1
1040 _fields_ = [
1041 ('Header', EFI_DEVICE_PATH),
1042 ('FunctionNumber', c_uint8),
1043 ]
1044
1045
1046 class MEMMAP_DEVICE_PATH(LittleEndianStructure):
1047 _pack_ = 1
1048 _fields_ = [
1049 ('Header', EFI_DEVICE_PATH),
1050 ('StartingAddress', c_uint64),
1051 ('EndingAddress', c_uint64),
1052 ]
1053
1054
1055 class VENDOR_DEVICE_PATH(LittleEndianStructure):
1056 _pack_ = 1
1057 _fields_ = [
1058 ('Header', EFI_DEVICE_PATH),
1059 ('Guid', EFI_GUID),
1060 ]
1061
1062
1063 class CONTROLLER_DEVICE_PATH(LittleEndianStructure):
1064 _pack_ = 1
1065 _fields_ = [
1066 ('Header', EFI_DEVICE_PATH),
1067 ('ControllerNumber', c_uint32),
1068 ]
1069
1070
1071 class BMC_DEVICE_PATH(LittleEndianStructure):
1072 _pack_ = 1
1073 _fields_ = [
1074 ('Header', EFI_DEVICE_PATH),
1075 ('InterfaceType', c_uint8),
1076 ('BaseAddress', ARRAY(c_uint8, 8)),
1077 ]
1078
1079
1080 class BBS_BBS_DEVICE_PATH(LittleEndianStructure):
1081 _pack_ = 1
1082 _fields_ = [
1083 ('Header', EFI_DEVICE_PATH),
1084 ('DeviceType', c_uint16),
1085 ('StatusFlag', c_uint16)
1086 ]
1087
1088
1089 class ACPI_HID_DEVICE_PATH(LittleEndianStructure):
1090 _pack_ = 1
1091 _fields_ = [
1092 ('Header', EFI_DEVICE_PATH),
1093 ('HID', c_uint32),
1094 ('UID', c_uint32)
1095 ]
1096
1097
1098 class ACPI_EXTENDED_HID_DEVICE_PATH(LittleEndianStructure):
1099 _pack_ = 1
1100 _fields_ = [
1101 ('Header', EFI_DEVICE_PATH),
1102 ('HID', c_uint32),
1103 ('UID', c_uint32),
1104 ('CID', c_uint32)
1105 ]
1106
1107
1108 class ACPI_ADR_DEVICE_PATH(LittleEndianStructure):
1109 _pack_ = 1
1110 _fields_ = [
1111 ('Header', EFI_DEVICE_PATH),
1112 ('ARD', c_uint32)
1113 ]
1114
1115
1116 class ACPI_NVDIMM_DEVICE_PATH(LittleEndianStructure):
1117 _pack_ = 1
1118 _fields_ = [
1119 ('Header', EFI_DEVICE_PATH),
1120 ('NFITDeviceHandle', c_uint32)
1121 ]
1122
1123
1124 class ATAPI_DEVICE_PATH(LittleEndianStructure):
1125 _pack_ = 1
1126 _fields_ = [
1127 ('Header', EFI_DEVICE_PATH),
1128 ("PrimarySecondary", c_uint8),
1129 ("SlaveMaster", c_uint8),
1130 ("Lun", c_uint16)
1131 ]
1132
1133
1134 class SCSI_DEVICE_PATH(LittleEndianStructure):
1135 _pack_ = 1
1136 _fields_ = [
1137 ('Header', EFI_DEVICE_PATH),
1138 ("Pun", c_uint16),
1139 ("Lun", c_uint16)
1140 ]
1141
1142
1143 class FIBRECHANNEL_DEVICE_PATH(LittleEndianStructure):
1144 _pack_ = 1
1145 _fields_ = [
1146 ('Header', EFI_DEVICE_PATH),
1147 ("Reserved", c_uint32),
1148 ("WWN", c_uint64),
1149 ("Lun", c_uint64)
1150 ]
1151
1152
1153 class F1394_DEVICE_PATH(LittleEndianStructure):
1154 _pack_ = 1
1155 _fields_ = [
1156 ('Header', EFI_DEVICE_PATH),
1157 ("Reserved", c_uint32),
1158 ("Guid", c_uint64)
1159 ]
1160
1161
1162 class USB_DEVICE_PATH(LittleEndianStructure):
1163 _pack_ = 1
1164 _fields_ = [
1165 ('Header', EFI_DEVICE_PATH),
1166 ("ParentPortNumber", c_uint8),
1167 ("InterfaceNumber", c_uint8),
1168 ]
1169
1170
1171 class I2O_DEVICE_PATH(LittleEndianStructure):
1172 _pack_ = 1
1173 _fields_ = [
1174 ('Header', EFI_DEVICE_PATH),
1175 ("Tid", c_uint32)
1176 ]
1177
1178
1179 class INFINIBAND_DEVICE_PATH(LittleEndianStructure):
1180 _pack_ = 1
1181 _fields_ = [
1182 ('Header', EFI_DEVICE_PATH),
1183 ("ResourceFlags", c_uint32),
1184 ("PortGid", ARRAY(c_uint8, 16)),
1185 ("ServiceId", c_uint64),
1186 ("TargetPortId", c_uint64),
1187 ("DeviceId", c_uint64)
1188 ]
1189
1190
1191 class UART_FLOW_CONTROL_DEVICE_PATH(LittleEndianStructure):
1192 _pack_ = 1
1193 _fields_ = [
1194 ('Header', EFI_DEVICE_PATH),
1195 ("Guid", EFI_GUID),
1196 ("FlowControlMap", c_uint32)
1197 ]
1198
1199
1200 class SAS_DEVICE_PATH(LittleEndianStructure):
1201 _pack_ = 1
1202 _fields_ = [
1203 ('Header', EFI_DEVICE_PATH),
1204 ("Guid", EFI_GUID),
1205 ("Reserved", c_uint32),
1206 ("SasAddress", c_uint64),
1207 ("Lun", c_uint64),
1208 ("DeviceTopology", c_uint16),
1209 ("RelativeTargetPort", c_uint16)
1210 ]
1211
1212
1213 class EFI_MAC_ADDRESS(LittleEndianStructure):
1214 _pack_ = 1
1215 _fields_ = [
1216 ("Addr", ARRAY(c_uint8, 32)),
1217 ]
1218
1219
1220 class MAC_ADDR_DEVICE_PATH(LittleEndianStructure):
1221 _pack_ = 1
1222 _fields_ = [
1223 ('Header', EFI_DEVICE_PATH),
1224 ('MacAddress', EFI_MAC_ADDRESS),
1225 ('IfType', c_uint8)
1226 ]
1227
1228
1229 class IPv4_ADDRESS(LittleEndianStructure):
1230 _fields_ = [
1231 ("Addr", ARRAY(c_uint8, 4)),
1232 ]
1233
1234
1235 class IPv4_DEVICE_PATH(LittleEndianStructure):
1236 _pack_ = 1
1237 _fields_ = [
1238 ('Header', EFI_DEVICE_PATH),
1239 ('LocalIpAddress', IPv4_ADDRESS),
1240 ('RemoteIpAddress', IPv4_ADDRESS),
1241 ('LocalPort', c_uint16),
1242 ('RemotePort', c_uint16),
1243 ('Protocol', c_uint16),
1244 ('StaticIpAddress', c_uint8),
1245 ('GatewayIpAddress', IPv4_ADDRESS),
1246 ('SubnetMask', IPv4_ADDRESS)
1247 ]
1248
1249
1250 class IPv6_ADDRESS(LittleEndianStructure):
1251 _fields_ = [
1252 ("Addr", ARRAY(c_uint8, 16)),
1253 ]
1254
1255
1256 class IPv6_DEVICE_PATH(LittleEndianStructure):
1257 _pack_ = 1
1258 _fields_ = [
1259 ('Header', EFI_DEVICE_PATH),
1260 ('LocalIpAddress', IPv6_ADDRESS),
1261 ('RemoteIpAddress', IPv6_ADDRESS),
1262 ('LocalPort', c_uint16),
1263 ('RemotePort', c_uint16),
1264 ('Protocol', c_uint16),
1265 ('IpAddressOrigin', c_uint8),
1266 ('PrefixLength', c_uint8),
1267 ('GatewayIpAddress', IPv6_ADDRESS)
1268 ]
1269
1270
1271 class UART_DEVICE_PATH(LittleEndianStructure):
1272 _pack_ = 1
1273 _fields_ = [
1274 ('Header', EFI_DEVICE_PATH),
1275 ('Reserved', c_uint32),
1276 ('BaudRate', c_uint64),
1277 ('DataBits', c_uint8),
1278 ('Parity', c_uint8),
1279 ('StopBits', c_uint8)
1280 ]
1281
1282
1283 class USB_CLASS_DEVICE_PATH(LittleEndianStructure):
1284 _pack_ = 1
1285 _fields_ = [
1286 ('Header', EFI_DEVICE_PATH),
1287 ('VendorId', c_uint16),
1288 ('ProductId', c_uint16),
1289 ('DeviceClass', c_uint8),
1290 ('DeviceCSjblass', c_uint8),
1291 ('DeviceProtocol', c_uint8),
1292 ]
1293
1294
1295 class USB_WWID_DEVICE_PATH(LittleEndianStructure):
1296 _pack_ = 1
1297 _fields_ = [
1298 ('Header', EFI_DEVICE_PATH),
1299 ('InterfaceNumber', c_uint16),
1300 ('VendorId', c_uint16),
1301 ('ProductId', c_uint16),
1302 ]
1303
1304
1305 class DEVICE_LOGICAL_UNIT_DEVICE_PATH(LittleEndianStructure):
1306 _pack_ = 1
1307 _fields_ = [
1308 ('Header', EFI_DEVICE_PATH),
1309 ('Lun', c_uint8)
1310 ]
1311
1312
1313 class SATA_DEVICE_PATH(LittleEndianStructure):
1314 _pack_ = 1
1315 _fields_ = [
1316 ('Header', EFI_DEVICE_PATH),
1317 ('HBAPortNumber', c_uint16),
1318 ('PortMultiplierPortNumber', c_uint16),
1319 ('Lun', c_uint16),
1320 ]
1321
1322
1323 class ISCSI_DEVICE_PATH(LittleEndianStructure):
1324 _pack_ = 1
1325 _fields_ = [
1326 ('Header', EFI_DEVICE_PATH),
1327 ('NetworkProtocol', c_uint16),
1328 ('LoginOption', c_uint16),
1329 ('Lun', c_uint64),
1330 ('TargetPortalGroupTag', c_uint16),
1331 ]
1332
1333
1334 class VLAN_DEVICE_PATH(LittleEndianStructure):
1335 _pack_ = 1
1336 _fields_ = [
1337 ('Header', EFI_DEVICE_PATH),
1338 ("VlandId", c_uint16)
1339 ]
1340
1341
1342 class FIBRECHANNELEX_DEVICE_PATH(LittleEndianStructure):
1343 _pack_ = 1
1344 _fields_ = [
1345 ('Header', EFI_DEVICE_PATH),
1346 ("Reserved", c_uint16),
1347 ("WWN", ARRAY(c_uint8, 8)),
1348 ("Lun", ARRAY(c_uint8, 8)),
1349 ]
1350
1351
1352 class SASEX_DEVICE_PATH(LittleEndianStructure):
1353 _pack_ = 1
1354 _fields_ = [
1355 ('Header', EFI_DEVICE_PATH),
1356 ("SasAddress", ARRAY(c_uint8, 8)),
1357 ("Lun", ARRAY(c_uint8, 8)),
1358 ("DeviceTopology", c_uint16),
1359 ("RelativeTargetPort", c_uint16)
1360 ]
1361
1362
1363 class NVME_NAMESPACE_DEVICE_PATH(LittleEndianStructure):
1364 _pack_ = 1
1365 _fields_ = [
1366 ('Header', EFI_DEVICE_PATH),
1367 ("NamespaceId", c_uint32),
1368 ("NamespaceUuid", c_uint64)
1369 ]
1370
1371
1372 class DNS_DEVICE_PATH(LittleEndianStructure):
1373 _pack_ = 1
1374 _fields_ = [
1375 ('Header', EFI_DEVICE_PATH),
1376 ("IsIPv6", c_uint8),
1377 ("DnsServerIp", IPv6_ADDRESS)
1378
1379 ]
1380
1381
1382 class UFS_DEVICE_PATH(LittleEndianStructure):
1383 _pack_ = 1
1384 _fields_ = [
1385 ('Header', EFI_DEVICE_PATH),
1386 ("Pun", c_uint8),
1387 ("Lun", c_uint8),
1388 ]
1389
1390
1391 class SD_DEVICE_PATH(LittleEndianStructure):
1392 _pack_ = 1
1393 _fields_ = [
1394 ('Header', EFI_DEVICE_PATH),
1395 ("SlotNumber", c_uint8)
1396 ]
1397
1398
1399 class BLUETOOTH_ADDRESS(LittleEndianStructure):
1400 _pack_ = 1
1401 _fields_ = [
1402 ("Address", ARRAY(c_uint8, 6))
1403 ]
1404
1405
1406 class BLUETOOTH_LE_ADDRESS(LittleEndianStructure):
1407 _pack_ = 1
1408 _fields_ = [
1409 ("Format", c_uint8),
1410 ("Class", c_uint16)
1411 ]
1412
1413
1414 class BLUETOOTH_DEVICE_PATH(LittleEndianStructure):
1415 _pack_ = 1
1416 _fields_ = [
1417 ('Header', EFI_DEVICE_PATH),
1418 ("BD_ADDR", BLUETOOTH_ADDRESS)
1419 ]
1420
1421
1422 class WIFI_DEVICE_PATH(LittleEndianStructure):
1423 _pack_ = 1
1424 _fields_ = [
1425 ('Header', EFI_DEVICE_PATH),
1426 ("SSId", ARRAY(c_uint8, 32))
1427 ]
1428
1429
1430 class EMMC_DEVICE_PATH(LittleEndianStructure):
1431 _pack_ = 1
1432 _fields_ = [
1433 ('Header', EFI_DEVICE_PATH),
1434 ("SlotNumber", c_uint8)
1435 ]
1436
1437
1438 class BLUETOOTH_LE_DEVICE_PATH(LittleEndianStructure):
1439 _pack_ = 1
1440 _fields_ = [
1441 ('Header', EFI_DEVICE_PATH),
1442 ("BD_ADDR", BLUETOOTH_LE_ADDRESS)
1443 ]
1444
1445
1446 class NVDIMM_NAMESPACE_DEVICE_PATH(LittleEndianStructure):
1447 _pack_ = 1
1448 _fields_ = [
1449 ('Header', EFI_DEVICE_PATH),
1450 ("Uuid", EFI_GUID)
1451 ]
1452
1453
1454 class REST_SERVICE_DEVICE_PATH(LittleEndianStructure):
1455 _pack_ = 1
1456 _fields_ = [
1457 ('Header', EFI_DEVICE_PATH),
1458 ("RESTService", c_uint8),
1459 ("AccessMode", c_uint8)
1460 ]
1461
1462
1463 class REST_VENDOR_SERVICE_DEVICE_PATH(LittleEndianStructure):
1464 _pack_ = 1
1465 _fields_ = [
1466 ('Header', EFI_DEVICE_PATH),
1467 ("RESTService", c_uint8),
1468 ("AccessMode", c_uint8),
1469 ("Guid", EFI_GUID),
1470 ]
1471
1472
1473 class HARDDRIVE_DEVICE_PATH(LittleEndianStructure):
1474 _pack_ = 1
1475 _fields_ = [
1476 ('Header', EFI_DEVICE_PATH),
1477 ('PartitionNumber', c_uint32),
1478 ('PartitionStart', c_uint64),
1479 ('PartitionSize', c_uint64),
1480 ('Signature', ARRAY(c_uint8, 16)),
1481 ('MBRType', c_uint8),
1482 ('SignatureType', c_uint8)
1483 ]
1484
1485
1486 class CDROM_DEVICE_PATH(LittleEndianStructure):
1487 _pack_ = 1
1488 _fields_ = [
1489 ('Header', EFI_DEVICE_PATH),
1490 ('BootEntry', c_uint32),
1491 ('PartitionStart', c_uint64),
1492 ('PartitionSize', c_uint64)
1493 ]
1494
1495
1496 class MEDIA_PROTOCOL_DEVICE_PATH(LittleEndianStructure):
1497 _pack_ = 1
1498 _fields_ = [
1499 ('Header', EFI_DEVICE_PATH),
1500 ('Protocol', EFI_GUID)
1501 ]
1502
1503
1504 class MEDIA_FW_VOL_FILEPATH_DEVICE_PATH(LittleEndianStructure):
1505 _pack_ = 1
1506 _fields_ = [
1507 ('Header', EFI_DEVICE_PATH),
1508 ('FvFileName', EFI_GUID)
1509 ]
1510
1511
1512 class MEDIA_FW_VOL_DEVICE_PATH(LittleEndianStructure):
1513 _pack_ = 1
1514 _fields_ = [
1515 ('Header', EFI_DEVICE_PATH),
1516 ('FvName', EFI_GUID)
1517 ]
1518
1519
1520 class MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH(LittleEndianStructure):
1521 _pack_ = 1
1522 _fields_ = [
1523 ('Header', EFI_DEVICE_PATH),
1524 ('Reserved', c_uint32),
1525 ('StartingOffset', c_uint64),
1526 ('EndingOffset', c_uint64)
1527 ]
1528
1529
1530 class MEDIA_RAM_DISK_DEVICE_PATH(LittleEndianStructure):
1531 _pack_ = 1
1532 _fields_ = [
1533 ('Header', EFI_DEVICE_PATH),
1534 ('StartingAddr', c_uint64),
1535 ('EndingAddr', c_uint64),
1536 ('TypeGuid', EFI_GUID),
1537 ('Instance', c_uint16)
1538 ]
1539
1540
1541 class EfiDevicePath:
1542 '''
1543 Parse EFI Device Paths based on the edk2 C Structures defined above.
1544 In the context of this class verbose means hexdump extra data.
1545
1546
1547 Attributes
1548 ??????
1549 DevicePath : list
1550 List of devixe path instances. Each instance is a list of nodes
1551 for the given Device Path instance.
1552
1553 Methods
1554 -----------
1555 device_path_node(address)
1556 return the Device Path ctype hdr, ctype, and any extra data in
1557 the Device Path node. This is just a single Device Path node,
1558 not the entire Device Path.
1559 device_path_node_str(address)
1560 return the device path node (not the entire Device Path) as a string
1561 '''
1562
1563 DevicePath = []
1564
1565 device_path_dict = {
1566 # ( Type, SubType ) : Device Path C typedef
1567 # HARDWARE_DEVICE_PATH
1568 (1, 1): PCI_DEVICE_PATH,
1569 (1, 2): PCCARD_DEVICE_PATH,
1570 (1, 3): MEMMAP_DEVICE_PATH,
1571 (1, 4): VENDOR_DEVICE_PATH,
1572 (1, 5): CONTROLLER_DEVICE_PATH,
1573 (1, 6): BMC_DEVICE_PATH,
1574
1575 # ACPI_DEVICE_PATH
1576 (2, 1): ACPI_HID_DEVICE_PATH,
1577 (2, 2): ACPI_EXTENDED_HID_DEVICE_PATH,
1578 (2, 3): ACPI_ADR_DEVICE_PATH,
1579 (2, 4): ACPI_NVDIMM_DEVICE_PATH,
1580
1581 # MESSAGING_DEVICE_PATH
1582 (3, 1): ATAPI_DEVICE_PATH,
1583 (3, 2): SCSI_DEVICE_PATH,
1584 (3, 3): FIBRECHANNEL_DEVICE_PATH,
1585 (3, 4): F1394_DEVICE_PATH,
1586 (3, 5): USB_DEVICE_PATH,
1587 (3, 6): I2O_DEVICE_PATH,
1588
1589 (3, 9): INFINIBAND_DEVICE_PATH,
1590 (3, 10): VENDOR_DEVICE_PATH,
1591 (3, 11): MAC_ADDR_DEVICE_PATH,
1592 (3, 12): IPv4_DEVICE_PATH,
1593 (3, 13): IPv6_DEVICE_PATH,
1594 (3, 14): UART_DEVICE_PATH,
1595 (3, 15): USB_CLASS_DEVICE_PATH,
1596 (3, 16): USB_WWID_DEVICE_PATH,
1597 (3, 17): DEVICE_LOGICAL_UNIT_DEVICE_PATH,
1598 (3, 18): SATA_DEVICE_PATH,
1599 (3, 19): ISCSI_DEVICE_PATH,
1600 (3, 20): VLAN_DEVICE_PATH,
1601 (3, 21): FIBRECHANNELEX_DEVICE_PATH,
1602 (3, 22): SASEX_DEVICE_PATH,
1603 (3, 23): NVME_NAMESPACE_DEVICE_PATH,
1604 (3, 24): DNS_DEVICE_PATH,
1605 (3, 25): UFS_DEVICE_PATH,
1606 (3, 26): SD_DEVICE_PATH,
1607 (3, 27): BLUETOOTH_DEVICE_PATH,
1608 (3, 28): WIFI_DEVICE_PATH,
1609 (3, 29): EMMC_DEVICE_PATH,
1610 (3, 30): BLUETOOTH_LE_DEVICE_PATH,
1611 (3, 31): DNS_DEVICE_PATH,
1612 (3, 32): NVDIMM_NAMESPACE_DEVICE_PATH,
1613
1614 (3, 33): REST_SERVICE_DEVICE_PATH,
1615 (3, 34): REST_VENDOR_SERVICE_DEVICE_PATH,
1616
1617 # MEDIA_DEVICE_PATH
1618 (4, 1): HARDDRIVE_DEVICE_PATH,
1619 (4, 2): CDROM_DEVICE_PATH,
1620 (4, 3): VENDOR_DEVICE_PATH,
1621 (4, 4): EFI_DEVICE_PATH,
1622 (4, 5): MEDIA_PROTOCOL_DEVICE_PATH,
1623 (4, 6): MEDIA_FW_VOL_FILEPATH_DEVICE_PATH,
1624 (4, 7): MEDIA_FW_VOL_DEVICE_PATH,
1625 (4, 8): MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH,
1626 (4, 9): MEDIA_RAM_DISK_DEVICE_PATH,
1627
1628 # BBS_DEVICE_PATH
1629 (5, 1): BBS_BBS_DEVICE_PATH,
1630
1631 }
1632
1633 guid_override_dict = {
1634 uuid.UUID('37499A9D-542F-4C89-A026-35DA142094E4'):
1635 UART_FLOW_CONTROL_DEVICE_PATH,
1636 uuid.UUID('D487DDB4-008B-11D9-AFDC-001083FFCA4D'):
1637 SAS_DEVICE_PATH,
1638 }
1639
1640 def __init__(self, file, ptr=None, verbose=False, count=64):
1641 '''
1642 Convert ptr into a list of Device Path nodes. If verbose also hexdump
1643 extra data.
1644 '''
1645 self._file = file
1646 self._verbose = verbose
1647 if ptr is None:
1648 return
1649
1650 try:
1651 instance = []
1652 for _ in range(count): # while True
1653 hdr, _ = self._ctype_read_ex(EFI_DEVICE_PATH, ptr)
1654 if hdr.Length < sizeof(EFI_DEVICE_PATH):
1655 # Not a valid device path
1656 break
1657
1658 if hdr.Type == 0x7F: # END_DEVICE_PATH_TYPE
1659 self.DevicePath.append(instance)
1660 if hdr.SubType == 0xFF: # END_ENTIRE_DEVICE_PATH_SUBTYPE
1661 break
1662 if hdr.SubType == 0x01: # END_INSTANCE_DEVICE_PATH_SUBTYPE
1663 # start new device path instance
1664 instance = []
1665
1666 type_str = self.device_path_dict.get(
1667 (hdr.Type, hdr.SubType), EFI_DEVICE_PATH)
1668 node, extra = self._ctype_read_ex(type_str, ptr, hdr.Length)
1669 if 'VENDOR_DEVICE_PATH' in type(node).__name__:
1670 guid_type = self.guid_override_dict.get(
1671 GuidNames.to_uuid(node.Guid), None)
1672 if guid_type:
1673 # use the ctype associated with the GUID
1674 node, extra = self._ctype_read_ex(
1675 guid_type, ptr, hdr.Length)
1676
1677 instance.append((type(node).__name__, hdr.Type,
1678 hdr.SubType, hdr.Length, node, extra))
1679 ptr += hdr.Length
1680 except ValueError:
1681 pass
1682
1683 def __str__(self):
1684 ''' '''
1685 if not self.valid():
1686 return '<class: EfiDevicePath>'
1687
1688 result = ""
1689 for instance in self.DevicePath:
1690 for (Name, Type, SubType, Length, cnode, extra) in instance:
1691 result += f'{Name:s} {Type:2d}:{SubType:2d} Len: {Length:3d}\n'
1692 result += ctype_to_str(cnode, ' ', ['Reserved'])
1693 if self._verbose:
1694 if extra is not None:
1695 result += hexdump(extra, ' ')
1696 result += '\n'
1697
1698 return result
1699
1700 def valid(self):
1701 return True if self.DevicePath else False
1702
1703 def device_path_node(self, address):
1704 try:
1705 hdr, _ = self._ctype_read_ex(EFI_DEVICE_PATH, address)
1706 if hdr.Length < sizeof(EFI_DEVICE_PATH):
1707 return None, None, None
1708
1709 type_str = self.device_path_dict.get(
1710 (hdr.Type, hdr.SubType), EFI_DEVICE_PATH)
1711 cnode, extra = self._ctype_read_ex(type_str, address, hdr.Length)
1712 return hdr, cnode, extra
1713 except ValueError:
1714 return None, None, None
1715
1716 def device_path_node_str(self, address, verbose=False):
1717 hdr, cnode, extra = self.device_path_node(address)
1718 if hdr is None:
1719 return ''
1720
1721 cname = type(cnode).__name__
1722 result = f'{cname:s} {hdr.Type:2d}:{hdr.SubType:2d} '
1723 result += f'Len: 0x{hdr.Length:03x}\n'
1724 result += ctype_to_str(cnode, ' ', ['Reserved'])
1725 if verbose:
1726 if extra is not None:
1727 result += hexdump(extra, ' ')
1728
1729 return result
1730
1731 def _ctype_read_ex(self, ctype_struct, offset=0, rsize=None):
1732 if offset != 0:
1733 self._file.seek(offset)
1734
1735 type_size = sizeof(ctype_struct)
1736 size = rsize if rsize else type_size
1737 data = self._file.read(size)
1738 if data is None:
1739 return None, None
1740
1741 cdata = ctype_struct.from_buffer(bytearray(data))
1742
1743 if size > type_size:
1744 return cdata, data[type_size:]
1745 else:
1746 return cdata, None
1747
1748
1749 class EfiConfigurationTable:
1750 '''
1751 A class to abstract EFI Configuration Tables from gST->ConfigurationTable
1752 and gST->NumberOfTableEntries. Pass in the gST pointer from EFI,
1753 likely you need to look up this address after you have loaded symbols
1754
1755 Attributes
1756 ??????
1757 ConfigurationTableDict : dictionary
1758 dictionary of EFI Configuration Table entries
1759
1760 Methods
1761 -----------
1762 GetConfigTable(uuid)
1763 pass in VendorGuid and return VendorTable from EFI System Table
1764 DebugImageInfo(table)
1765 return tuple of load address and size of PE/COFF images
1766 '''
1767
1768 ConfigurationTableDict = {}
1769
1770 def __init__(self, file, gST_addr=None):
1771 self._file = file
1772 if gST_addr is None:
1773 # ToDo add code to search for gST via EFI_SYSTEM_TABLE_POINTER
1774 return
1775
1776 gST = self._ctype_read(EFI_SYSTEM_TABLE, gST_addr)
1777 self.read_efi_config_table(gST.NumberOfTableEntries,
1778 gST.ConfigurationTable,
1779 self._ctype_read)
1780
1781 @ classmethod
1782 def __str__(cls):
1783 '''return EFI_CONFIGURATION_TABLE entries as a string'''
1784 result = ""
1785 for key, value in cls.ConfigurationTableDict.items():
1786 result += f'{GuidNames().to_name(key):>37s}: '
1787 result += f'VendorTable = 0x{value:08x}\n'
1788
1789 return result
1790
1791 def _ctype_read(self, ctype_struct, offset=0):
1792 '''ctype worker function to read data'''
1793 if offset != 0:
1794 self._file.seek(offset)
1795
1796 data = self._file.read(sizeof(ctype_struct))
1797 return ctype_struct.from_buffer(bytearray(data))
1798
1799 @ classmethod
1800 def read_efi_config_table(cls, table_cnt, table_ptr, ctype_read):
1801 '''Create a dictionary of EFI Configuration table entries'''
1802 EmptryTables = EFI_CONFIGURATION_TABLE * table_cnt
1803 Tables = ctype_read(EmptryTables, table_ptr)
1804 for i in range(table_cnt):
1805 cls.ConfigurationTableDict[str(GuidNames.to_uuid(
1806 Tables[i].VendorGuid)).upper()] = Tables[i].VendorTable
1807
1808 return cls.ConfigurationTableDict
1809
1810 def GetConfigTable(self, uuid):
1811 ''' Return VendorTable for VendorGuid (uuid.UUID) or None'''
1812 return self.ConfigurationTableDict.get(uuid.upper())
1813
1814 def DebugImageInfo(self, table=None):
1815 '''
1816 Walk the debug image info table to find the LoadedImage protocols
1817 for all the loaded PE/COFF images and return a list of load address
1818 and image size.
1819 '''
1820 ImageLoad = []
1821
1822 if table is None:
1823 table = self.GetConfigTable('49152e77-1ada-4764-b7a2-7afefed95e8b')
1824
1825 DbgInfoHdr = self._ctype_read(EFI_DEBUG_IMAGE_INFO_TABLE_HEADER, table)
1826 NormalImageArray = EFI_DEBUG_IMAGE_INFO * DbgInfoHdr.TableSize
1827 NormalImageArray = self._ctype_read(
1828 NormalImageArray, DbgInfoHdr.EfiDebugImageInfoTable)
1829 for i in range(DbgInfoHdr.TableSize):
1830 ImageInfo = self._ctype_read(
1831 EFI_DEBUG_IMAGE_INFO_NORMAL, NormalImageArray[i].NormalImage)
1832 LoadedImage = self._ctype_read(
1833 EFI_LOADED_IMAGE_PROTOCOL,
1834 ImageInfo.LoadedImageProtocolInstance)
1835 ImageLoad.append((LoadedImage.ImageBase, LoadedImage.ImageSize))
1836
1837 return ImageLoad
1838
1839
1840 class PeTeImage:
1841 '''
1842 A class to abstract PE/COFF or TE image processing via passing in a
1843 Python file like object. If you pass in an address the PE/COFF is parsed,
1844 if you pass in NULL for an address then you get a class instance you can
1845 use to search memory for a PE/COFF hader given a pc value.
1846
1847 Attributes
1848 ??????
1849 LoadAddress : int
1850 Load address of the PE/COFF image
1851 AddressOfEntryPoint : int
1852 Address of the Entry point of the PE/COFF image
1853 TextAddress : int
1854 Start of the PE/COFF text section
1855 DataAddress : int
1856 Start of the PE/COFF data section
1857 CodeViewPdb : str
1858 File name of the symbols file
1859 CodeViewUuid : uuid:UUID
1860 GUID for "RSDS" Debug Directory entry, or Mach-O UUID for "MTOC"
1861
1862 Methods
1863 -----------
1864 pcToPeCoff(address, step, max_range, rom_range)
1865 Given an address(pc) find the PE/COFF image it is in
1866 sections_to_str()
1867 return a string giving info for all the PE/COFF sections
1868 '''
1869
1870 def __init__(self, file, address=0):
1871 self._file = file
1872
1873 # book keeping, but public
1874 self.PeHdr = None
1875 self.TeHdr = None
1876 self.Machine = None
1877 self.Subsystem = None
1878 self.CodeViewSig = None
1879 self.e_lfanew = 0
1880 self.NumberOfSections = 0
1881 self.Sections = None
1882
1883 # Things debuggers may want to know
1884 self.LoadAddress = 0 if address is None else address
1885 self.EndLoadAddress = 0
1886 self.AddressOfEntryPoint = 0
1887 self.TextAddress = 0
1888 self.DataAddress = 0
1889 self.CodeViewPdb = None
1890 self.CodeViewUuid = None
1891 self.TeAdjust = 0
1892
1893 self.dir_name = {
1894 0: 'Export Table',
1895 1: 'Import Table',
1896 2: 'Resource Table',
1897 3: 'Exception Table',
1898 4: 'Certificate Table',
1899 5: 'Relocation Table',
1900 6: 'Debug',
1901 7: 'Architecture',
1902 8: 'Global Ptr',
1903 9: 'TLS Table',
1904 10: 'Load Config Table',
1905 11: 'Bound Import',
1906 12: 'IAT',
1907 13: 'Delay Import Descriptor',
1908 14: 'CLR Runtime Header',
1909 15: 'Reserved',
1910 }
1911
1912 if address is not None:
1913 if self.maybe():
1914 self.parse()
1915
1916 def __str__(self):
1917 if self.PeHdr is None and self.TeHdr is None:
1918 # no PE/COFF header found
1919 return "<class: PeTeImage>"
1920
1921 if self.CodeViewPdb:
1922 pdb = f'{self.Machine}`{self.CodeViewPdb}'
1923 else:
1924 pdb = 'No Debug Info:'
1925
1926 if self.CodeViewUuid:
1927 guid = f'{self.CodeViewUuid}:'
1928 else:
1929 guid = ''
1930
1931 slide = f'slide = {self.TeAdjust:d} ' if self.TeAdjust != 0 else ' '
1932 res = guid + f'{pdb} load = 0x{self.LoadAddress:08x} ' + slide
1933 return res
1934
1935 def _seek(self, offset):
1936 """
1937 seek() relative to start of PE/COFF (TE) image
1938 """
1939 self._file.seek(self.LoadAddress + offset)
1940
1941 def _read_offset(self, size, offset=None):
1942 """
1943 read() relative to start of PE/COFF (TE) image
1944 if offset is not None then seek() before the read
1945 """
1946 if offset is not None:
1947 self._seek(offset)
1948
1949 return self._file.read(size)
1950
1951 def _read_ctype(self, ctype_struct, offset=None):
1952 data = self._read_offset(sizeof(ctype_struct), offset)
1953 return ctype_struct.from_buffer(bytearray(data), 0)
1954
1955 def _unsigned(self, i):
1956 """return a 32-bit unsigned int (UINT32) """
1957 return int.from_bytes(i, byteorder='little', signed=False)
1958
1959 def pcToPeCoff(self,
1960 address,
1961 step=None,
1962 max_range=None,
1963 rom_range=[0xFE800000, 0xFFFFFFFF]):
1964 """
1965 Given an address search backwards for PE/COFF (TE) header
1966 For DXE 4K is probably OK
1967 For PEI you might have to search every 4 bytes.
1968 """
1969 if step is None:
1970 step = 0x1000
1971
1972 if max_range is None:
1973 max_range = 0x200000
1974
1975 if address in range(*rom_range):
1976 # The XIP code in the ROM ends up 4 byte aligned.
1977 step = 4
1978 max_range = min(max_range, 0x100000)
1979
1980 # Align address to page boundary for memory image search.
1981 address = address & ~(step-1)
1982 # Search every step backward
1983 offset_range = list(range(0, min(max_range, address), step))
1984 for offset in offset_range:
1985 if self.maybe(address - offset):
1986 if self.parse():
1987 return True
1988
1989 return False
1990
1991 def maybe(self, offset=None):
1992 """Probe to see if this offset is likely a PE/COFF or TE file """
1993 self.LoadAddress = 0
1994 e_magic = self._read_offset(2, offset)
1995 header_ok = e_magic == b'MZ' or e_magic == b'VZ'
1996 if offset is not None and header_ok:
1997 self.LoadAddress = offset
1998 return header_ok
1999
2000 def parse(self):
2001 """Parse PE/COFF (TE) debug directory entry """
2002 DosHdr = self._read_ctype(EFI_IMAGE_DOS_HEADER, 0)
2003 if DosHdr.e_magic == self._unsigned(b'VZ'):
2004 # TE image
2005 self.TeHdr = self._read_ctype(EFI_TE_IMAGE_HEADER, 0)
2006
2007 self.TeAdjust = sizeof(self.TeHdr) - self.TeHdr.StrippedSize
2008 self.Machine = image_machine_dict.get(self.TeHdr.Machine, None)
2009 self.Subsystem = self.TeHdr.Subsystem
2010 self.AddressOfEntryPoint = self.TeHdr.AddressOfEntryPoint
2011
2012 debug_dir_size = self.TeHdr.DataDirectoryDebug.Size
2013 debug_dir_offset = (self.TeAdjust +
2014 self.TeHdr.DataDirectoryDebug.VirtualAddress)
2015 else:
2016 if DosHdr.e_magic == self._unsigned(b'MZ'):
2017 self.e_lfanew = DosHdr.e_lfanew
2018 else:
2019 self.e_lfanew = 0
2020
2021 self.PeHdr = self._read_ctype(
2022 EFI_IMAGE_NT_HEADERS64, self.e_lfanew)
2023 if self.PeHdr.Signature != self._unsigned(b'PE\0\0'):
2024 return False
2025
2026 if self.PeHdr.OptionalHeader.Magic == \
2027 EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
2028 self.PeHdr = self._read_ctype(
2029 EFI_IMAGE_NT_HEADERS32, self.e_lfanew)
2030
2031 if self.PeHdr.OptionalHeader.NumberOfRvaAndSizes <= \
2032 DIRECTORY_DEBUG:
2033 return False
2034
2035 self.Machine = image_machine_dict.get(
2036 self.PeHdr.FileHeader.Machine, None)
2037 self.Subsystem = self.PeHdr.OptionalHeader.Subsystem
2038 self.AddressOfEntryPoint = \
2039 self.PeHdr.OptionalHeader.AddressOfEntryPoint
2040 self.TeAdjust = 0
2041
2042 debug_dir_size = self.PeHdr.OptionalHeader.DataDirectory[
2043 DIRECTORY_DEBUG].Size
2044 debug_dir_offset = self.PeHdr.OptionalHeader.DataDirectory[
2045 DIRECTORY_DEBUG].VirtualAddress
2046
2047 if self.Machine is None or self.Subsystem not in [0, 10, 11, 12]:
2048 return False
2049
2050 self.AddressOfEntryPoint += self.LoadAddress
2051
2052 self.sections()
2053 return self.processDebugDirEntry(debug_dir_offset, debug_dir_size)
2054
2055 def sections(self):
2056 '''Parse the PE/COFF (TE) section table'''
2057 if self.Sections is not None:
2058 return
2059 elif self.TeHdr is not None:
2060 self.NumberOfSections = self.TeHdr.NumberOfSections
2061 offset = sizeof(EFI_TE_IMAGE_HEADER)
2062 elif self.PeHdr is not None:
2063 self.NumberOfSections = self.PeHdr.FileHeader.NumberOfSections
2064 offset = sizeof(c_uint32) + \
2065 sizeof(EFI_IMAGE_FILE_HEADER)
2066 offset += self.PeHdr.FileHeader.SizeOfOptionalHeader
2067 offset += self.e_lfanew
2068 else:
2069 return
2070
2071 self.Sections = EFI_IMAGE_SECTION_HEADER * self.NumberOfSections
2072 self.Sections = self._read_ctype(self.Sections, offset)
2073
2074 for i in range(self.NumberOfSections):
2075 name = str(self.Sections[i].Name, 'ascii', 'ignore')
2076 addr = self.Sections[i].VirtualAddress
2077 addr += self.LoadAddress + self.TeAdjust
2078 if name == '.text':
2079 self.TextAddress = addr
2080 elif name == '.data':
2081 self.DataAddress = addr
2082
2083 end_addr = addr + self.Sections[i].VirtualSize - 1
2084 if end_addr > self.EndLoadAddress:
2085 self.EndLoadAddress = end_addr
2086
2087 def sections_to_str(self):
2088 # return text summary of sections
2089 # name virt addr (virt size) flags:Characteristics
2090 result = ''
2091 for i in range(self.NumberOfSections):
2092 name = str(self.Sections[i].Name, 'ascii', 'ignore')
2093 result += f'{name:8s} '
2094 result += f'0x{self.Sections[i].VirtualAddress:08X} '
2095 result += f'(0x{self.Sections[i].VirtualSize:05X}) '
2096 result += f'flags:0x{self.Sections[i].Characteristics:08X}\n'
2097
2098 return result
2099
2100 def directory_to_str(self):
2101 result = ''
2102 if self.TeHdr:
2103 debug_size = self.TeHdr.DataDirectoryDebug.Size
2104 if debug_size > 0:
2105 debug_offset = (self.TeAdjust
2106 + self.TeHdr.DataDirectoryDebug.VirtualAddress)
2107 result += f"Debug 0x{debug_offset:08X} 0x{debug_size}\n"
2108
2109 relocation_size = self.TeHdr.DataDirectoryBaseReloc.Size
2110 if relocation_size > 0:
2111 relocation_offset = (
2112 self.TeAdjust
2113 + self.TeHdr.DataDirectoryBaseReloc.VirtualAddress)
2114 result += f'Relocation 0x{relocation_offset:08X} '
2115 result += f' 0x{relocation_size}\n'
2116
2117 elif self.PeHdr:
2118 for i in range(self.PeHdr.OptionalHeader.NumberOfRvaAndSizes):
2119 size = self.PeHdr.OptionalHeader.DataDirectory[i].Size
2120 if size == 0:
2121 continue
2122
2123 virt_addr = self.PeHdr.OptionalHeader.DataDirectory[
2124 i].VirtualAddress
2125 name = self.dir_name.get(i, '?')
2126 result += f'{name:s} 0x{virt_addr:08X} 0x{size:X}\n'
2127
2128 return result
2129
2130 def processDebugDirEntry(self, virt_address, virt_size):
2131 """Process PE/COFF Debug Directory Entry"""
2132 if (virt_address == 0 or
2133 virt_size < sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)):
2134 return False
2135
2136 data = bytearray(self._read_offset(virt_size, virt_address))
2137 for offset in range(0,
2138 virt_size,
2139 sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)):
2140 DirectoryEntry = EFI_IMAGE_DEBUG_DIRECTORY_ENTRY.from_buffer(
2141 data[offset:])
2142 if DirectoryEntry.Type != 2:
2143 continue
2144
2145 entry = self._read_offset(
2146 DirectoryEntry.SizeOfData, DirectoryEntry.RVA + self.TeAdjust)
2147 self.CodeViewSig = entry[:4]
2148 if self.CodeViewSig == b'MTOC':
2149 self.CodeViewUuid = uuid.UUID(bytes_le=entry[4:4+16])
2150 PdbOffset = 20
2151 elif self.CodeViewSig == b'RSDS':
2152 self.CodeViewUuid = uuid.UUID(bytes_le=entry[4:4+16])
2153 PdbOffset = 24
2154 elif self.CodeViewSig == b'NB10':
2155 PdbOffset = 16
2156 else:
2157 continue
2158
2159 # can't find documentation about Pdb string encoding?
2160 # guessing utf-8 since that will match file systems in macOS
2161 # and Linux Windows is UTF-16, or ANSI adjusted for local.
2162 # We might need a different value for Windows here?
2163 self.CodeViewPdb = entry[PdbOffset:].split(b'\x00')[
2164 0].decode('utf-8')
2165 return True
2166 return False
2167
2168
2169 def main():
2170 '''Process arguments as PE/COFF files'''
2171 for fname in sys.argv[1:]:
2172 with open(fname, 'rb') as f:
2173 image = PeTeImage(f)
2174 print(image)
2175 res = f'EntryPoint = 0x{image.AddressOfEntryPoint:08x} '
2176 res += f'TextAddress = 0x{image.TextAddress:08x} '
2177 res += f'DataAddress = 0x{image.DataAddress:08x}'
2178 print(res)
2179 print(image.sections_to_str())
2180 print('Data Directories:')
2181 print(image.directory_to_str())
2182
2183
2184 if __name__ == "__main__":
2185 main()