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_NEW_API", True, False)),
114    ("ztest_test_rule", ("CONFIG_ZTEST_NEW_API", 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/syscall_handler.h>
728#include <string.h>
729%}
730struct z_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 z_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 z_object *z_object_find(const void *obj)
756	ALIAS_OF(z_object_gperf_find);
757
758void z_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 Z_SYSCALL_DRIVER_%(driver_upper)s(ptr, op) Z_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 Z_SYSCALL_DRIVER_GEN(ptr, op, driver_lower_case, driver_upper_case) \\
897		(Z_SYSCALL_OBJ(ptr, K_OBJ_DRIVER_##driver_upper_case) || \\
898		 Z_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