1# Copyright (c) 2015-2022 Intel Corporation. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are met: 5# 6# * Redistributions of source code must retain the above copyright notice, 7# this list of conditions and the following disclaimer. 8# * Redistributions in binary form must reproduce the above copyright notice, 9# this list of conditions and the following disclaimer in the documentation 10# and/or other materials provided with the distribution. 11# * Neither the name of Intel Corporation nor the names of its contributors 12# may be used to endorse or promote products derived from this software 13# without specific prior written permission. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26"""bits.cdata module.""" 27 28from __future__ import print_function 29import binascii 30import ctypes 31import textwrap 32import uuid 33 34def print_fields(cls): 35 with ttypager.page(): 36 print("{}".format(cls.__name__)) 37 print("{:20s} {:6} {:6}".format('field', 'length', 'offset')) 38 for f in cls._fields_: 39 a = getattr(cls, f[0]) 40 print("{:20s} {:6} {:6}".format(f[0], a.size, a.offset)) 41 42def to_bytes(var): 43 return (ctypes.c_char * ctypes.sizeof(var)).from_buffer(var).raw 44 45_CTYPES_HEX_TYPES = ( 46 ctypes.c_void_p, 47 ctypes.c_uint8, ctypes.c_uint16, ctypes.c_uint32, ctypes.c_uint64, 48 ctypes.c_ubyte, ctypes.c_ushort, ctypes.c_uint, ctypes.c_ulong, ctypes.c_ulonglong, 49) 50 51class c_base(object): 52 """Base class for ctypes structures and unions.""" 53 @staticmethod 54 def _formatval(t, val): 55 if val is not None and t in _CTYPES_HEX_TYPES: 56 return "{:#x}".format(val) 57 if issubclass(t, ctypes.Array): 58 if issubclass(t._type_, (ctypes.c_char, ctypes.c_wchar)): 59 return "'{}'".format(val) 60 else: 61 return "[{}]".format(", ".join(Struct._formatval(t._type_, item) for item in val)) 62 return "{}".format(val) 63 64 65 def _formatter(self, field): 66 name = field[0] 67 t = field[1] 68 val = getattr(self, name) 69 if hasattr(self, '_formats'): 70 f = self._formats.get(name, None) 71 if f: 72 return f(val) 73 if issubclass(t, (Struct, Union)): 74 val._indent = self._indent 75 return str(val) 76 if issubclass(t, ctypes.Array): 77 if issubclass(t._type_, (Struct, Union)): 78 s = "[" 79 for item in val: 80 item._indent = self._indent + " " 81 s += "\n" + str(item) 82 s += "]" 83 return s 84 return self._formatval(t, val) 85 86 _indent = "" 87 88 def _wrap(self, str, indent=True): 89 line_len = 77 - len(self._indent + ' ') 90 _wrapper = textwrap.TextWrapper(width=line_len, initial_indent=self._indent, subsequent_indent=self._indent + ' ') 91 _wrapper_indentall = textwrap.TextWrapper(width=line_len, initial_indent=self._indent + ' ', subsequent_indent=self._indent + ' ') 92 def __wrap(): 93 wrapper = _wrapper 94 for line in str.split("\n"): 95 # Preserve blank lines, for which wrapper emits an empty list 96 if not line: 97 yield "" 98 for wrapped_line in wrapper.wrap(line): 99 yield wrapped_line 100 if indent: 101 wrapper = _wrapper_indentall 102 return '\n'.join(__wrap()) 103 104 def preface_field(self, field): 105 a = getattr(self.__class__, field[0]) 106 return "ofs={} ".format(a.offset) 107 108 def bitfield_info(self, field): 109 a = getattr(self.__class__, field[0]) 110 bit_count = a.size >> 16 111 lo_bit = a.size & 0xFFFF 112 hi_bit = lo_bit + bit_count - 1 113 return bit_count, hi_bit, lo_bit 114 115 def preface_bitfield(self, field): 116 bit_count, hi_bit, lo_bit = self.bitfield_info(field) 117 if bit_count > 1: 118 return "bits[{}:{}]=".format(hi_bit, lo_bit) 119 if bit_count == 1: 120 return "bit[{}]=".format(lo_bit) 121 return "" 122 123 def __str__(self): 124 self._indent += " " 125 s = "{}({})".format(self.__class__.__name__, "".join("\n{}{}={}{}".format(self.preface_field(field), field[0], self.preface_bitfield(field), self._formatter(field)) for field in self._fields_)) 126 self._indent = "" 127 return self._wrap(s) 128 129class Struct(ctypes.Structure, c_base): 130 """Base class for ctypes structures.""" 131 def __hash__(self): 132 buf = (ctypes.c_uint8 * ctypes.sizeof(self)).from_buffer(self) 133 return binascii.crc32(buf) 134 135 def __cmp__(self, other): 136 return cmp(hash(self), hash(other)) 137 138class Union(ctypes.Union, c_base): 139 """Base class for ctypes unions.""" 140 def __hash__(self): 141 buf = (ctypes.c_uint8 * ctypes.sizeof(self)).from_buffer(self) 142 return binascii.crc32(buf) 143 144 def __cmp__(self, other): 145 return cmp(hash(self), hash(other)) 146 147class GUID(Struct): 148 _fields_ = [ 149 ('Data', ctypes.c_ubyte * 16), 150 ] 151 152 def __init__(self, *args, **kwargs): 153 """Create a GUID. Accepts any arguments the uuid.UUID constructor 154 would accept. Also accepts an instance of uuid.UUID, either as the 155 first argument or as a keyword argument "uuid". As with other 156 ctypes structures, passing no parameters yields a zero-initialized 157 structure.""" 158 u = kwargs.get("uuid") 159 if u is not None: 160 self.uuid = u 161 elif not(args) and not(kwargs): 162 self.uuid = uuid.UUID(int=0) 163 elif args and isinstance(args[0], uuid.UUID): 164 self.uuid = args[0] 165 else: 166 self.uuid = uuid.UUID(*args, **kwargs) 167 168 def _get_uuid(self): 169 return uuid.UUID(bytes_le=to_bytes(self)) 170 171 def _set_uuid(self, u): 172 ctypes.memmove(ctypes.addressof(self), ctypes.c_char_p(u.bytes_le), ctypes.sizeof(self)) 173 174 uuid = property(_get_uuid, _set_uuid) 175 176 def __cmp__(self, other): 177 if isinstance(other, GUID): 178 return cmp(self.uuid, other.uuid) 179 if isinstance(other, uuid.UUID): 180 return cmp(self.uuid, other) 181 return NotImplemented 182 183 def __hash__(self): 184 return hash(self.uuid) 185 186 def __repr__(self): 187 return "GUID({})".format(self.uuid) 188 189 def __str__(self): 190 return "{}".format(self.uuid) 191 192def _format_guid(val): 193 try: 194 import efi 195 guid_str = efi.known_uuids.get(val.uuid, None) 196 except: 197 guid_str = None 198 if guid_str: 199 return '{} ({})'.format(val, guid_str) 200 return '{}'.format(val) 201