1# Copyright (C) 2021-2022 Intel Corporation. 2# 3# SPDX-License-Identifier: BSD-3-Clause 4# 5 6import logging 7from copy import copy 8from math import floor 9 10from .exception import * 11from .stream import Stream 12 13class NamedDecl: 14 @staticmethod 15 def object_type(): 16 return 0 17 18 def __init__(self, name, tree): 19 self.tree = tree 20 self.name = name 21 22 def dump(self): 23 print(f"{self.name}: {self.__class__.__name__}") 24 25class FieldDecl(NamedDecl): 26 def __init__(self, name, length, tree): 27 super().__init__(name, tree) 28 self.length = length 29 30 def dump(self): 31 print(f"{self.name}: {self.__class__.__name__}, {self.length} bits") 32 33class OperationRegionDecl(NamedDecl): 34 @staticmethod 35 def object_type(): 36 return 10 37 38 def __init__(self, name, tree): 39 super().__init__(name, tree) 40 41class OperationFieldDecl(NamedDecl): 42 def __init__(self, name, length, tree): 43 super().__init__(name, tree) 44 self.region = None 45 self.offset = None 46 self.length = length 47 self.access_width = None 48 self.parent_tree = None 49 50 def set_location(self, region, offset, access_width): 51 self.region = region 52 self.offset = offset 53 self.access_width = access_width 54 55 def set_indexed_location(self, index_register, data_register, index, access_width): 56 self.region = (index_register, data_register) 57 self.offset = index 58 self.access_width = access_width 59 60 def dump(self): 61 if self.region and self.offset: 62 bit_index = self.offset 63 byte_index = floor(bit_index / 8) 64 offset_in_byte = bit_index % 8 65 if isinstance(self.region, str): 66 print(f"{self.name}: {self.__class__.__name__}, {self.region}: bit {hex(bit_index)} (byte {hex(byte_index)}.{offset_in_byte}), {self.length} bits") 67 else: 68 print(f"{self.name}: {self.__class__.__name__}, ({self.region[0]}, {self.region[1]}): bit {hex(bit_index)} (byte {hex(byte_index)}.{offset_in_byte}), {self.length} bits") 69 else: 70 print(f"{self.name}: {self.__class__.__name__}, {self.length} bits") 71 72class AliasDecl(NamedDecl): 73 def __init__(self, name, source, tree): 74 super().__init__(name, tree) 75 self.name = name 76 self.source = source 77 78 def dump(self): 79 print(f"{self.name}: {self.__class__.__name__}, aliasing {self.source}") 80 81class MethodDecl(NamedDecl): 82 @staticmethod 83 def object_type(): 84 return 8 85 86 def __init__(self, name, nargs, tree): 87 super().__init__(name, tree) 88 self.nargs = nargs 89 90 def dump(self): 91 print(f"{self.name}: {self.__class__.__name__}, {self.nargs} args") 92 93class PredefinedMethodDecl(NamedDecl): 94 @staticmethod 95 def object_type(): 96 return 8 97 98 def __init__(self, name, nargs, fn): 99 super().__init__(name, None) 100 self.nargs = nargs 101 self.fn = fn 102 103 def dump(self): 104 print(f"{self.name}: {self.__class__.__name__}, {self.nargs} args") 105 106class DeviceDecl(NamedDecl): 107 @staticmethod 108 def object_type(): 109 return 6 110 111 def __init__(self, name, tree): 112 super().__init__(name, tree) 113 114def predefined_osi(args): 115 feature = args[0].get() 116 if feature.startswith("Linux"): 117 return 0xffffffff 118 elif feature.startswith("Windows") or \ 119 feature.startswith("FreeBSD") or \ 120 feature.startswith("HP-UX") or \ 121 feature.startswith("OpenVMS"): 122 return 0 123 return 0xffffffff 124 125class Context: 126 @staticmethod 127 def realpath(scope, name): 128 if name and name.startswith("\\"): 129 return name 130 131 if name and name.startswith("^"): 132 parent_count = name.count("^") 133 assert parent_count <= len(scope) 134 scope = scope[:-parent_count] 135 name = name[parent_count:] 136 137 if scope: 138 if isinstance(scope, list): 139 if name: 140 return f"\\{'.'.join(scope)}.{name}" 141 else: 142 return f"\\{'.'.join(scope)}" 143 elif isinstance(scope, str): 144 if scope == "\\": 145 return f"\\{name}" 146 else: 147 return f"{scope}.{name}" 148 else: 149 raise NotImplementedError 150 else: 151 if name: 152 return f"\\{name}" 153 else: 154 return f"\\" 155 156 @staticmethod 157 def parent(scope): 158 if scope == "\\": 159 return "\\" 160 else: 161 parent = scope[:-4] 162 if parent.endswith("."): 163 return parent[:-1] 164 else: 165 return parent 166 167 @staticmethod 168 def normalize_namepath(namepath): 169 path = namepath.lstrip("\\^") 170 prefix = namepath[:(len(namepath) - len(path))] 171 parts = '.'.join(map(lambda x: x[:4].ljust(4, '_'), path.split("."))) 172 return prefix + parts 173 174 def __init__(self): 175 self.streams = {} 176 self.current_stream = None 177 self.trees = {} 178 179 # Loaded namespace 180 self.__symbol_table = {} 181 self.__devices = [] 182 183 # Context during parsing 184 self.__current_scope = [] 185 self.__scope_history = [] 186 self.__deferred_mode_depth = 0 187 188 # Context during interpretation 189 self.__binding_table = {} 190 self.__op_regions = {} 191 192 # Register predefined objects per section 5.7, ACPI 6.4 193 self.register_symbol(NamedDecl("_GL_", None)) 194 self.register_symbol(PredefinedMethodDecl("_OSI", 1, predefined_osi)) 195 self.register_symbol(NamedDecl("_OS_", None)) 196 self.register_symbol(NamedDecl("_REV", None)) 197 self.register_symbol(NamedDecl("_DLM", None)) 198 199 # Mode switches 200 self.__skip_external_on_lookup = False 201 202 def switch_stream(self, val): 203 if isinstance(val, str): 204 if not val in self.streams.keys(): 205 with open(val, "rb") as f: 206 stream = Stream(f.read()) 207 self.streams[val] = stream 208 self.current_stream = stream 209 else: 210 self.current_stream = self.streams[val] 211 self.current_stream.reset() 212 elif isinstance(val, (bytes, bytearray)): 213 self.current_stream = Stream(val) 214 else: 215 raise NotImplementedError(f"Cannot use {val} as a stream.") 216 217 def get_scope(self): 218 return self.realpath(self.__current_scope, "") 219 220 def change_scope(self, new_scope): 221 self.__scope_history.append(copy(self.__current_scope)) 222 if isinstance(new_scope, list): 223 self.__current_scope = new_scope 224 elif isinstance(new_scope, str): 225 if new_scope.startswith("\\"): 226 self.__current_scope = [i for i in new_scope[1:].split(".") if i] 227 elif new_scope.startswith("^"): 228 parent_count = new_scope.count("^") 229 assert parent_count <= len(self.__current_scope) 230 self.__current_scope = self.__current_scope[:-parent_count].extend(new_scope.split(".")) 231 else: 232 self.__current_scope.extend(new_scope.split(".")) 233 else: 234 raise InvalidPath(new_scope) 235 236 def pop_scope(self): 237 assert(self.__scope_history) 238 self.__current_scope = self.__scope_history.pop() 239 240 def __register_symbol(self, symbol): 241 self.__symbol_table[symbol.name] = symbol 242 if isinstance(symbol, DeviceDecl): 243 self.__devices.append(symbol) 244 245 def register_symbol(self, symbol): 246 symbol.name = self.realpath(self.__current_scope, symbol.name) 247 if symbol.name in self.__symbol_table.keys(): 248 old_tree = self.__symbol_table[symbol.name].tree 249 new_tree = symbol.tree 250 if old_tree.label != new_tree.label: 251 if old_tree.label == "DefExternal": 252 self.__register_symbol(symbol) 253 elif new_tree.label == "DefExternal": 254 pass 255 else: 256 logging.debug(f"{symbol.name} is redefined as {new_tree.label} (previously was {old_tree.label})") 257 self.__register_symbol(symbol) 258 else: 259 self.__register_symbol(symbol) 260 261 def unregister_object(self, realpath): 262 sym = self.__symbol_table.pop(realpath, None) 263 if isinstance(sym, DeviceDecl): 264 self.__devices.remove(sym) 265 266 def __lookup_symbol_in_parents(self, table, name): 267 prefix_len = len(self.__current_scope) 268 while prefix_len >= 0: 269 path = self.realpath(self.__current_scope[:prefix_len], name) 270 if path in table: 271 sym = table[path] 272 # External object declarations are only for parsing. At 273 # interpretation time such declarations should not be looked up. 274 if (not self.__skip_external_on_lookup) or \ 275 isinstance(sym, PredefinedMethodDecl) or \ 276 (sym.tree and sym.tree.label != "DefExternal"): 277 return sym 278 prefix_len -= 1 279 raise KeyError(name) 280 281 def lookup_symbol(self, name, scope=None): 282 if scope: 283 self.change_scope(scope) 284 try: 285 if name.startswith("\\"): 286 ret = self.__symbol_table[name] 287 elif name.startswith("^") or name.find(".") >= 0: 288 realpath = self.realpath(self.__current_scope, name) 289 ret = self.__symbol_table[realpath] 290 else: 291 ret = self.__lookup_symbol_in_parents(self.__symbol_table, name) 292 except KeyError: 293 ret = None 294 295 if scope: 296 self.pop_scope() 297 if not ret: 298 raise UndefinedSymbol(name, scope if scope else self.get_scope()) 299 return ret 300 301 def has_symbol(self, name): 302 try: 303 self.lookup_symbol(name) 304 return True 305 except UndefinedSymbol: 306 return False 307 308 def lookup_symbol_by_tree(self, tree): 309 result = filter(lambda x: x[1].tree is tree, self.__symbol_table.items()) 310 try: 311 return next(result)[1] 312 except StopIteration: 313 return None 314 315 def get_fresh_name(self): 316 current_scope = self.get_scope() 317 for i in range(0, 10): 318 name = self.realpath(current_scope, f"_T_{i}") 319 if not self.lookup_symbol(name): 320 return name 321 raise NotImplementedError("Cannot find a proper fresh name") 322 323 @property 324 def devices(self): 325 return self.__devices 326 327 def dump_symbols(self): 328 for k,v in sorted(self.__symbol_table.items()): 329 v.dump() 330 331 def enter_deferred_mode(self): 332 self.__deferred_mode_depth += 1 333 334 def exit_deferred_mode(self): 335 assert self.__deferred_mode_depth > 0 336 self.__deferred_mode_depth -= 1 337 338 def in_deferred_mode(self): 339 return (self.__deferred_mode_depth > 0) 340 341 def skip_external_on_lookup(self): 342 self.__skip_external_on_lookup = True 343 344 def register_operation_region(self, name, op_region): 345 self.__op_regions[name] = op_region 346 347 def lookup_operation_region(self, name): 348 try: 349 if name.startswith("\\"): 350 return self.__op_regions[name] 351 elif name.startswith("^") or name.find(".") >= 0: 352 realpath = self.realpath(self.__current_scope, name) 353 return self.__op_regions[realpath] 354 else: 355 return self.__lookup_symbol_in_parents(self.__op_regions, name) 356 except KeyError: 357 return None 358 359 def register_binding(self, name, value): 360 sym = self.lookup_symbol(name) 361 logging.debug(f"Bind {sym.name} to {value}") 362 self.__binding_table[sym.name] = value 363 364 def lookup_binding(self, name): 365 sym = self.lookup_symbol(name) 366 try: 367 return self.__binding_table[sym.name] 368 except KeyError: 369 return None 370 371 def dump_bindings(self): 372 for k in sorted(self.__binding_table.keys()): 373 v = self.__binding_table[k] 374 if v: 375 try: 376 val = v.get() 377 if isinstance(val, int): 378 val = hex(val) 379 print(k, val) 380 except NotImplementedError: 381 print(k, f"({v.__class__.__name__})") 382 except AttributeError: 383 print(k, f"(wrong type: {v})") 384 else: 385 print(k, "(None)") 386