1#!/usr/bin/env python3
2#
3# Copyright (c) 2017 Intel Corporation
4# Copyright (c) 2018 Foundries.io
5#
6# SPDX-License-Identifier: Apache-2.0
7#
8
9import argparse
10import struct
11import sys
12import os
13from elftools.elf.elffile import ELFFile
14from elftools.elf.sections import SymbolTableSection
15
16ISR_FLAG_DIRECT = (1 << 0)
17
18# The below few hardware independent magic numbers represent various
19# levels of interrupts in a multi-level interrupt system.
20# 0x000000FF - represents the 1st level (i.e. the interrupts
21#              that directly go to the processor).
22# 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel
23#              into 1 line which then goes into the 1st level)
24# 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel
25#              into 1 line which then goes into the 2nd level)
26FIRST_LVL_INTERRUPTS = 0x000000FF
27SECND_LVL_INTERRUPTS = 0x0000FF00
28THIRD_LVL_INTERRUPTS = 0x00FF0000
29
30def debug(text):
31    if args.debug:
32        sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
33
34def error(text):
35    sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n")
36
37def endian_prefix():
38    if args.big_endian:
39        return ">"
40    else:
41        return "<"
42
43def read_intlist(intlist_path, syms):
44    """read a binary file containing the contents of the kernel's .intList
45    section. This is an instance of a header created by
46    include/linker/intlist.ld:
47
48     struct {
49       uint32_t num_vectors;       <- typically CONFIG_NUM_IRQS
50       struct _isr_list isrs[]; <- Usually of smaller size than num_vectors
51    }
52
53    Followed by instances of struct _isr_list created by IRQ_CONNECT()
54    calls:
55
56    struct _isr_list {
57        /** IRQ line number */
58        int32_t irq;
59        /** Flags for this IRQ, see ISR_FLAG_* definitions */
60        int32_t flags;
61        /** ISR to call */
62        void *func;
63        /** Parameter for non-direct IRQs */
64        const void *param;
65    };
66    """
67
68    intlist = {}
69
70    prefix = endian_prefix()
71
72    intlist_header_fmt = prefix + "II"
73    if "CONFIG_64BIT" in syms:
74        intlist_entry_fmt = prefix + "iiQQ"
75    else:
76        intlist_entry_fmt = prefix + "iiII"
77
78    with open(intlist_path, "rb") as fp:
79        intdata = fp.read()
80
81    header_sz = struct.calcsize(intlist_header_fmt)
82    header = struct.unpack_from(intlist_header_fmt, intdata, 0)
83    intdata = intdata[header_sz:]
84
85    debug(str(header))
86
87    intlist["num_vectors"]    = header[0]
88    intlist["offset"]         = header[1]
89
90    intlist["interrupts"] = [i for i in
91            struct.iter_unpack(intlist_entry_fmt, intdata)]
92
93    debug("Configured interrupt routing")
94    debug("handler    irq flags param")
95    debug("--------------------------")
96
97    for irq in intlist["interrupts"]:
98        debug("{0:<10} {1:<3} {2:<3}   {3}".format(
99            hex(irq[2]), irq[0], irq[1], hex(irq[3])))
100
101    return intlist
102
103
104def parse_args():
105    global args
106
107    parser = argparse.ArgumentParser(description=__doc__,
108            formatter_class=argparse.RawDescriptionHelpFormatter)
109
110    parser.add_argument("-e", "--big-endian", action="store_true",
111            help="Target encodes data in big-endian format (little endian is "
112                 "the default)")
113    parser.add_argument("-d", "--debug", action="store_true",
114            help="Print additional debugging information")
115    parser.add_argument("-o", "--output-source", required=True,
116            help="Output source file")
117    parser.add_argument("-k", "--kernel", required=True,
118            help="Zephyr kernel image")
119    parser.add_argument("-s", "--sw-isr-table", action="store_true",
120            help="Generate SW ISR table")
121    parser.add_argument("-V", "--vector-table", action="store_true",
122            help="Generate vector table")
123    parser.add_argument("-i", "--intlist", required=True,
124            help="Zephyr intlist binary for intList extraction")
125
126    args = parser.parse_args()
127
128source_header = """
129/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
130
131#include <toolchain.h>
132#include <linker/sections.h>
133#include <sw_isr_table.h>
134#include <arch/cpu.h>
135
136#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_GEN_IRQ_VECTOR_TABLE)
137#define ISR_WRAPPER ((uintptr_t)&_isr_wrapper)
138#else
139#define ISR_WRAPPER NULL
140#endif
141
142typedef void (* ISR)(const void *);
143"""
144
145def write_source_file(fp, vt, swt, intlist, syms):
146    fp.write(source_header)
147
148    nv = intlist["num_vectors"]
149
150    if vt:
151        fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv)
152        for i in range(nv):
153            fp.write("\t{},\n".format(vt[i]))
154        fp.write("};\n")
155
156    if not swt:
157        return
158
159    fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
160            % nv)
161
162    level2_offset = syms.get("CONFIG_2ND_LVL_ISR_TBL_OFFSET")
163    level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET")
164
165    for i in range(nv):
166        param, func = swt[i]
167        if isinstance(func, int):
168            func_as_string = "{0:#x}".format(func)
169        else:
170            func_as_string = func
171
172        if level2_offset is not None and i == level2_offset:
173            fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n".
174                     format(level2_offset))
175        if level3_offset is not None and i == level3_offset:
176            fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
177                     format(level3_offset))
178
179        fp.write("\t{{(const void *){0:#x}, (ISR){1}}},\n".format(param, func_as_string))
180    fp.write("};\n")
181
182def get_symbols(obj):
183    for section in obj.iter_sections():
184        if isinstance(section, SymbolTableSection):
185            return {sym.name: sym.entry.st_value
186                    for sym in section.iter_symbols()}
187
188    error("Could not find symbol table")
189
190def getindex(irq, irq_aggregator_pos):
191    try:
192        return irq_aggregator_pos.index(irq)
193    except ValueError:
194        error("IRQ {} not present in parent offsets ({}). ".
195              format(irq, irq_aggregator_pos) +
196              " Recheck interrupt configuration.")
197
198def main():
199    parse_args()
200
201    with open(args.kernel, "rb") as fp:
202        kernel = ELFFile(fp)
203        syms = get_symbols(kernel)
204
205    if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
206        max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"]
207
208        if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms:
209            num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"]
210            irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"]
211            list_2nd_lvl_offsets = [syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'.
212                                         format(str(i).zfill(2))] for i in
213                                    range(num_aggregators)]
214
215            debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets))
216
217            if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms:
218                num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"]
219                irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"]
220                list_3rd_lvl_offsets = [syms['CONFIG_3RD_LVL_INTR_{}_OFFSET'.
221                                             format(str(i).zfill(2))] for i in
222                                        range(num_aggregators)]
223
224                debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets))
225
226    intlist = read_intlist(args.intlist, syms)
227    nvec = intlist["num_vectors"]
228    offset = intlist["offset"]
229
230    if nvec > pow(2, 15):
231        raise ValueError('nvec is too large, check endianness.')
232
233    spurious_handler = "&z_irq_spurious"
234    sw_irq_handler   = "ISR_WRAPPER"
235
236    debug('offset is ' + str(offset))
237    debug('num_vectors is ' + str(nvec))
238
239    # Set default entries in both tables
240    if args.sw_isr_table:
241        # All vectors just jump to the common sw_irq_handler. If some entries
242        # are used for direct interrupts, they will be replaced later.
243        if args.vector_table:
244            vt = [sw_irq_handler for i in range(nvec)]
245        else:
246            vt = None
247        # Default to spurious interrupt handler. Configured interrupts
248        # will replace these entries.
249        swt = [(0, spurious_handler) for i in range(nvec)]
250    else:
251        if args.vector_table:
252            vt = [spurious_handler for i in range(nvec)]
253        else:
254            error("one or both of -s or -V needs to be specified on command line")
255        swt = None
256
257    for irq, flags, func, param in intlist["interrupts"]:
258        if flags & ISR_FLAG_DIRECT:
259            if param != 0:
260                error("Direct irq %d declared, but has non-NULL parameter"
261                        % irq)
262            if not 0 <= irq - offset < len(vt):
263                error("IRQ %d (offset=%d) exceeds the maximum of %d" %
264                      (irq - offset, offset, len(vt) - 1))
265            vt[irq - offset] = func
266        else:
267            # Regular interrupt
268            if not swt:
269                error("Regular Interrupt %d declared with parameter 0x%x "
270                        "but no SW ISR_TABLE in use"
271                        % (irq, param))
272
273            if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
274                table_index = irq - offset
275            else:
276                # Figure out third level interrupt position
277                debug('IRQ = ' + hex(irq))
278                irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16
279                irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8
280                irq1 = (irq & FIRST_LVL_INTERRUPTS)
281
282                if irq3:
283                    irq_parent = irq2
284                    list_index = getindex(irq_parent, list_3rd_lvl_offsets)
285                    irq3_pos = irq3_baseoffset + max_irq_per*list_index + irq3 - 1
286                    debug('IRQ_level = 3')
287                    debug('IRQ_Indx = ' + str(irq3))
288                    debug('IRQ_Pos  = ' + str(irq3_pos))
289                    table_index = irq3_pos - offset
290
291                # Figure out second level interrupt position
292                elif irq2:
293                    irq_parent = irq1
294                    list_index = getindex(irq_parent, list_2nd_lvl_offsets)
295                    irq2_pos = irq2_baseoffset + max_irq_per*list_index + irq2 - 1
296                    debug('IRQ_level = 2')
297                    debug('IRQ_Indx = ' + str(irq2))
298                    debug('IRQ_Pos  = ' + str(irq2_pos))
299                    table_index = irq2_pos - offset
300
301                # Figure out first level interrupt position
302                else:
303                    debug('IRQ_level = 1')
304                    debug('IRQ_Indx = ' + str(irq1))
305                    debug('IRQ_Pos  = ' + str(irq1))
306                    table_index = irq1 - offset
307
308            if not 0 <= table_index < len(swt):
309                error("IRQ %d (offset=%d) exceeds the maximum of %d" %
310                      (table_index, offset, len(swt) - 1))
311            if swt[table_index] != (0, spurious_handler):
312                error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
313                      + f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}"
314                      + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
315                )
316
317            swt[table_index] = (param, func)
318
319    with open(args.output_source, "w") as fp:
320        write_source_file(fp, vt, swt, intlist, syms)
321
322if __name__ == "__main__":
323    main()
324