123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053 |
- #!/usr/bin/env python3
- #
- # Copyright (c) 2017 Intel Corporation
- #
- # SPDX-License-Identifier: Apache-2.0
- """
- Script to generate gperf tables of kernel object metadata
- User mode threads making system calls reference kernel objects by memory
- address, as the kernel/driver APIs in Zephyr are the same for both user
- and supervisor contexts. It is necessary for the kernel to be able to
- validate accesses to kernel objects to make the following assertions:
- - That the memory address points to a kernel object
- - The kernel object is of the expected type for the API being invoked
- - The kernel object is of the expected initialization state
- - The calling thread has sufficient permissions on the object
- For more details see the :ref:`kernelobjects` section in the documentation.
- The zephyr build generates an intermediate ELF binary, zephyr_prebuilt.elf,
- which this script scans looking for kernel objects by examining the DWARF
- debug information to look for instances of data structures that are considered
- kernel objects. For device drivers, the API struct pointer populated at build
- time is also examined to disambiguate between various device driver instances
- since they are all 'struct device'.
- This script can generate five different output files:
- - A gperf script to generate the hash table mapping kernel object memory
- addresses to kernel object metadata, used to track permissions,
- object type, initialization state, and any object-specific data.
- - A header file containing generated macros for validating driver instances
- inside the system call handlers for the driver subsystem APIs.
- - A code fragment included by kernel.h with one enum constant for
- each kernel object type and each driver instance.
- - The inner cases of a switch/case C statement, included by
- kernel/userspace.c, mapping the kernel object types and driver
- instances to their human-readable representation in the
- otype_to_str() function.
- - The inner cases of a switch/case C statement, included by
- kernel/userspace.c, mapping kernel object types to their sizes.
- This is used for allocating instances of them at runtime
- (CONFIG_DYNAMIC_OBJECTS) in the obj_size_get() function.
- """
- import sys
- import argparse
- import math
- import os
- import struct
- import json
- from distutils.version import LooseVersion
- import elftools
- from elftools.elf.elffile import ELFFile
- from elftools.elf.sections import SymbolTableSection
- if LooseVersion(elftools.__version__) < LooseVersion('0.24'):
- sys.exit("pyelftools is out of date, need version 0.24 or later")
- from collections import OrderedDict
- # Keys in this dictionary are structs which should be recognized as kernel
- # objects. Values are a tuple:
- #
- # - The first item is None, or the name of a Kconfig that
- # indicates the presence of this object's definition in case it is not
- # available in all configurations.
- #
- # - The second item is a boolean indicating whether it is permissible for
- # the object to be located in user-accessible memory.
- #
- # - The third items is a boolean indicating whether this item can be
- # dynamically allocated with k_object_alloc(). Keep this in sync with
- # the switch statement in z_impl_k_object_alloc().
- #
- # Key names in all caps do not correspond to a specific data type but instead
- # indicate that objects of its type are of a family of compatible data
- # structures
- # Regular dictionaries are ordered only with Python 3.6 and
- # above. Good summary and pointers to official documents at:
- # https://stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6
- kobjects = OrderedDict([
- ("k_mem_slab", (None, False, True)),
- ("k_msgq", (None, False, True)),
- ("k_mutex", (None, False, True)),
- ("k_pipe", (None, False, True)),
- ("k_queue", (None, False, True)),
- ("k_poll_signal", (None, False, True)),
- ("k_sem", (None, False, True)),
- ("k_stack", (None, False, True)),
- ("k_thread", (None, False, True)), # But see #
- ("k_timer", (None, False, True)),
- ("z_thread_stack_element", (None, False, False)),
- ("device", (None, False, False)),
- ("NET_SOCKET", (None, False, False)),
- ("net_if", (None, False, False)),
- ("sys_mutex", (None, True, False)),
- ("k_futex", (None, True, False)),
- ("k_condvar", (None, False, True))
- ])
- def kobject_to_enum(kobj):
- if kobj.startswith("k_") or kobj.startswith("z_"):
- name = kobj[2:]
- else:
- name = kobj
- return "K_OBJ_%s" % name.upper()
- subsystems = [
- # Editing the list is deprecated, add the __subsystem sentinal to your driver
- # api declaration instead. e.x.
- #
- # __subsystem struct my_driver_api {
- # ....
- #};
- ]
- # Names of all structs tagged with __net_socket, found by parse_syscalls.py
- net_sockets = [ ]
- def subsystem_to_enum(subsys):
- return "K_OBJ_DRIVER_" + subsys[:-11].upper()
- # --- debug stuff ---
- scr = os.path.basename(sys.argv[0])
- def debug(text):
- if not args.verbose:
- return
- sys.stdout.write(scr + ": " + text + "\n")
- def error(text):
- sys.exit("%s ERROR: %s" % (scr, text))
- def debug_die(die, text):
- lp_header = die.dwarfinfo.line_program_for_CU(die.cu).header
- files = lp_header["file_entry"]
- includes = lp_header["include_directory"]
- fileinfo = files[die.attributes["DW_AT_decl_file"].value - 1]
- filename = fileinfo.name.decode("utf-8")
- filedir = includes[fileinfo.dir_index - 1].decode("utf-8")
- path = os.path.join(filedir, filename)
- lineno = die.attributes["DW_AT_decl_line"].value
- debug(str(die))
- debug("File '%s', line %d:" % (path, lineno))
- debug(" %s" % text)
- # -- ELF processing
- DW_OP_addr = 0x3
- DW_OP_fbreg = 0x91
- STACK_TYPE = "z_thread_stack_element"
- thread_counter = 0
- sys_mutex_counter = 0
- futex_counter = 0
- stack_counter = 0
- # Global type environment. Populated by pass 1.
- type_env = {}
- extern_env = {}
- class KobjectInstance:
- def __init__(self, type_obj, addr):
- self.addr = addr
- self.type_obj = type_obj
- # Type name determined later since drivers needs to look at the
- # API struct address
- self.type_name = None
- self.data = 0
- class KobjectType:
- def __init__(self, offset, name, size, api=False):
- self.name = name
- self.size = size
- self.offset = offset
- self.api = api
- def __repr__(self):
- return "<kobject %s>" % self.name
- @staticmethod
- def has_kobject():
- return True
- def get_kobjects(self, addr):
- return {addr: KobjectInstance(self, addr)}
- class ArrayType:
- def __init__(self, offset, elements, member_type):
- self.elements = elements
- self.member_type = member_type
- self.offset = offset
- def __repr__(self):
- return "<array of %d>" % self.member_type
- def has_kobject(self):
- if self.member_type not in type_env:
- return False
- return type_env[self.member_type].has_kobject()
- def get_kobjects(self, addr):
- mt = type_env[self.member_type]
- # Stacks are arrays of _k_stack_element_t but we want to treat
- # the whole array as one kernel object (a thread stack)
- # Data value gets set to size of entire region
- if isinstance(mt, KobjectType) and mt.name == STACK_TYPE:
- # An array of stacks appears as a multi-dimensional array.
- # The last size is the size of each stack. We need to track
- # each stack within the array, not as one huge stack object.
- *dimensions, stacksize = self.elements
- num_members = 1
- for e in dimensions:
- num_members = num_members * e
- ret = {}
- for i in range(num_members):
- a = addr + (i * stacksize)
- o = mt.get_kobjects(a)
- o[a].data = stacksize
- ret.update(o)
- return ret
- objs = {}
- # Multidimensional array flattened out
- num_members = 1
- for e in self.elements:
- num_members = num_members * e
- for i in range(num_members):
- objs.update(mt.get_kobjects(addr + (i * mt.size)))
- return objs
- class AggregateTypeMember:
- def __init__(self, offset, member_name, member_type, member_offset):
- self.member_name = member_name
- self.member_type = member_type
- if isinstance(member_offset, list):
- # DWARF v2, location encoded as set of operations
- # only "DW_OP_plus_uconst" with ULEB128 argument supported
- if member_offset[0] == 0x23:
- self.member_offset = member_offset[1] & 0x7f
- for i in range(1, len(member_offset)-1):
- if member_offset[i] & 0x80:
- self.member_offset += (
- member_offset[i+1] & 0x7f) << i*7
- else:
- raise Exception("not yet supported location operation (%s:%d:%d)" %
- (self.member_name, self.member_type, member_offset[0]))
- else:
- self.member_offset = member_offset
- def __repr__(self):
- return "<member %s, type %d, offset %d>" % (
- self.member_name, self.member_type, self.member_offset)
- def has_kobject(self):
- if self.member_type not in type_env:
- return False
- return type_env[self.member_type].has_kobject()
- def get_kobjects(self, addr):
- mt = type_env[self.member_type]
- return mt.get_kobjects(addr + self.member_offset)
- class ConstType:
- def __init__(self, child_type):
- self.child_type = child_type
- def __repr__(self):
- return "<const %d>" % self.child_type
- def has_kobject(self):
- if self.child_type not in type_env:
- return False
- return type_env[self.child_type].has_kobject()
- def get_kobjects(self, addr):
- return type_env[self.child_type].get_kobjects(addr)
- class AggregateType:
- def __init__(self, offset, name, size):
- self.name = name
- self.size = size
- self.offset = offset
- self.members = []
- def add_member(self, member):
- self.members.append(member)
- def __repr__(self):
- return "<struct %s, with %s>" % (self.name, self.members)
- def has_kobject(self):
- result = False
- bad_members = []
- for member in self.members:
- if member.has_kobject():
- result = True
- else:
- bad_members.append(member)
- # Don't need to consider this again, just remove it
- for bad_member in bad_members:
- self.members.remove(bad_member)
- return result
- def get_kobjects(self, addr):
- objs = {}
- for member in self.members:
- objs.update(member.get_kobjects(addr))
- return objs
- # --- helper functions for getting data from DIEs ---
- def die_get_spec(die):
- if 'DW_AT_specification' not in die.attributes:
- return None
- spec_val = die.attributes["DW_AT_specification"].value
- # offset of the DW_TAG_variable for the extern declaration
- offset = spec_val + die.cu.cu_offset
- return extern_env.get(offset)
- def die_get_name(die):
- if 'DW_AT_name' not in die.attributes:
- die = die_get_spec(die)
- if not die:
- return None
- return die.attributes["DW_AT_name"].value.decode("utf-8")
- def die_get_type_offset(die):
- if 'DW_AT_type' not in die.attributes:
- die = die_get_spec(die)
- if not die:
- return None
- return die.attributes["DW_AT_type"].value + die.cu.cu_offset
- def die_get_byte_size(die):
- if 'DW_AT_byte_size' not in die.attributes:
- return 0
- return die.attributes["DW_AT_byte_size"].value
- def analyze_die_struct(die):
- name = die_get_name(die) or "<anon>"
- offset = die.offset
- size = die_get_byte_size(die)
- # Incomplete type
- if not size:
- return
- if name in kobjects:
- type_env[offset] = KobjectType(offset, name, size)
- elif name in subsystems:
- type_env[offset] = KobjectType(offset, name, size, api=True)
- elif name in net_sockets:
- type_env[offset] = KobjectType(offset, "NET_SOCKET", size)
- else:
- at = AggregateType(offset, name, size)
- type_env[offset] = at
- for child in die.iter_children():
- if child.tag != "DW_TAG_member":
- continue
- data_member_location = child.attributes.get("DW_AT_data_member_location")
- if not data_member_location:
- continue
- child_type = die_get_type_offset(child)
- member_offset = data_member_location.value
- cname = die_get_name(child) or "<anon>"
- m = AggregateTypeMember(child.offset, cname, child_type,
- member_offset)
- at.add_member(m)
- return
- def analyze_die_const(die):
- type_offset = die_get_type_offset(die)
- if not type_offset:
- return
- type_env[die.offset] = ConstType(type_offset)
- def analyze_die_array(die):
- type_offset = die_get_type_offset(die)
- elements = []
- for child in die.iter_children():
- if child.tag != "DW_TAG_subrange_type":
- continue
- if "DW_AT_upper_bound" in child.attributes:
- ub = child.attributes["DW_AT_upper_bound"]
- if not ub.form.startswith("DW_FORM_data"):
- continue
- elements.append(ub.value + 1)
- # in DWARF 4, e.g. ARC Metaware toolchain, DW_AT_count is used
- # not DW_AT_upper_bound
- elif "DW_AT_count" in child.attributes:
- ub = child.attributes["DW_AT_count"]
- if not ub.form.startswith("DW_FORM_data"):
- continue
- elements.append(ub.value)
- else:
- continue
- if not elements:
- if type_offset in type_env.keys():
- mt = type_env[type_offset]
- if mt.has_kobject():
- if isinstance(mt, KobjectType) and mt.name == STACK_TYPE:
- elements.append(1)
- type_env[die.offset] = ArrayType(die.offset, elements, type_offset)
- else:
- type_env[die.offset] = ArrayType(die.offset, elements, type_offset)
- def analyze_typedef(die):
- type_offset = die_get_type_offset(die)
- if type_offset not in type_env.keys():
- return
- type_env[die.offset] = type_env[type_offset]
- def unpack_pointer(elf, data, offset):
- endian_code = "<" if elf.little_endian else ">"
- if elf.elfclass == 32:
- size_code = "I"
- size = 4
- else:
- size_code = "Q"
- size = 8
- return struct.unpack(endian_code + size_code,
- data[offset:offset + size])[0]
- def addr_deref(elf, addr):
- for section in elf.iter_sections():
- start = section['sh_addr']
- end = start + section['sh_size']
- if start <= addr < end:
- data = section.data()
- offset = addr - start
- return unpack_pointer(elf, data, offset)
- return 0
- def device_get_api_addr(elf, addr):
- # See include/device.h for a description of struct device
- offset = 8 if elf.elfclass == 32 else 16
- return addr_deref(elf, addr + offset)
- def find_kobjects(elf, syms):
- global thread_counter
- global sys_mutex_counter
- global futex_counter
- global stack_counter
- if not elf.has_dwarf_info():
- sys.exit("ELF file has no DWARF information")
- app_smem_start = syms["_app_smem_start"]
- app_smem_end = syms["_app_smem_end"]
- if "CONFIG_LINKER_USE_PINNED_SECTION" in syms and "_app_smem_pinned_start" in syms:
- app_smem_pinned_start = syms["_app_smem_pinned_start"]
- app_smem_pinned_end = syms["_app_smem_pinned_end"]
- else:
- app_smem_pinned_start = app_smem_start
- app_smem_pinned_end = app_smem_end
- user_stack_start = syms["z_user_stacks_start"]
- user_stack_end = syms["z_user_stacks_end"]
- di = elf.get_dwarf_info()
- variables = []
- # Step 1: collect all type information.
- for CU in di.iter_CUs():
- for die in CU.iter_DIEs():
- # Unions are disregarded, kernel objects should never be union
- # members since the memory is not dedicated to that object and
- # could be something else
- if die.tag == "DW_TAG_structure_type":
- analyze_die_struct(die)
- elif die.tag == "DW_TAG_const_type":
- analyze_die_const(die)
- elif die.tag == "DW_TAG_array_type":
- analyze_die_array(die)
- elif die.tag == "DW_TAG_typedef":
- analyze_typedef(die)
- elif die.tag == "DW_TAG_variable":
- variables.append(die)
- # Step 2: filter type_env to only contain kernel objects, or structs
- # and arrays of kernel objects
- bad_offsets = []
- for offset, type_object in type_env.items():
- if not type_object.has_kobject():
- bad_offsets.append(offset)
- for offset in bad_offsets:
- del type_env[offset]
- # Step 3: Now that we know all the types we are looking for, examine
- # all variables
- all_objs = {}
- for die in variables:
- name = die_get_name(die)
- if not name:
- continue
- if name.startswith("__init_sys_init"):
- # Boot-time initialization function; not an actual device
- continue
- type_offset = die_get_type_offset(die)
- # Is this a kernel object, or a structure containing kernel
- # objects?
- if type_offset not in type_env:
- continue
- if "DW_AT_declaration" in die.attributes:
- # Extern declaration, only used indirectly
- extern_env[die.offset] = die
- continue
- if "DW_AT_location" not in die.attributes:
- debug_die(die,
- "No location information for object '%s'; possibly stack allocated"
- % name)
- continue
- loc = die.attributes["DW_AT_location"]
- if loc.form != "DW_FORM_exprloc" and \
- loc.form != "DW_FORM_block1":
- debug_die(die, "kernel object '%s' unexpected location format" %
- name)
- continue
- opcode = loc.value[0]
- if opcode != DW_OP_addr:
- # Check if frame pointer offset DW_OP_fbreg
- if opcode == DW_OP_fbreg:
- debug_die(die, "kernel object '%s' found on stack" % name)
- else:
- debug_die(die,
- "kernel object '%s' unexpected exprloc opcode %s" %
- (name, hex(opcode)))
- continue
- if "CONFIG_64BIT" in syms:
- addr = ((loc.value[1] << 0 ) | (loc.value[2] << 8) |
- (loc.value[3] << 16) | (loc.value[4] << 24) |
- (loc.value[5] << 32) | (loc.value[6] << 40) |
- (loc.value[7] << 48) | (loc.value[8] << 56))
- else:
- addr = ((loc.value[1] << 0 ) | (loc.value[2] << 8) |
- (loc.value[3] << 16) | (loc.value[4] << 24))
- if addr == 0:
- # Never linked; gc-sections deleted it
- continue
- type_obj = type_env[type_offset]
- objs = type_obj.get_kobjects(addr)
- all_objs.update(objs)
- debug("symbol '%s' at %s contains %d object(s)"
- % (name, hex(addr), len(objs)))
- # Step 4: objs is a dictionary mapping variable memory addresses to
- # their associated type objects. Now that we have seen all variables
- # and can properly look up API structs, convert this into a dictionary
- # mapping variables to the C enumeration of what kernel object type it
- # is.
- ret = {}
- for addr, ko in all_objs.items():
- # API structs don't get into the gperf table
- if ko.type_obj.api:
- continue
- _, user_ram_allowed, _ = kobjects[ko.type_obj.name]
- if (not user_ram_allowed and
- ((app_smem_start <= addr < app_smem_end)
- or (app_smem_pinned_start <= addr < app_smem_pinned_end))):
- debug("object '%s' found in invalid location %s"
- % (ko.type_obj.name, hex(addr)))
- continue
- if (ko.type_obj.name == STACK_TYPE and
- (addr < user_stack_start or addr >= user_stack_end)):
- debug("skip kernel-only stack at %s" % hex(addr))
- continue
- # At this point we know the object will be included in the gperf table
- if ko.type_obj.name == "k_thread":
- # Assign an ID for this thread object, used to track its
- # permissions to other kernel objects
- ko.data = thread_counter
- thread_counter = thread_counter + 1
- elif ko.type_obj.name == "sys_mutex":
- ko.data = "&kernel_mutexes[%d]" % sys_mutex_counter
- sys_mutex_counter += 1
- elif ko.type_obj.name == "k_futex":
- ko.data = "&futex_data[%d]" % futex_counter
- futex_counter += 1
- elif ko.type_obj.name == STACK_TYPE:
- stack_counter += 1
- if ko.type_obj.name != "device":
- # Not a device struct so we immediately know its type
- ko.type_name = kobject_to_enum(ko.type_obj.name)
- ret[addr] = ko
- continue
- # Device struct. Need to get the address of its API struct,
- # if it has one.
- apiaddr = device_get_api_addr(elf, addr)
- if apiaddr not in all_objs:
- if apiaddr == 0:
- debug("device instance at 0x%x has no associated subsystem"
- % addr)
- else:
- debug("device instance at 0x%x has unknown API 0x%x"
- % (addr, apiaddr))
- # API struct does not correspond to a known subsystem, skip it
- continue
- apiobj = all_objs[apiaddr]
- ko.type_name = subsystem_to_enum(apiobj.type_obj.name)
- ret[addr] = ko
- debug("found %d kernel object instances total" % len(ret))
- # 1. Before python 3.7 dict order is not guaranteed. With Python
- # 3.5 it doesn't seem random with *integer* keys but can't
- # rely on that.
- # 2. OrderedDict means _insertion_ order, so not enough because
- # built from other (random!) dicts: need to _sort_ first.
- # 3. Sorting memory address looks good.
- return OrderedDict(sorted(ret.items()))
- def get_symbols(elf):
- for section in elf.iter_sections():
- if isinstance(section, SymbolTableSection):
- return {sym.name: sym.entry.st_value
- for sym in section.iter_symbols()}
- raise LookupError("Could not find symbol table")
- # -- GPERF generation logic
- header = """%compare-lengths
- %define lookup-function-name z_object_lookup
- %language=ANSI-C
- %global-table
- %struct-type
- %{
- #include <kernel.h>
- #include <toolchain.h>
- #include <syscall_handler.h>
- #include <string.h>
- %}
- struct z_object;
- """
- # Different versions of gperf have different prototypes for the lookup
- # function, best to implement the wrapper here. The pointer value itself is
- # turned into a string, we told gperf to expect binary strings that are not
- # NULL-terminated.
- footer = """%%
- struct z_object *z_object_gperf_find(const void *obj)
- {
- return z_object_lookup((const char *)obj, sizeof(void *));
- }
- void z_object_gperf_wordlist_foreach(_wordlist_cb_func_t func, void *context)
- {
- int i;
- for (i = MIN_HASH_VALUE; i <= MAX_HASH_VALUE; i++) {
- if (wordlist[i].name != NULL) {
- func(&wordlist[i], context);
- }
- }
- }
- #ifndef CONFIG_DYNAMIC_OBJECTS
- struct z_object *z_object_find(const void *obj)
- ALIAS_OF(z_object_gperf_find);
- void z_object_wordlist_foreach(_wordlist_cb_func_t func, void *context)
- ALIAS_OF(z_object_gperf_wordlist_foreach);
- #endif
- """
- def write_gperf_table(fp, syms, objs, little_endian, static_begin, static_end):
- fp.write(header)
- if sys_mutex_counter != 0:
- fp.write("static struct k_mutex kernel_mutexes[%d] = {\n"
- % sys_mutex_counter)
- for i in range(sys_mutex_counter):
- fp.write("Z_MUTEX_INITIALIZER(kernel_mutexes[%d])" % i)
- if i != sys_mutex_counter - 1:
- fp.write(", ")
- fp.write("};\n")
- if futex_counter != 0:
- fp.write("static struct z_futex_data futex_data[%d] = {\n"
- % futex_counter)
- for i in range(futex_counter):
- fp.write("Z_FUTEX_DATA_INITIALIZER(futex_data[%d])" % i)
- if i != futex_counter - 1:
- fp.write(", ")
- fp.write("};\n")
- metadata_names = {
- "K_OBJ_THREAD" : "thread_id",
- "K_OBJ_SYS_MUTEX" : "mutex",
- "K_OBJ_FUTEX" : "futex_data"
- }
- if "CONFIG_GEN_PRIV_STACKS" in syms:
- metadata_names["K_OBJ_THREAD_STACK_ELEMENT"] = "stack_data"
- if stack_counter != 0:
- # Same as K_KERNEL_STACK_ARRAY_DEFINE, but routed to a different
- # memory section.
- fp.write("static uint8_t Z_GENERIC_SECTION(.priv_stacks.noinit) "
- " __aligned(Z_KERNEL_STACK_OBJ_ALIGN)"
- " priv_stacks[%d][Z_KERNEL_STACK_LEN(CONFIG_PRIVILEGED_STACK_SIZE)];\n"
- % stack_counter)
- fp.write("static const struct z_stack_data stack_data[%d] = {\n"
- % stack_counter)
- counter = 0
- for _, ko in objs.items():
- if ko.type_name != "K_OBJ_THREAD_STACK_ELEMENT":
- continue
- # ko.data currently has the stack size. fetch the value to
- # populate the appropriate entry in stack_data, and put
- # a reference to the entry in stack_data into the data value
- # instead
- size = ko.data
- ko.data = "&stack_data[%d]" % counter
- fp.write("\t{ %d, (uint8_t *)(&priv_stacks[%d]) }"
- % (size, counter))
- if counter != (stack_counter - 1):
- fp.write(",")
- fp.write("\n")
- counter += 1
- fp.write("};\n")
- else:
- metadata_names["K_OBJ_THREAD_STACK_ELEMENT"] = "stack_size"
- fp.write("%%\n")
- # Setup variables for mapping thread indexes
- thread_max_bytes = syms["CONFIG_MAX_THREAD_BYTES"]
- thread_idx_map = {}
- for i in range(0, thread_max_bytes):
- thread_idx_map[i] = 0xFF
- for obj_addr, ko in objs.items():
- obj_type = ko.type_name
- # pre-initialized objects fall within this memory range, they are
- # either completely initialized at build time, or done automatically
- # at boot during some PRE_KERNEL_* phase
- initialized = static_begin <= obj_addr < static_end
- is_driver = obj_type.startswith("K_OBJ_DRIVER_")
- if "CONFIG_64BIT" in syms:
- format_code = "Q"
- else:
- format_code = "I"
- if little_endian:
- endian = "<"
- else:
- endian = ">"
- byte_str = struct.pack(endian + format_code, obj_addr)
- fp.write("\"")
- for byte in byte_str:
- val = "\\x%02x" % byte
- fp.write(val)
- flags = "0"
- if initialized:
- flags += " | K_OBJ_FLAG_INITIALIZED"
- if is_driver:
- flags += " | K_OBJ_FLAG_DRIVER"
- if ko.type_name in metadata_names:
- tname = metadata_names[ko.type_name]
- else:
- tname = "unused"
- fp.write("\", {}, %s, %s, { .%s = %s }\n" % (obj_type, flags,
- tname, str(ko.data)))
- if obj_type == "K_OBJ_THREAD":
- idx = math.floor(ko.data / 8)
- bit = ko.data % 8
- thread_idx_map[idx] = thread_idx_map[idx] & ~(2**bit)
- fp.write(footer)
- # Generate the array of already mapped thread indexes
- fp.write('\n')
- fp.write('uint8_t _thread_idx_map[%d] = {' % (thread_max_bytes))
- for i in range(0, thread_max_bytes):
- fp.write(' 0x%x, ' % (thread_idx_map[i]))
- fp.write('};\n')
- driver_macro_tpl = """
- #define Z_SYSCALL_DRIVER_%(driver_upper)s(ptr, op) Z_SYSCALL_DRIVER_GEN(ptr, op, %(driver_lower)s, %(driver_upper)s)
- """
- def write_validation_output(fp):
- fp.write("#ifndef DRIVER_VALIDATION_GEN_H\n")
- fp.write("#define DRIVER_VALIDATION_GEN_H\n")
- fp.write("""#define Z_SYSCALL_DRIVER_GEN(ptr, op, driver_lower_case, driver_upper_case) \\
- (Z_SYSCALL_OBJ(ptr, K_OBJ_DRIVER_##driver_upper_case) || \\
- Z_SYSCALL_DRIVER_OP(ptr, driver_lower_case##_driver_api, op))
- """)
- for subsystem in subsystems:
- subsystem = subsystem.replace("_driver_api", "")
- fp.write(driver_macro_tpl % {
- "driver_lower": subsystem.lower(),
- "driver_upper": subsystem.upper(),
- })
- fp.write("#endif /* DRIVER_VALIDATION_GEN_H */\n")
- def write_kobj_types_output(fp):
- fp.write("/* Core kernel objects */\n")
- for kobj, obj_info in kobjects.items():
- dep, _, _ = obj_info
- if kobj == "device":
- continue
- if dep:
- fp.write("#ifdef %s\n" % dep)
- fp.write("%s,\n" % kobject_to_enum(kobj))
- if dep:
- fp.write("#endif\n")
- fp.write("/* Driver subsystems */\n")
- for subsystem in subsystems:
- subsystem = subsystem.replace("_driver_api", "").upper()
- fp.write("K_OBJ_DRIVER_%s,\n" % subsystem)
- def write_kobj_otype_output(fp):
- fp.write("/* Core kernel objects */\n")
- for kobj, obj_info in kobjects.items():
- dep, _, _ = obj_info
- if kobj == "device":
- continue
- if dep:
- fp.write("#ifdef %s\n" % dep)
- fp.write('case %s: ret = "%s"; break;\n' %
- (kobject_to_enum(kobj), kobj))
- if dep:
- fp.write("#endif\n")
- fp.write("/* Driver subsystems */\n")
- for subsystem in subsystems:
- subsystem = subsystem.replace("_driver_api", "")
- fp.write('case K_OBJ_DRIVER_%s: ret = "%s driver"; break;\n' % (
- subsystem.upper(),
- subsystem
- ))
- def write_kobj_size_output(fp):
- fp.write("/* Non device/stack objects */\n")
- for kobj, obj_info in kobjects.items():
- dep, _, alloc = obj_info
- if not alloc:
- continue
- if dep:
- fp.write("#ifdef %s\n" % dep)
- fp.write('case %s: ret = sizeof(struct %s); break;\n' %
- (kobject_to_enum(kobj), kobj))
- if dep:
- fp.write("#endif\n")
- def parse_subsystems_list_file(path):
- with open(path, "r") as fp:
- subsys_list = json.load(fp)
- subsystems.extend(subsys_list["__subsystem"])
- net_sockets.extend(subsys_list["__net_socket"])
- def parse_args():
- global args
- parser = argparse.ArgumentParser(
- description=__doc__,
- formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument("-k", "--kernel", required=False,
- help="Input zephyr ELF binary")
- parser.add_argument(
- "-g", "--gperf-output", required=False,
- help="Output list of kernel object addresses for gperf use")
- parser.add_argument(
- "-V", "--validation-output", required=False,
- help="Output driver validation macros")
- parser.add_argument(
- "-K", "--kobj-types-output", required=False,
- help="Output k_object enum constants")
- parser.add_argument(
- "-S", "--kobj-otype-output", required=False,
- help="Output case statements for otype_to_str()")
- parser.add_argument(
- "-Z", "--kobj-size-output", required=False,
- help="Output case statements for obj_size_get()")
- parser.add_argument("-i", "--include-subsystem-list", required=False, action='append',
- help='''Specifies a file with a JSON encoded list of subsystem names to append to
- the driver subsystems list. Can be specified multiple times:
- -i file1 -i file2 ...''')
- parser.add_argument("-v", "--verbose", action="store_true",
- help="Print extra debugging information")
- args = parser.parse_args()
- if "VERBOSE" in os.environ:
- args.verbose = 1
- def main():
- parse_args()
- if args.include_subsystem_list is not None:
- for list_file in args.include_subsystem_list:
- parse_subsystems_list_file(list_file)
- if args.gperf_output:
- assert args.kernel, "--kernel ELF required for --gperf-output"
- elf = ELFFile(open(args.kernel, "rb"))
- syms = get_symbols(elf)
- max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8
- objs = find_kobjects(elf, syms)
- if not objs:
- sys.stderr.write("WARNING: zero kobject found in %s\n"
- % args.kernel)
- if thread_counter > max_threads:
- sys.exit("Too many thread objects ({})\n"
- "Increase CONFIG_MAX_THREAD_BYTES to {}"
- .format(thread_counter, -(-thread_counter // 8)))
- with open(args.gperf_output, "w") as fp:
- write_gperf_table(fp, syms, objs, elf.little_endian,
- syms["_static_kernel_objects_begin"],
- syms["_static_kernel_objects_end"])
- if args.validation_output:
- with open(args.validation_output, "w") as fp:
- write_validation_output(fp)
- if args.kobj_types_output:
- with open(args.kobj_types_output, "w") as fp:
- write_kobj_types_output(fp)
- if args.kobj_otype_output:
- with open(args.kobj_otype_output, "w") as fp:
- write_kobj_otype_output(fp)
- if args.kobj_size_output:
- with open(args.kobj_size_output, "w") as fp:
- write_kobj_size_output(fp)
- if __name__ == "__main__":
- main()
|