1#!/usr/bin/env python3
2#
3# Copyright (c) 2017 Intel Corporation
4# Copyright (c) 2018 Foundries.io
5# Copyright (c) 2023 Nordic Semiconductor NA
6#
7# SPDX-License-Identifier: Apache-2.0
8#
9
10import argparse
11import sys
12import os
13import importlib
14from elftools.elf.elffile import ELFFile
15from elftools.elf.sections import SymbolTableSection
16
17
18class gen_isr_log:
19
20    def __init__(self, debug = False):
21        self.__debug = debug
22
23    def debug(self, text):
24        """Print debug message if debugging is enabled.
25
26        Note - this function requires config global variable to be initialized.
27        """
28        if self.__debug:
29            sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
30
31    @staticmethod
32    def error(text):
33        sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n")
34
35    def set_debug(self, state):
36        self.__debug = state
37
38
39log = gen_isr_log()
40
41
42class gen_isr_config:
43    """All the constants and configuration gathered in single class for readability.
44    """
45    # Constants
46    __ISR_FLAG_DIRECT = 1 << 0
47    __swt_spurious_handler = "z_irq_spurious"
48    __swt_shared_handler = "z_shared_isr"
49    __vt_spurious_handler = "z_irq_spurious"
50    __vt_irq_handler = "_isr_wrapper"
51    __shared_array_name = "z_shared_sw_isr_table"
52    __sw_isr_array_name = "_sw_isr_table"
53    __irq_vector_array_name = "_irq_vector_table"
54
55    @staticmethod
56    def __bm(bits):
57        return (1 << bits) - 1
58
59    def __init__(self, args, syms, log):
60        """Initialize the configuration object.
61
62        The configuration object initialization takes only arguments as a parameter.
63        This is done to allow debug function work as soon as possible.
64        """
65        # Store the arguments required for work
66        self.__args = args
67        self.__syms = syms
68        self.__log = log
69
70        # Select the default interrupt vector handler
71        if self.args.sw_isr_table:
72            self.__vt_default_handler = self.__vt_irq_handler
73        else:
74            self.__vt_default_handler = self.__vt_spurious_handler
75        # Calculate interrupt bits
76        self.__int_bits = [8, 8, 8]
77        # The below few hardware independent magic numbers represent various
78        # levels of interrupts in a multi-level interrupt system.
79        # 0x000000FF - represents the 1st level (i.e. the interrupts
80        #              that directly go to the processor).
81        # 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel
82        #              into 1 line which then goes into the 1st level)
83        # 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel
84        #              into 1 line which then goes into the 2nd level)
85        self.__int_lvl_masks = [0x000000FF, 0x0000FF00, 0x00FF0000]
86
87        self.__irq2_baseoffset = None
88        self.__irq3_baseoffset = None
89        self.__irq2_offsets = None
90        self.__irq3_offsets = None
91
92        if self.check_multi_level_interrupts():
93            self.__max_irq_per = self.get_sym("CONFIG_MAX_IRQ_PER_AGGREGATOR")
94
95            self.__int_bits[0] = self.get_sym("CONFIG_1ST_LEVEL_INTERRUPT_BITS")
96            self.__int_bits[1] = self.get_sym("CONFIG_2ND_LEVEL_INTERRUPT_BITS")
97            self.__int_bits[2] = self.get_sym("CONFIG_3RD_LEVEL_INTERRUPT_BITS")
98
99            if sum(self.int_bits) > 32:
100                raise ValueError("Too many interrupt bits")
101
102            self.__int_lvl_masks[0] = self.__bm(self.int_bits[0])
103            self.__int_lvl_masks[1] = self.__bm(self.int_bits[1]) << self.int_bits[0]
104            self.__int_lvl_masks[2] = self.__bm(self.int_bits[2]) << (self.int_bits[0] + self.int_bits[1])
105
106            self.__log.debug("Level    Bits        Bitmask")
107            self.__log.debug("----------------------------")
108            for i in range(3):
109                bitmask_str = "0x" + format(self.__int_lvl_masks[i], '08X')
110                self.__log.debug(f"{i + 1:>5} {self.__int_bits[i]:>7} {bitmask_str:>14}")
111
112            if self.check_sym("CONFIG_2ND_LEVEL_INTERRUPTS"):
113                num_aggregators = self.get_sym("CONFIG_NUM_2ND_LEVEL_AGGREGATORS")
114                self.__irq2_baseoffset = self.get_sym("CONFIG_2ND_LVL_ISR_TBL_OFFSET")
115                self.__irq2_offsets = [self.get_sym('CONFIG_2ND_LVL_INTR_{}_OFFSET'.
116                                                  format(str(i).zfill(2))) for i in
117                                     range(num_aggregators)]
118
119                self.__log.debug('2nd level offsets: {}'.format(self.__irq2_offsets))
120
121                if self.check_sym("CONFIG_3RD_LEVEL_INTERRUPTS"):
122                    num_aggregators = self.get_sym("CONFIG_NUM_3RD_LEVEL_AGGREGATORS")
123                    self.__irq3_baseoffset = self.get_sym("CONFIG_3RD_LVL_ISR_TBL_OFFSET")
124                    self.__irq3_offsets = [self.get_sym('CONFIG_3RD_LVL_INTR_{}_OFFSET'.
125                                                      format(str(i).zfill(2))) for i in
126                                         range(num_aggregators)]
127
128                    self.__log.debug('3rd level offsets: {}'.format(self.__irq3_offsets))
129
130    @property
131    def args(self):
132        return self.__args
133
134    @property
135    def swt_spurious_handler(self):
136        return self.__swt_spurious_handler
137
138    @property
139    def swt_shared_handler(self):
140        return self.__swt_shared_handler
141
142    @property
143    def vt_default_handler(self):
144        return self.__vt_default_handler
145
146    @property
147    def shared_array_name(self):
148        return self.__shared_array_name
149
150    @property
151    def sw_isr_array_name(self):
152        return self.__sw_isr_array_name
153
154    @property
155    def irq_vector_array_name(self):
156        return self.__irq_vector_array_name
157
158    @property
159    def int_bits(self):
160        return self.__int_bits
161
162    @property
163    def int_lvl_masks(self):
164        return self.__int_lvl_masks
165
166    def endian_prefix(self):
167        if self.args.big_endian:
168            return ">"
169        else:
170            return "<"
171
172    def get_irq_baseoffset(self, lvl):
173        if lvl == 2:
174            return self.__irq2_baseoffset
175        if lvl == 3:
176            return self.__irq3_baseoffset
177        self.__log.error("Unsupported irq level: {}".format(lvl))
178
179    def get_irq_index(self, irq, lvl):
180        if lvl == 2:
181            offsets = self.__irq2_offsets
182        elif lvl == 3:
183            offsets = self.__irq3_offsets
184        else:
185            self.__log.error("Unsupported irq level: {}".format(lvl))
186        try:
187            return offsets.index(irq)
188        except ValueError:
189            self.__log.error("IRQ {} not present in parent offsets ({}). ".
190                             format(irq, offsets) +
191                             " Recheck interrupt configuration.")
192
193    def get_swt_table_index(self, offset, irq):
194        if not self.check_multi_level_interrupts():
195            return irq - offset
196        # Calculate index for multi level interrupts
197        self.__log.debug('IRQ = ' + hex(irq))
198        irq3 = (irq & self.int_lvl_masks[2]) >> (self.int_bits[0] + self.int_bits[1])
199        irq2 = (irq & self.int_lvl_masks[1]) >> (self.int_bits[0])
200        irq1 = irq & self.int_lvl_masks[0]
201        # Figure out third level interrupt position
202        if irq3:
203            list_index = self.get_irq_index(irq2 - 1, 3)
204            irq3_pos = self.get_irq_baseoffset(3) + self.__max_irq_per * list_index + irq3 - 1
205            self.__log.debug('IRQ_level = 3')
206            self.__log.debug('IRQ_Indx = ' + str(irq3))
207            self.__log.debug('IRQ_Pos  = ' + str(irq3_pos))
208            return irq3_pos - offset
209        # Figure out second level interrupt position
210        if irq2:
211            list_index = self.get_irq_index(irq1, 2)
212            irq2_pos = self.get_irq_baseoffset(2) + self.__max_irq_per * list_index + irq2 - 1
213            self.__log.debug('IRQ_level = 2')
214            self.__log.debug('IRQ_Indx = ' + str(irq2))
215            self.__log.debug('IRQ_Pos  = ' + str(irq2_pos))
216            return irq2_pos - offset
217        # Figure out first level interrupt position
218        self.__log.debug('IRQ_level = 1')
219        self.__log.debug('IRQ_Indx = ' + str(irq1))
220        self.__log.debug('IRQ_Pos  = ' + str(irq1))
221        return irq1 - offset
222
223    def get_intlist_snames(self):
224        return self.args.intlist_section
225
226    def test_isr_direct(self, flags):
227        return flags & self.__ISR_FLAG_DIRECT
228
229    def get_sym_from_addr(self, addr):
230        for key, value in self.__syms.items():
231            if addr == value:
232                return key
233        return None
234
235    def get_sym(self, name):
236        return self.__syms.get(name)
237
238    def check_sym(self, name):
239        return name in self.__syms
240
241    def check_multi_level_interrupts(self):
242        return self.check_sym("CONFIG_MULTI_LEVEL_INTERRUPTS")
243
244    def check_shared_interrupts(self):
245        return self.check_sym("CONFIG_SHARED_INTERRUPTS")
246
247    def check_64b(self):
248        return self.check_sym("CONFIG_64BIT")
249
250
251def get_symbols(obj):
252    for section in obj.iter_sections():
253        if isinstance(section, SymbolTableSection):
254            return {sym.name: sym.entry.st_value
255                    for sym in section.iter_symbols()}
256
257    log.error("Could not find symbol table")
258
259def read_intList_sect(elfobj, snames):
260    """
261    Load the raw intList section data in a form of byte array.
262    """
263    intList_sect = None
264
265    for sname in snames:
266        intList_sect = elfobj.get_section_by_name(sname)
267        if intList_sect is not None:
268            log.debug("Found intlist section: \"{}\"".format(sname))
269            break
270
271    if intList_sect is None:
272        log.error("Cannot find the intlist section!")
273
274    intdata = intList_sect.data()
275
276    return intdata
277
278def parse_args():
279    parser = argparse.ArgumentParser(description=__doc__,
280            formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
281
282    parser.add_argument("-e", "--big-endian", action="store_true",
283            help="Target encodes data in big-endian format (little endian is "
284                 "the default)")
285    parser.add_argument("-d", "--debug", action="store_true",
286            help="Print additional debugging information")
287    parser.add_argument("-o", "--output-source", required=True,
288            help="Output source file")
289    parser.add_argument("-l", "--linker-output-files",
290            nargs=2,
291            metavar=("vector_table_link", "software_interrupt_link"),
292            help="Output linker files. "
293                 "Used only if CONFIG_ISR_TABLES_LOCAL_DECLARATION is enabled. "
294                 "In other case empty file would be generated.")
295    parser.add_argument("-k", "--kernel", required=True,
296            help="Zephyr kernel image")
297    parser.add_argument("-s", "--sw-isr-table", action="store_true",
298            help="Generate SW ISR table")
299    parser.add_argument("-V", "--vector-table", action="store_true",
300            help="Generate vector table")
301    parser.add_argument("-i", "--intlist-section", action="append", required=True,
302            help="The name of the section to search for the interrupt data. "
303                 "This is accumulative argument. The first section found would be used.")
304
305    return parser.parse_args()
306
307def main():
308    args = parse_args()
309    # Configure logging as soon as possible
310    log.set_debug(args.debug)
311
312    with open(args.kernel, "rb") as fp:
313        kernel = ELFFile(fp)
314        config = gen_isr_config(args, get_symbols(kernel), log)
315        intlist_data = read_intList_sect(kernel, config.get_intlist_snames())
316
317        if config.check_sym("CONFIG_ISR_TABLES_LOCAL_DECLARATION"):
318            sys.stdout.write(
319                "Warning: The EXPERIMENTAL ISR_TABLES_LOCAL_DECLARATION feature selected\n")
320            parser_module = importlib.import_module('gen_isr_tables_parser_local')
321            parser = parser_module.gen_isr_parser(intlist_data, config, log)
322        else:
323            parser_module = importlib.import_module('gen_isr_tables_parser_carrays')
324            parser = parser_module.gen_isr_parser(intlist_data, config, log)
325
326    with open(args.output_source, "w") as fp:
327        parser.write_source(fp)
328
329    if args.linker_output_files is not None:
330        with open(args.linker_output_files[0], "w") as fp_vt, \
331             open(args.linker_output_files[1], "w") as fp_swi:
332            if hasattr(parser, 'write_linker_vt'):
333                parser.write_linker_vt(fp_vt)
334            else:
335                log.debug("Chosen parser does not support vector table linker file")
336                fp_vt.write('/* Empty */\n')
337            if hasattr(parser, 'write_linker_swi'):
338                parser.write_linker_swi(fp_swi)
339            else:
340                log.debug("Chosen parser does not support software interrupt linker file")
341                fp_swi.write('/* Empty */\n')
342
343if __name__ == "__main__":
344    main()
345