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
30INTERRUPT_BITS = [8, 8, 8]
31
32def debug(text):
33    if args.debug:
34        sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
35
36def error(text):
37    sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n")
38
39def endian_prefix():
40    if args.big_endian:
41        return ">"
42    else:
43        return "<"
44
45def read_intlist(intlist_path, syms):
46    """read a binary file containing the contents of the kernel's .intList
47    section. This is an instance of a header created by
48    include/zephyr/linker/intlist.ld:
49
50     struct {
51       uint32_t num_vectors;       <- typically CONFIG_NUM_IRQS
52       struct _isr_list isrs[]; <- Usually of smaller size than num_vectors
53    }
54
55    Followed by instances of struct _isr_list created by IRQ_CONNECT()
56    calls:
57
58    struct _isr_list {
59        /** IRQ line number */
60        int32_t irq;
61        /** Flags for this IRQ, see ISR_FLAG_* definitions */
62        int32_t flags;
63        /** ISR to call */
64        void *func;
65        /** Parameter for non-direct IRQs */
66        const void *param;
67    };
68    """
69
70    intlist = {}
71
72    prefix = endian_prefix()
73
74    intlist_header_fmt = prefix + "II"
75    if "CONFIG_64BIT" in syms:
76        intlist_entry_fmt = prefix + "iiQQ"
77    else:
78        intlist_entry_fmt = prefix + "iiII"
79
80    with open(intlist_path, "rb") as fp:
81        intdata = fp.read()
82
83    header_sz = struct.calcsize(intlist_header_fmt)
84    header = struct.unpack_from(intlist_header_fmt, intdata, 0)
85    intdata = intdata[header_sz:]
86
87    debug(str(header))
88
89    intlist["num_vectors"]    = header[0]
90    intlist["offset"]         = header[1]
91
92    intlist["interrupts"] = [i for i in
93            struct.iter_unpack(intlist_entry_fmt, intdata)]
94
95    debug("Configured interrupt routing")
96    debug("handler    irq flags param")
97    debug("--------------------------")
98
99    for irq in intlist["interrupts"]:
100        debug("{0:<10} {1:<3} {2:<3}   {3}".format(
101            hex(irq[2]), irq[0], irq[1], hex(irq[3])))
102
103    return intlist
104
105
106def parse_args():
107    global args
108
109    parser = argparse.ArgumentParser(description=__doc__,
110            formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
111
112    parser.add_argument("-e", "--big-endian", action="store_true",
113            help="Target encodes data in big-endian format (little endian is "
114                 "the default)")
115    parser.add_argument("-d", "--debug", action="store_true",
116            help="Print additional debugging information")
117    parser.add_argument("-o", "--output-source", required=True,
118            help="Output source file")
119    parser.add_argument("-k", "--kernel", required=True,
120            help="Zephyr kernel image")
121    parser.add_argument("-s", "--sw-isr-table", action="store_true",
122            help="Generate SW ISR table")
123    parser.add_argument("-V", "--vector-table", action="store_true",
124            help="Generate vector table")
125    parser.add_argument("-i", "--intlist", required=True,
126            help="Zephyr intlist binary for intList extraction")
127
128    args = parser.parse_args()
129
130source_assembly_header = """
131#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
132#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
133#endif
134"""
135
136def get_symbol_from_addr(syms, addr):
137    for key, value in syms.items():
138        if addr == value:
139            return key
140    return None
141
142def write_code_irq_vector_table(fp, vt, nv, syms):
143    fp.write(source_assembly_header)
144
145    fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n")
146    for i in range(nv):
147        func = vt[i]
148
149        if isinstance(func, int):
150            func_as_string = get_symbol_from_addr(syms, func)
151        else:
152            func_as_string = func
153
154        fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string))
155    fp.write("}\n")
156
157def write_address_irq_vector_table(fp, vt, nv):
158    fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv)
159    for i in range(nv):
160        func = vt[i]
161
162        if isinstance(func, int):
163            fp.write("\t{},\n".format(vt[i]))
164        else:
165            fp.write("\t((uintptr_t)&{}),\n".format(vt[i]))
166
167    fp.write("};\n")
168
169source_header = """
170/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
171
172#include <zephyr/toolchain.h>
173#include <zephyr/linker/sections.h>
174#include <zephyr/sw_isr_table.h>
175#include <zephyr/arch/cpu.h>
176
177typedef void (* ISR)(const void *);
178"""
179
180def write_shared_table(fp, shared, nv):
181    fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table"
182            " z_shared_sw_isr_table[%d] = {\n" % nv)
183
184    for i in range(nv):
185        client_num = shared[i][1]
186        client_list = shared[i][0]
187
188        if not client_num:
189            fp.write("\t{ },\n")
190        else:
191            fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ")
192            for j in range(0, client_num):
193                routine = client_list[j][1]
194                arg = client_list[j][0]
195
196                fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, "
197                        f".arg = (const void *){hex(arg)} }},")
198
199            fp.write(" },\n},\n")
200
201    fp.write("};\n")
202
203def write_source_file(fp, vt, swt, intlist, syms, shared):
204    fp.write(source_header)
205
206    nv = intlist["num_vectors"]
207
208    if "CONFIG_SHARED_INTERRUPTS" in syms:
209        write_shared_table(fp, shared, nv)
210
211    if vt:
212        if "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS" in syms:
213            write_address_irq_vector_table(fp, vt, nv)
214        elif "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE" in syms:
215            write_code_irq_vector_table(fp, vt, nv, syms)
216        else:
217            error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set")
218
219    if not swt:
220        return
221
222    fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
223            % nv)
224
225    level2_offset = syms.get("CONFIG_2ND_LVL_ISR_TBL_OFFSET")
226    level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET")
227
228    for i in range(nv):
229        param = "{0:#x}".format(swt[i][0])
230        func = swt[i][1]
231
232        if isinstance (func, str) and "z_shared_isr" in func:
233            param = "&z_shared_sw_isr_table[{0}]".format(i)
234        if isinstance(func, int):
235            func_as_string = "{0:#x}".format(func)
236        else:
237            func_as_string = func
238
239        if level2_offset is not None and i == level2_offset:
240            fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n".
241                     format(level2_offset))
242        if level3_offset is not None and i == level3_offset:
243            fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
244                     format(level3_offset))
245
246        fp.write("\t{{(const void *){0}, (ISR){1}}},\n".format(param, func_as_string))
247    fp.write("};\n")
248
249def get_symbols(obj):
250    for section in obj.iter_sections():
251        if isinstance(section, SymbolTableSection):
252            return {sym.name: sym.entry.st_value
253                    for sym in section.iter_symbols()}
254
255    error("Could not find symbol table")
256
257def getindex(irq, irq_aggregator_pos):
258    try:
259        return irq_aggregator_pos.index(irq)
260    except ValueError:
261        error("IRQ {} not present in parent offsets ({}). ".
262              format(irq, irq_aggregator_pos) +
263              " Recheck interrupt configuration.")
264
265def bit_mask(bits):
266    mask = 0
267    for _ in range(0, bits):
268        mask = (mask << 1) | 1
269    return mask
270
271def update_masks():
272    global FIRST_LVL_INTERRUPTS
273    global SECND_LVL_INTERRUPTS
274    global THIRD_LVL_INTERRUPTS
275
276    if sum(INTERRUPT_BITS) > 32:
277        raise ValueError("Too many interrupt bits")
278
279    FIRST_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[0])
280    SECND_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[1]) << INTERRUPT_BITS[0]
281    THIRD_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[2]) << INTERRUPT_BITS[0] + INTERRUPT_BITS[2]
282
283def main():
284    parse_args()
285
286    with open(args.kernel, "rb") as fp:
287        kernel = ELFFile(fp)
288        syms = get_symbols(kernel)
289
290    if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
291        max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"]
292
293        INTERRUPT_BITS[0] = syms["CONFIG_1ST_LEVEL_INTERRUPT_BITS"]
294        INTERRUPT_BITS[1] = syms["CONFIG_2ND_LEVEL_INTERRUPT_BITS"]
295        INTERRUPT_BITS[2] = syms["CONFIG_3RD_LEVEL_INTERRUPT_BITS"]
296        update_masks()
297
298        if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms:
299            num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"]
300            irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"]
301            list_2nd_lvl_offsets = [syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'.
302                                         format(str(i).zfill(2))] for i in
303                                    range(num_aggregators)]
304
305            debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets))
306
307            if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms:
308                num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"]
309                irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"]
310                list_3rd_lvl_offsets = [syms['CONFIG_3RD_LVL_INTR_{}_OFFSET'.
311                                             format(str(i).zfill(2))] for i in
312                                        range(num_aggregators)]
313
314                debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets))
315
316    intlist = read_intlist(args.intlist, syms)
317    nvec = intlist["num_vectors"]
318    offset = intlist["offset"]
319
320    if nvec > pow(2, 15):
321        raise ValueError('nvec is too large, check endianness.')
322
323    swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
324    swt_shared_handler = "((uintptr_t)&z_shared_isr)"
325    vt_spurious_handler = "z_irq_spurious"
326    vt_irq_handler = "_isr_wrapper"
327
328    debug('offset is ' + str(offset))
329    debug('num_vectors is ' + str(nvec))
330
331    # Set default entries in both tables
332    if args.sw_isr_table:
333        # All vectors just jump to the common vt_irq_handler. If some entries
334        # are used for direct interrupts, they will be replaced later.
335        if args.vector_table:
336            vt = [vt_irq_handler for i in range(nvec)]
337        else:
338            vt = None
339        # Default to spurious interrupt handler. Configured interrupts
340        # will replace these entries.
341        swt = [(0, swt_spurious_handler) for i in range(nvec)]
342        shared = [([], 0) for i in range(nvec)]
343    else:
344        if args.vector_table:
345            vt = [vt_spurious_handler for i in range(nvec)]
346        else:
347            error("one or both of -s or -V needs to be specified on command line")
348        swt = None
349
350    for irq, flags, func, param in intlist["interrupts"]:
351        if flags & ISR_FLAG_DIRECT:
352            if param != 0:
353                error("Direct irq %d declared, but has non-NULL parameter"
354                        % irq)
355            if not 0 <= irq - offset < len(vt):
356                error("IRQ %d (offset=%d) exceeds the maximum of %d" %
357                      (irq - offset, offset, len(vt) - 1))
358            vt[irq - offset] = func
359        else:
360            # Regular interrupt
361            if not swt:
362                error("Regular Interrupt %d declared with parameter 0x%x "
363                        "but no SW ISR_TABLE in use"
364                        % (irq, param))
365
366            if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
367                table_index = irq - offset
368            else:
369                # Figure out third level interrupt position
370                debug('IRQ = ' + hex(irq))
371                irq3 = (irq & THIRD_LVL_INTERRUPTS) >> INTERRUPT_BITS[0] + INTERRUPT_BITS[1]
372                irq2 = (irq & SECND_LVL_INTERRUPTS) >> INTERRUPT_BITS[0]
373                irq1 = irq & FIRST_LVL_INTERRUPTS
374
375                if irq3:
376                    irq_parent = irq2
377                    list_index = getindex(irq_parent, list_3rd_lvl_offsets)
378                    irq3_pos = irq3_baseoffset + max_irq_per*list_index + irq3 - 1
379                    debug('IRQ_level = 3')
380                    debug('IRQ_Indx = ' + str(irq3))
381                    debug('IRQ_Pos  = ' + str(irq3_pos))
382                    table_index = irq3_pos - offset
383
384                # Figure out second level interrupt position
385                elif irq2:
386                    irq_parent = irq1
387                    list_index = getindex(irq_parent, list_2nd_lvl_offsets)
388                    irq2_pos = irq2_baseoffset + max_irq_per*list_index + irq2 - 1
389                    debug('IRQ_level = 2')
390                    debug('IRQ_Indx = ' + str(irq2))
391                    debug('IRQ_Pos  = ' + str(irq2_pos))
392                    table_index = irq2_pos - offset
393
394                # Figure out first level interrupt position
395                else:
396                    debug('IRQ_level = 1')
397                    debug('IRQ_Indx = ' + str(irq1))
398                    debug('IRQ_Pos  = ' + str(irq1))
399                    table_index = irq1 - offset
400
401            if not 0 <= table_index < len(swt):
402                error("IRQ %d (offset=%d) exceeds the maximum of %d" %
403                      (table_index, offset, len(swt) - 1))
404            if "CONFIG_SHARED_INTERRUPTS" in syms:
405                if swt[table_index] != (0, swt_spurious_handler):
406                    # check client limit
407                    if syms["CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"] == shared[table_index][1]:
408                        error(f"Reached shared interrupt client limit. Maybe increase"
409                              + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
410                    lst = shared[table_index][0]
411                    delta_size = 1
412                    if not shared[table_index][1]:
413                        lst.append(swt[table_index])
414			# note: the argument will be fixed when writing the ISR table
415			# to isr_table.c
416                        swt[table_index] = (0, swt_shared_handler)
417                        delta_size += 1
418                    if (param, func) in lst:
419                        error("Attempting to register the same ISR/arg pair twice.")
420                    lst.append((param, func))
421                    shared[table_index] = (lst, shared[table_index][1] + delta_size)
422                else:
423                    swt[table_index] = (param, func)
424            else:
425                if swt[table_index] != (0, swt_spurious_handler):
426                    error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
427                          + f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}"
428                          + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
429                    )
430                else:
431                    swt[table_index] = (param, func)
432
433    with open(args.output_source, "w") as fp:
434        write_source_file(fp, vt, swt, intlist, syms, shared)
435
436if __name__ == "__main__":
437    main()
438