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