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