1#!/usr/bin/env python3 2# 3# Copyright (c) 2017 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6""" 7Script to generate gperf tables of kernel object metadata 8 9User mode threads making system calls reference kernel objects by memory 10address, as the kernel/driver APIs in Zephyr are the same for both user 11and supervisor contexts. It is necessary for the kernel to be able to 12validate accesses to kernel objects to make the following assertions: 13 14 - That the memory address points to a kernel object 15 16 - The kernel object is of the expected type for the API being invoked 17 18 - The kernel object is of the expected initialization state 19 20 - The calling thread has sufficient permissions on the object 21 22For more details see the :ref:`kernelobjects` section in the documentation. 23 24The zephyr build generates an intermediate ELF binary, zephyr_prebuilt.elf, 25which this script scans looking for kernel objects by examining the DWARF 26debug information to look for instances of data structures that are considered 27kernel objects. For device drivers, the API struct pointer populated at build 28time is also examined to disambiguate between various device driver instances 29since they are all 'struct device'. 30 31This script can generate five different output files: 32 33 - A gperf script to generate the hash table mapping kernel object memory 34 addresses to kernel object metadata, used to track permissions, 35 object type, initialization state, and any object-specific data. 36 37 - A header file containing generated macros for validating driver instances 38 inside the system call handlers for the driver subsystem APIs. 39 40 - A code fragment included by kernel.h with one enum constant for 41 each kernel object type and each driver instance. 42 43 - The inner cases of a switch/case C statement, included by 44 kernel/userspace.c, mapping the kernel object types and driver 45 instances to their human-readable representation in the 46 otype_to_str() function. 47 48 - The inner cases of a switch/case C statement, included by 49 kernel/userspace.c, mapping kernel object types to their sizes. 50 This is used for allocating instances of them at runtime 51 (CONFIG_DYNAMIC_OBJECTS) in the obj_size_get() function. 52""" 53 54import sys 55import argparse 56import math 57import os 58import struct 59import json 60from packaging import version 61 62import elftools 63from elftools.elf.elffile import ELFFile 64from elftools.elf.sections import SymbolTableSection 65 66if version.parse(elftools.__version__) < version.parse('0.24'): 67 sys.exit("pyelftools is out of date, need version 0.24 or later") 68 69from collections import OrderedDict 70 71# Keys in this dictionary are structs which should be recognized as kernel 72# objects. Values are a tuple: 73# 74# - The first item is None, or the name of a Kconfig that 75# indicates the presence of this object's definition in case it is not 76# available in all configurations. 77# 78# - The second item is a boolean indicating whether it is permissible for 79# the object to be located in user-accessible memory. 80# 81# - The third items is a boolean indicating whether this item can be 82# dynamically allocated with k_object_alloc(). Keep this in sync with 83# the switch statement in z_impl_k_object_alloc(). 84# 85# Key names in all caps do not correspond to a specific data type but instead 86# indicate that objects of its type are of a family of compatible data 87# structures 88 89# Regular dictionaries are ordered only with Python 3.6 and 90# above. Good summary and pointers to official documents at: 91# https://stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6 92kobjects = OrderedDict([ 93 ("k_mem_slab", (None, False, True)), 94 ("k_msgq", (None, False, True)), 95 ("k_mutex", (None, False, True)), 96 ("k_pipe", (None, False, True)), 97 ("k_queue", (None, False, True)), 98 ("k_poll_signal", (None, False, True)), 99 ("k_sem", (None, False, True)), 100 ("k_stack", (None, False, True)), 101 ("k_thread", (None, False, True)), # But see # 102 ("k_timer", (None, False, True)), 103 ("z_thread_stack_element", (None, False, False)), 104 ("device", (None, False, False)), 105 ("NET_SOCKET", (None, False, False)), 106 ("net_if", (None, False, False)), 107 ("sys_mutex", (None, True, False)), 108 ("k_futex", (None, True, False)), 109 ("k_condvar", (None, False, True)), 110 ("k_event", ("CONFIG_EVENTS", False, True)), 111 ("ztest_suite_node", ("CONFIG_ZTEST", True, False)), 112 ("ztest_suite_stats", ("CONFIG_ZTEST", True, False)), 113 ("ztest_unit_test", ("CONFIG_ZTEST", True, False)), 114 ("ztest_test_rule", ("CONFIG_ZTEST", True, False)), 115 ("rtio", ("CONFIG_RTIO", False, False)), 116 ("rtio_iodev", ("CONFIG_RTIO", False, False)), 117 ("sensor_decoder_api", ("CONFIG_SENSOR_ASYNC_API", True, False)) 118]) 119 120def kobject_to_enum(kobj): 121 if kobj.startswith("k_") or kobj.startswith("z_"): 122 name = kobj[2:] 123 else: 124 name = kobj 125 126 return "K_OBJ_%s" % name.upper() 127 128subsystems = [ 129 # Editing the list is deprecated, add the __subsystem sentinel to your driver 130 # api declaration instead. e.x. 131 # 132 # __subsystem struct my_driver_api { 133 # .... 134 #}; 135] 136 137# Names of all structs tagged with __net_socket, found by parse_syscalls.py 138net_sockets = [ ] 139 140def subsystem_to_enum(subsys): 141 return "K_OBJ_DRIVER_" + subsys[:-11].upper() 142 143# --- debug stuff --- 144 145scr = os.path.basename(sys.argv[0]) 146 147def debug(text): 148 if not args.verbose: 149 return 150 sys.stdout.write(scr + ": " + text + "\n") 151 152def error(text): 153 sys.exit("%s ERROR: %s" % (scr, text)) 154 155def debug_die(die, text): 156 lp_header = die.dwarfinfo.line_program_for_CU(die.cu).header 157 files = lp_header["file_entry"] 158 includes = lp_header["include_directory"] 159 160 fileinfo = files[die.attributes["DW_AT_decl_file"].value - 1] 161 filename = fileinfo.name.decode("utf-8") 162 filedir = includes[fileinfo.dir_index - 1].decode("utf-8") 163 164 path = os.path.join(filedir, filename) 165 lineno = die.attributes["DW_AT_decl_line"].value 166 167 debug(str(die)) 168 debug("File '%s', line %d:" % (path, lineno)) 169 debug(" %s" % text) 170 171# -- ELF processing 172 173DW_OP_addr = 0x3 174DW_OP_fbreg = 0x91 175STACK_TYPE = "z_thread_stack_element" 176thread_counter = 0 177sys_mutex_counter = 0 178futex_counter = 0 179stack_counter = 0 180 181# Global type environment. Populated by pass 1. 182type_env = {} 183extern_env = {} 184 185class KobjectInstance: 186 def __init__(self, type_obj, addr): 187 self.addr = addr 188 self.type_obj = type_obj 189 190 # Type name determined later since drivers needs to look at the 191 # API struct address 192 self.type_name = None 193 self.data = 0 194 195 196class KobjectType: 197 def __init__(self, offset, name, size, api=False): 198 self.name = name 199 self.size = size 200 self.offset = offset 201 self.api = api 202 203 def __repr__(self): 204 return "<kobject %s>" % self.name 205 206 @staticmethod 207 def has_kobject(): 208 return True 209 210 def get_kobjects(self, addr): 211 return {addr: KobjectInstance(self, addr)} 212 213 214class ArrayType: 215 def __init__(self, offset, elements, member_type): 216 self.elements = elements 217 self.member_type = member_type 218 self.offset = offset 219 220 def __repr__(self): 221 return "<array of %d>" % self.member_type 222 223 def has_kobject(self): 224 if self.member_type not in type_env: 225 return False 226 227 return type_env[self.member_type].has_kobject() 228 229 def get_kobjects(self, addr): 230 mt = type_env[self.member_type] 231 232 # Stacks are arrays of _k_stack_element_t but we want to treat 233 # the whole array as one kernel object (a thread stack) 234 # Data value gets set to size of entire region 235 if isinstance(mt, KobjectType) and mt.name == STACK_TYPE: 236 # An array of stacks appears as a multi-dimensional array. 237 # The last size is the size of each stack. We need to track 238 # each stack within the array, not as one huge stack object. 239 *dimensions, stacksize = self.elements 240 num_members = 1 241 for e in dimensions: 242 num_members = num_members * e 243 244 ret = {} 245 for i in range(num_members): 246 a = addr + (i * stacksize) 247 o = mt.get_kobjects(a) 248 o[a].data = stacksize 249 ret.update(o) 250 return ret 251 252 objs = {} 253 254 # Multidimensional array flattened out 255 num_members = 1 256 for e in self.elements: 257 num_members = num_members * e 258 259 for i in range(num_members): 260 objs.update(mt.get_kobjects(addr + (i * mt.size))) 261 return objs 262 263 264class AggregateTypeMember: 265 def __init__(self, offset, member_name, member_type, member_offset): 266 self.member_name = member_name 267 self.member_type = member_type 268 if isinstance(member_offset, list): 269 # DWARF v2, location encoded as set of operations 270 # only "DW_OP_plus_uconst" with ULEB128 argument supported 271 if member_offset[0] == 0x23: 272 self.member_offset = member_offset[1] & 0x7f 273 for i in range(1, len(member_offset)-1): 274 if member_offset[i] & 0x80: 275 self.member_offset += ( 276 member_offset[i+1] & 0x7f) << i*7 277 else: 278 raise Exception("not yet supported location operation (%s:%d:%d)" % 279 (self.member_name, self.member_type, member_offset[0])) 280 else: 281 self.member_offset = member_offset 282 283 def __repr__(self): 284 return "<member %s, type %d, offset %d>" % ( 285 self.member_name, self.member_type, self.member_offset) 286 287 def has_kobject(self): 288 if self.member_type not in type_env: 289 return False 290 291 return type_env[self.member_type].has_kobject() 292 293 def get_kobjects(self, addr): 294 mt = type_env[self.member_type] 295 return mt.get_kobjects(addr + self.member_offset) 296 297 298class ConstType: 299 def __init__(self, child_type): 300 self.child_type = child_type 301 302 def __repr__(self): 303 return "<const %d>" % self.child_type 304 305 def has_kobject(self): 306 if self.child_type not in type_env: 307 return False 308 309 return type_env[self.child_type].has_kobject() 310 311 def get_kobjects(self, addr): 312 return type_env[self.child_type].get_kobjects(addr) 313 314 315class AggregateType: 316 def __init__(self, offset, name, size): 317 self.name = name 318 self.size = size 319 self.offset = offset 320 self.members = [] 321 322 def add_member(self, member): 323 self.members.append(member) 324 325 def __repr__(self): 326 return "<struct %s, with %s>" % (self.name, self.members) 327 328 def has_kobject(self): 329 result = False 330 331 bad_members = [] 332 333 for member in self.members: 334 if member.has_kobject(): 335 result = True 336 else: 337 bad_members.append(member) 338 # Don't need to consider this again, just remove it 339 340 for bad_member in bad_members: 341 self.members.remove(bad_member) 342 343 return result 344 345 def get_kobjects(self, addr): 346 objs = {} 347 for member in self.members: 348 objs.update(member.get_kobjects(addr)) 349 return objs 350 351 352# --- helper functions for getting data from DIEs --- 353 354def die_get_spec(die): 355 if 'DW_AT_specification' not in die.attributes: 356 return None 357 358 spec_val = die.attributes["DW_AT_specification"].value 359 360 # offset of the DW_TAG_variable for the extern declaration 361 offset = spec_val + die.cu.cu_offset 362 363 return extern_env.get(offset) 364 365 366def die_get_name(die): 367 if 'DW_AT_name' not in die.attributes: 368 die = die_get_spec(die) 369 if not die: 370 return None 371 372 return die.attributes["DW_AT_name"].value.decode("utf-8") 373 374 375def die_get_type_offset(die): 376 if 'DW_AT_type' not in die.attributes: 377 die = die_get_spec(die) 378 if not die: 379 return None 380 381 return die.attributes["DW_AT_type"].value + die.cu.cu_offset 382 383 384def die_get_byte_size(die): 385 if 'DW_AT_byte_size' not in die.attributes: 386 return 0 387 388 return die.attributes["DW_AT_byte_size"].value 389 390 391def analyze_die_struct(die): 392 name = die_get_name(die) or "<anon>" 393 offset = die.offset 394 size = die_get_byte_size(die) 395 396 # Incomplete type 397 if not size: 398 return 399 400 if name in kobjects: 401 type_env[offset] = KobjectType(offset, name, size) 402 elif name in subsystems: 403 type_env[offset] = KobjectType(offset, name, size, api=True) 404 elif name in net_sockets: 405 type_env[offset] = KobjectType(offset, "NET_SOCKET", size) 406 else: 407 at = AggregateType(offset, name, size) 408 type_env[offset] = at 409 410 for child in die.iter_children(): 411 if child.tag != "DW_TAG_member": 412 continue 413 data_member_location = child.attributes.get("DW_AT_data_member_location") 414 if not data_member_location: 415 continue 416 417 child_type = die_get_type_offset(child) 418 member_offset = data_member_location.value 419 cname = die_get_name(child) or "<anon>" 420 m = AggregateTypeMember(child.offset, cname, child_type, 421 member_offset) 422 at.add_member(m) 423 424 return 425 426 427def analyze_die_const(die): 428 type_offset = die_get_type_offset(die) 429 if not type_offset: 430 return 431 432 type_env[die.offset] = ConstType(type_offset) 433 434 435def analyze_die_array(die): 436 type_offset = die_get_type_offset(die) 437 elements = [] 438 439 for child in die.iter_children(): 440 if child.tag != "DW_TAG_subrange_type": 441 continue 442 443 if "DW_AT_upper_bound" in child.attributes: 444 ub = child.attributes["DW_AT_upper_bound"] 445 446 if not ub.form.startswith("DW_FORM_data"): 447 continue 448 449 elements.append(ub.value + 1) 450 # in DWARF 4, e.g. ARC Metaware toolchain, DW_AT_count is used 451 # not DW_AT_upper_bound 452 elif "DW_AT_count" in child.attributes: 453 ub = child.attributes["DW_AT_count"] 454 455 if not ub.form.startswith("DW_FORM_data"): 456 continue 457 458 elements.append(ub.value) 459 else: 460 continue 461 462 if not elements: 463 if type_offset in type_env: 464 mt = type_env[type_offset] 465 if mt.has_kobject(): 466 if isinstance(mt, KobjectType) and mt.name == STACK_TYPE: 467 elements.append(1) 468 type_env[die.offset] = ArrayType(die.offset, elements, type_offset) 469 else: 470 type_env[die.offset] = ArrayType(die.offset, elements, type_offset) 471 472 473def analyze_typedef(die): 474 type_offset = die_get_type_offset(die) 475 476 if type_offset not in type_env: 477 return 478 479 type_env[die.offset] = type_env[type_offset] 480 481 482def unpack_pointer(elf, data, offset): 483 endian_code = "<" if elf.little_endian else ">" 484 if elf.elfclass == 32: 485 size_code = "I" 486 size = 4 487 else: 488 size_code = "Q" 489 size = 8 490 491 return struct.unpack(endian_code + size_code, 492 data[offset:offset + size])[0] 493 494 495def addr_deref(elf, addr): 496 for section in elf.iter_sections(): 497 start = section['sh_addr'] 498 end = start + section['sh_size'] 499 500 if start <= addr < end: 501 data = section.data() 502 offset = addr - start 503 return unpack_pointer(elf, data, offset) 504 505 return 0 506 507 508def device_get_api_addr(elf, addr): 509 # See include/device.h for a description of struct device 510 offset = 8 if elf.elfclass == 32 else 16 511 return addr_deref(elf, addr + offset) 512 513 514def find_kobjects(elf, syms): 515 global thread_counter 516 global sys_mutex_counter 517 global futex_counter 518 global stack_counter 519 520 if not elf.has_dwarf_info(): 521 sys.exit("ELF file has no DWARF information") 522 523 app_smem_start = syms["_app_smem_start"] 524 app_smem_end = syms["_app_smem_end"] 525 526 if "CONFIG_LINKER_USE_PINNED_SECTION" in syms and "_app_smem_pinned_start" in syms: 527 app_smem_pinned_start = syms["_app_smem_pinned_start"] 528 app_smem_pinned_end = syms["_app_smem_pinned_end"] 529 else: 530 app_smem_pinned_start = app_smem_start 531 app_smem_pinned_end = app_smem_end 532 533 user_stack_start = syms["z_user_stacks_start"] 534 user_stack_end = syms["z_user_stacks_end"] 535 536 di = elf.get_dwarf_info() 537 538 variables = [] 539 540 # Step 1: collect all type information. 541 for CU in di.iter_CUs(): 542 for die in CU.iter_DIEs(): 543 # Unions are disregarded, kernel objects should never be union 544 # members since the memory is not dedicated to that object and 545 # could be something else 546 if die.tag == "DW_TAG_structure_type": 547 analyze_die_struct(die) 548 elif die.tag == "DW_TAG_const_type": 549 analyze_die_const(die) 550 elif die.tag == "DW_TAG_array_type": 551 analyze_die_array(die) 552 elif die.tag == "DW_TAG_typedef": 553 analyze_typedef(die) 554 elif die.tag == "DW_TAG_variable": 555 variables.append(die) 556 557 # Step 2: filter type_env to only contain kernel objects, or structs 558 # and arrays of kernel objects 559 bad_offsets = [] 560 for offset, type_object in type_env.items(): 561 if not type_object.has_kobject(): 562 bad_offsets.append(offset) 563 564 for offset in bad_offsets: 565 del type_env[offset] 566 567 # Step 3: Now that we know all the types we are looking for, examine 568 # all variables 569 all_objs = {} 570 571 for die in variables: 572 name = die_get_name(die) 573 if not name: 574 continue 575 576 if name.startswith("__init_sys_init"): 577 # Boot-time initialization function; not an actual device 578 continue 579 580 type_offset = die_get_type_offset(die) 581 582 # Is this a kernel object, or a structure containing kernel 583 # objects? 584 if type_offset not in type_env: 585 continue 586 587 if "DW_AT_declaration" in die.attributes: 588 # Extern declaration, only used indirectly 589 extern_env[die.offset] = die 590 continue 591 592 if "DW_AT_location" not in die.attributes: 593 debug_die(die, 594 "No location information for object '%s'; possibly stack allocated" 595 % name) 596 continue 597 598 loc = die.attributes["DW_AT_location"] 599 if loc.form not in ("DW_FORM_exprloc", "DW_FORM_block1"): 600 debug_die(die, "kernel object '%s' unexpected location format" % 601 name) 602 continue 603 604 opcode = loc.value[0] 605 if opcode != DW_OP_addr: 606 607 # Check if frame pointer offset DW_OP_fbreg 608 if opcode == DW_OP_fbreg: 609 debug_die(die, "kernel object '%s' found on stack" % name) 610 else: 611 debug_die(die, 612 "kernel object '%s' unexpected exprloc opcode %s" % 613 (name, hex(opcode))) 614 continue 615 616 if "CONFIG_64BIT" in syms: 617 addr = ((loc.value[1] << 0 ) | (loc.value[2] << 8) | 618 (loc.value[3] << 16) | (loc.value[4] << 24) | 619 (loc.value[5] << 32) | (loc.value[6] << 40) | 620 (loc.value[7] << 48) | (loc.value[8] << 56)) 621 else: 622 addr = ((loc.value[1] << 0 ) | (loc.value[2] << 8) | 623 (loc.value[3] << 16) | (loc.value[4] << 24)) 624 625 if addr == 0: 626 # Never linked; gc-sections deleted it 627 continue 628 629 type_obj = type_env[type_offset] 630 objs = type_obj.get_kobjects(addr) 631 all_objs.update(objs) 632 633 debug("symbol '%s' at %s contains %d object(s)" 634 % (name, hex(addr), len(objs))) 635 636 # Step 4: objs is a dictionary mapping variable memory addresses to 637 # their associated type objects. Now that we have seen all variables 638 # and can properly look up API structs, convert this into a dictionary 639 # mapping variables to the C enumeration of what kernel object type it 640 # is. 641 ret = {} 642 for addr, ko in all_objs.items(): 643 # API structs don't get into the gperf table 644 if ko.type_obj.api: 645 continue 646 647 _, user_ram_allowed, _ = kobjects[ko.type_obj.name] 648 if (not user_ram_allowed and 649 ((app_smem_start <= addr < app_smem_end) 650 or (app_smem_pinned_start <= addr < app_smem_pinned_end))): 651 debug("object '%s' found in invalid location %s" 652 % (ko.type_obj.name, hex(addr))) 653 continue 654 655 if (ko.type_obj.name == STACK_TYPE and 656 (addr < user_stack_start or addr >= user_stack_end)): 657 debug("skip kernel-only stack at %s" % hex(addr)) 658 continue 659 660 # At this point we know the object will be included in the gperf table 661 if ko.type_obj.name == "k_thread": 662 # Assign an ID for this thread object, used to track its 663 # permissions to other kernel objects 664 ko.data = thread_counter 665 thread_counter = thread_counter + 1 666 elif ko.type_obj.name == "sys_mutex": 667 ko.data = "&kernel_mutexes[%d]" % sys_mutex_counter 668 sys_mutex_counter += 1 669 elif ko.type_obj.name == "k_futex": 670 ko.data = "&futex_data[%d]" % futex_counter 671 futex_counter += 1 672 elif ko.type_obj.name == STACK_TYPE: 673 stack_counter += 1 674 675 if ko.type_obj.name != "device": 676 # Not a device struct so we immediately know its type 677 ko.type_name = kobject_to_enum(ko.type_obj.name) 678 ret[addr] = ko 679 continue 680 681 # Device struct. Need to get the address of its API struct, 682 # if it has one. 683 apiaddr = device_get_api_addr(elf, addr) 684 if apiaddr not in all_objs: 685 if apiaddr == 0: 686 debug("device instance at 0x%x has no associated subsystem" 687 % addr) 688 else: 689 debug("device instance at 0x%x has unknown API 0x%x" 690 % (addr, apiaddr)) 691 # API struct does not correspond to a known subsystem, skip it 692 continue 693 694 apiobj = all_objs[apiaddr] 695 ko.type_name = subsystem_to_enum(apiobj.type_obj.name) 696 ret[addr] = ko 697 698 debug("found %d kernel object instances total" % len(ret)) 699 700 # 1. Before python 3.7 dict order is not guaranteed. With Python 701 # 3.5 it doesn't seem random with *integer* keys but can't 702 # rely on that. 703 # 2. OrderedDict means _insertion_ order, so not enough because 704 # built from other (random!) dicts: need to _sort_ first. 705 # 3. Sorting memory address looks good. 706 return OrderedDict(sorted(ret.items())) 707 708def get_symbols(elf): 709 for section in elf.iter_sections(): 710 if isinstance(section, SymbolTableSection): 711 return {sym.name: sym.entry.st_value 712 for sym in section.iter_symbols()} 713 714 raise LookupError("Could not find symbol table") 715 716 717# -- GPERF generation logic 718 719header = """%compare-lengths 720%define lookup-function-name z_object_lookup 721%language=ANSI-C 722%global-table 723%struct-type 724%{ 725#include <zephyr/kernel.h> 726#include <zephyr/toolchain.h> 727#include <zephyr/internal/syscall_handler.h> 728#include <string.h> 729%} 730struct k_object; 731""" 732 733# Different versions of gperf have different prototypes for the lookup 734# function, best to implement the wrapper here. The pointer value itself is 735# turned into a string, we told gperf to expect binary strings that are not 736# NULL-terminated. 737footer = """%% 738struct k_object *z_object_gperf_find(const void *obj) 739{ 740 return z_object_lookup((const char *)obj, sizeof(void *)); 741} 742 743void z_object_gperf_wordlist_foreach(_wordlist_cb_func_t func, void *context) 744{ 745 int i; 746 747 for (i = MIN_HASH_VALUE; i <= MAX_HASH_VALUE; i++) { 748 if (wordlist[i].name != NULL) { 749 func(&wordlist[i], context); 750 } 751 } 752} 753 754#ifndef CONFIG_DYNAMIC_OBJECTS 755struct k_object *k_object_find(const void *obj) 756 ALIAS_OF(z_object_gperf_find); 757 758void k_object_wordlist_foreach(_wordlist_cb_func_t func, void *context) 759 ALIAS_OF(z_object_gperf_wordlist_foreach); 760#endif 761""" 762 763 764def write_gperf_table(fp, syms, objs, little_endian, static_begin, static_end): 765 fp.write(header) 766 if sys_mutex_counter != 0: 767 fp.write("static struct k_mutex kernel_mutexes[%d] = {\n" 768 % sys_mutex_counter) 769 for i in range(sys_mutex_counter): 770 fp.write("Z_MUTEX_INITIALIZER(kernel_mutexes[%d])" % i) 771 if i != sys_mutex_counter - 1: 772 fp.write(", ") 773 fp.write("};\n") 774 775 if futex_counter != 0: 776 fp.write("static struct z_futex_data futex_data[%d] = {\n" 777 % futex_counter) 778 for i in range(futex_counter): 779 fp.write("Z_FUTEX_DATA_INITIALIZER(futex_data[%d])" % i) 780 if i != futex_counter - 1: 781 fp.write(", ") 782 fp.write("};\n") 783 784 metadata_names = { 785 "K_OBJ_THREAD" : "thread_id", 786 "K_OBJ_SYS_MUTEX" : "mutex", 787 "K_OBJ_FUTEX" : "futex_data" 788 } 789 790 if "CONFIG_GEN_PRIV_STACKS" in syms: 791 metadata_names["K_OBJ_THREAD_STACK_ELEMENT"] = "stack_data" 792 if stack_counter != 0: 793 # Same as K_KERNEL_STACK_ARRAY_DEFINE, but routed to a different 794 # memory section. 795 fp.write("static uint8_t Z_GENERIC_SECTION(.priv_stacks.noinit) " 796 " __aligned(Z_KERNEL_STACK_OBJ_ALIGN)" 797 " priv_stacks[%d][Z_KERNEL_STACK_LEN(CONFIG_PRIVILEGED_STACK_SIZE)];\n" 798 % stack_counter) 799 800 fp.write("static const struct z_stack_data stack_data[%d] = {\n" 801 % stack_counter) 802 counter = 0 803 for _, ko in objs.items(): 804 if ko.type_name != "K_OBJ_THREAD_STACK_ELEMENT": 805 continue 806 807 # ko.data currently has the stack size. fetch the value to 808 # populate the appropriate entry in stack_data, and put 809 # a reference to the entry in stack_data into the data value 810 # instead 811 size = ko.data 812 ko.data = "&stack_data[%d]" % counter 813 fp.write("\t{ %d, (uint8_t *)(&priv_stacks[%d]) }" 814 % (size, counter)) 815 if counter != (stack_counter - 1): 816 fp.write(",") 817 fp.write("\n") 818 counter += 1 819 fp.write("};\n") 820 else: 821 metadata_names["K_OBJ_THREAD_STACK_ELEMENT"] = "stack_size" 822 823 fp.write("%%\n") 824 # Setup variables for mapping thread indexes 825 thread_max_bytes = syms["CONFIG_MAX_THREAD_BYTES"] 826 thread_idx_map = {} 827 828 for i in range(0, thread_max_bytes): 829 thread_idx_map[i] = 0xFF 830 831 for obj_addr, ko in objs.items(): 832 obj_type = ko.type_name 833 # pre-initialized objects fall within this memory range, they are 834 # either completely initialized at build time, or done automatically 835 # at boot during some PRE_KERNEL_* phase 836 initialized = static_begin <= obj_addr < static_end 837 is_driver = obj_type.startswith("K_OBJ_DRIVER_") 838 839 if "CONFIG_64BIT" in syms: 840 format_code = "Q" 841 else: 842 format_code = "I" 843 844 if little_endian: 845 endian = "<" 846 else: 847 endian = ">" 848 849 byte_str = struct.pack(endian + format_code, obj_addr) 850 fp.write("\"") 851 for byte in byte_str: 852 val = "\\x%02x" % byte 853 fp.write(val) 854 855 flags = "0" 856 if initialized: 857 flags += " | K_OBJ_FLAG_INITIALIZED" 858 if is_driver: 859 flags += " | K_OBJ_FLAG_DRIVER" 860 861 if ko.type_name in metadata_names: 862 tname = metadata_names[ko.type_name] 863 else: 864 tname = "unused" 865 866 fp.write("\", {0}, %s, %s, { .%s = %s }\n" % (obj_type, flags, 867 tname, str(ko.data))) 868 869 if obj_type == "K_OBJ_THREAD": 870 idx = math.floor(ko.data / 8) 871 bit = ko.data % 8 872 thread_idx_map[idx] = thread_idx_map[idx] & ~(2**bit) 873 874 fp.write(footer) 875 876 # Generate the array of already mapped thread indexes 877 fp.write('\n') 878 fp.write('Z_GENERIC_DOT_SECTION(data)\n') 879 fp.write('uint8_t _thread_idx_map[%d] = {' % (thread_max_bytes)) 880 881 for i in range(0, thread_max_bytes): 882 fp.write(' 0x%x, ' % (thread_idx_map[i])) 883 884 fp.write('};\n') 885 886 887driver_macro_tpl = """ 888#define K_SYSCALL_DRIVER_%(driver_upper)s(ptr, op) K_SYSCALL_DRIVER_GEN(ptr, op, %(driver_lower)s, %(driver_upper)s) 889""" 890 891 892def write_validation_output(fp): 893 fp.write("#ifndef DRIVER_VALIDATION_GEN_H\n") 894 fp.write("#define DRIVER_VALIDATION_GEN_H\n") 895 896 fp.write("""#define K_SYSCALL_DRIVER_GEN(ptr, op, driver_lower_case, driver_upper_case) \\ 897 (K_SYSCALL_OBJ(ptr, K_OBJ_DRIVER_##driver_upper_case) || \\ 898 K_SYSCALL_DRIVER_OP(ptr, driver_lower_case##_driver_api, op)) 899 """) 900 901 for subsystem in subsystems: 902 subsystem = subsystem.replace("_driver_api", "") 903 904 fp.write(driver_macro_tpl % { 905 "driver_lower": subsystem.lower(), 906 "driver_upper": subsystem.upper(), 907 }) 908 909 fp.write("#endif /* DRIVER_VALIDATION_GEN_H */\n") 910 911 912def write_kobj_types_output(fp): 913 fp.write("/* Core kernel objects */\n") 914 for kobj, obj_info in kobjects.items(): 915 dep, _, _ = obj_info 916 if kobj == "device": 917 continue 918 919 if dep: 920 fp.write("#ifdef %s\n" % dep) 921 922 fp.write("%s,\n" % kobject_to_enum(kobj)) 923 924 if dep: 925 fp.write("#endif\n") 926 927 fp.write("/* Driver subsystems */\n") 928 for subsystem in subsystems: 929 subsystem = subsystem.replace("_driver_api", "").upper() 930 fp.write("K_OBJ_DRIVER_%s,\n" % subsystem) 931 932 933def write_kobj_otype_output(fp): 934 fp.write("/* Core kernel objects */\n") 935 for kobj, obj_info in kobjects.items(): 936 dep, _, _ = obj_info 937 if kobj == "device": 938 continue 939 940 if dep: 941 fp.write("#ifdef %s\n" % dep) 942 943 fp.write('case %s: ret = "%s"; break;\n' % 944 (kobject_to_enum(kobj), kobj)) 945 if dep: 946 fp.write("#endif\n") 947 948 fp.write("/* Driver subsystems */\n") 949 for subsystem in subsystems: 950 subsystem = subsystem.replace("_driver_api", "") 951 fp.write('case K_OBJ_DRIVER_%s: ret = "%s driver"; break;\n' % ( 952 subsystem.upper(), 953 subsystem 954 )) 955 956 957def write_kobj_size_output(fp): 958 fp.write("/* Non device/stack objects */\n") 959 for kobj, obj_info in kobjects.items(): 960 dep, _, alloc = obj_info 961 962 if not alloc: 963 continue 964 965 if dep: 966 fp.write("#ifdef %s\n" % dep) 967 968 fp.write('case %s: ret = sizeof(struct %s); break;\n' % 969 (kobject_to_enum(kobj), kobj)) 970 if dep: 971 fp.write("#endif\n") 972 973 974def parse_subsystems_list_file(path): 975 with open(path, "r") as fp: 976 subsys_list = json.load(fp) 977 subsystems.extend(subsys_list["__subsystem"]) 978 net_sockets.extend(subsys_list["__net_socket"]) 979 980def parse_args(): 981 global args 982 983 parser = argparse.ArgumentParser( 984 description=__doc__, 985 formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) 986 987 parser.add_argument("-k", "--kernel", required=False, 988 help="Input zephyr ELF binary") 989 parser.add_argument( 990 "-g", "--gperf-output", required=False, 991 help="Output list of kernel object addresses for gperf use") 992 parser.add_argument( 993 "-V", "--validation-output", required=False, 994 help="Output driver validation macros") 995 parser.add_argument( 996 "-K", "--kobj-types-output", required=False, 997 help="Output k_object enum constants") 998 parser.add_argument( 999 "-S", "--kobj-otype-output", required=False, 1000 help="Output case statements for otype_to_str()") 1001 parser.add_argument( 1002 "-Z", "--kobj-size-output", required=False, 1003 help="Output case statements for obj_size_get()") 1004 parser.add_argument("-i", "--include-subsystem-list", required=False, action='append', 1005 help='''Specifies a file with a JSON encoded list of subsystem names to append to 1006 the driver subsystems list. Can be specified multiple times: 1007 -i file1 -i file2 ...''') 1008 1009 parser.add_argument("-v", "--verbose", action="store_true", 1010 help="Print extra debugging information") 1011 args = parser.parse_args() 1012 if "VERBOSE" in os.environ: 1013 args.verbose = 1 1014 1015 1016def main(): 1017 parse_args() 1018 1019 if args.include_subsystem_list is not None: 1020 for list_file in args.include_subsystem_list: 1021 parse_subsystems_list_file(list_file) 1022 1023 if args.gperf_output: 1024 assert args.kernel, "--kernel ELF required for --gperf-output" 1025 elf = ELFFile(open(args.kernel, "rb")) 1026 syms = get_symbols(elf) 1027 max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8 1028 objs = find_kobjects(elf, syms) 1029 if not objs: 1030 sys.stderr.write("WARNING: zero kobject found in %s\n" 1031 % args.kernel) 1032 1033 if thread_counter > max_threads: 1034 sys.exit("Too many thread objects ({})\n" 1035 "Increase CONFIG_MAX_THREAD_BYTES to {}" 1036 .format(thread_counter, -(-thread_counter // 8))) 1037 1038 with open(args.gperf_output, "w") as fp: 1039 write_gperf_table(fp, syms, objs, elf.little_endian, 1040 syms["_static_kernel_objects_begin"], 1041 syms["_static_kernel_objects_end"]) 1042 1043 if args.validation_output: 1044 with open(args.validation_output, "w") as fp: 1045 write_validation_output(fp) 1046 1047 if args.kobj_types_output: 1048 with open(args.kobj_types_output, "w") as fp: 1049 write_kobj_types_output(fp) 1050 1051 if args.kobj_otype_output: 1052 with open(args.kobj_otype_output, "w") as fp: 1053 write_kobj_otype_output(fp) 1054 1055 if args.kobj_size_output: 1056 with open(args.kobj_size_output, "w") as fp: 1057 write_kobj_size_output(fp) 1058 1059 1060if __name__ == "__main__": 1061 main() 1062