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 struct
11
12class gen_isr_parser:
13    source_header = """
14/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
15
16#include <zephyr/toolchain.h>
17#include <zephyr/linker/sections.h>
18#include <zephyr/sw_isr_table.h>
19#include <zephyr/arch/cpu.h>
20
21typedef void (* ISR)(const void *);
22"""
23
24    source_assembly_header = """
25#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
26#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
27#endif
28"""
29
30    def __init__(self, intlist_data, config, log):
31        """Initialize the parser.
32
33        The function prepares parser to work.
34        Parameters:
35        - intlist_data: The binnary data from intlist section
36        - config: The configuration object
37        - log: The logging object, has to have error and debug methods
38        """
39        self.__config = config
40        self.__log = log
41        intlist = self.__read_intlist(intlist_data)
42        self.__vt, self.__swt, self.__nv = self.__parse_intlist(intlist)
43
44    def __read_intlist(self, intlist_data):
45        """read a binary file containing the contents of the kernel's .intList
46        section. This is an instance of a header created by
47        include/zephyr/linker/intlist.ld:
48
49         struct {
50           uint32_t num_vectors;       <- typically CONFIG_NUM_IRQS
51           struct _isr_list isrs[]; <- Usually of smaller size than num_vectors
52        }
53
54        Followed by instances of struct _isr_list created by IRQ_CONNECT()
55        calls:
56
57        struct _isr_list {
58            /** IRQ line number */
59            int32_t irq;
60            /** Flags for this IRQ, see ISR_FLAG_* definitions */
61            int32_t flags;
62            /** ISR to call */
63            void *func;
64            /** Parameter for non-direct IRQs */
65            const void *param;
66        };
67        """
68        intlist = {}
69        prefix = self.__config.endian_prefix()
70
71        # Extract header and the rest of the data
72        intlist_header_fmt = prefix + "II"
73        header_sz = struct.calcsize(intlist_header_fmt)
74        header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0)
75        self.__log.debug(str(header_raw))
76
77        intlist["num_vectors"]    = header_raw[0]
78        intlist["offset"]         = header_raw[1]
79        intdata = intlist_data[header_sz:]
80
81        # Extract information about interrupts
82        if self.__config.check_64b():
83            intlist_entry_fmt = prefix + "iiQQ"
84        else:
85            intlist_entry_fmt = prefix + "iiII"
86
87        intlist["interrupts"] = [i for i in
88                struct.iter_unpack(intlist_entry_fmt, intdata)]
89
90        self.__log.debug("Configured interrupt routing")
91        self.__log.debug("handler    irq flags param")
92        self.__log.debug("--------------------------")
93
94        for irq in intlist["interrupts"]:
95            self.__log.debug("{0:<10} {1:<3} {2:<3}   {3}".format(
96                hex(irq[2]), irq[0], irq[1], hex(irq[3])))
97
98        return intlist
99
100    def __parse_intlist(self, intlist):
101        """All the intlist data are parsed into swt and vt arrays.
102
103        The vt array is prepared for hardware interrupt table.
104        Every entry in the selected position would contain None or the name of the function pointer
105        (address or string).
106
107        The swt is a little more complex. At every position it would contain an array of parameter and
108        function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry.
109        If empty array is placed on selected position - it means that the application does not implement
110        this interrupt.
111
112        Parameters:
113        - intlist: The preprocessed list of intlist section content (see read_intlist)
114
115        Return:
116        vt, swt - parsed vt and swt arrays (see function description above)
117        """
118        nvec = intlist["num_vectors"]
119        offset = intlist["offset"]
120
121        if nvec > pow(2, 15):
122            raise ValueError('nvec is too large, check endianness.')
123
124        self.__log.debug('offset is ' + str(offset))
125        self.__log.debug('num_vectors is ' + str(nvec))
126
127        # Set default entries in both tables
128        if not(self.__config.args.sw_isr_table or self.__config.args.vector_table):
129            self.__log.error("one or both of -s or -V needs to be specified on command line")
130        if self.__config.args.vector_table:
131            vt = [None for i in range(nvec)]
132        else:
133            vt = None
134        if self.__config.args.sw_isr_table:
135            swt = [[] for i in range(nvec)]
136        else:
137            swt = None
138
139        # Process intlist and write to the tables created
140        for irq, flags, func, param in intlist["interrupts"]:
141            if self.__config.test_isr_direct(flags):
142                if not vt:
143                    self.__log.error("Direct Interrupt %d declared with parameter 0x%x "
144                                     "but no vector table in use"
145                                      % (irq, param))
146                if param != 0:
147                    self.__log.error("Direct irq %d declared, but has non-NULL parameter"
148                                     % irq)
149                if not 0 <= irq - offset < len(vt):
150                    self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d"
151                                     % (irq - offset, offset, len(vt) - 1))
152                vt[irq - offset] = func
153            else:
154                # Regular interrupt
155                if not swt:
156                    self.__log.error("Regular Interrupt %d declared with parameter 0x%x "
157                                     "but no SW ISR_TABLE in use"
158                                     % (irq, param))
159
160                table_index = self.__config.get_swt_table_index(offset, irq)
161
162                if not 0 <= table_index < len(swt):
163                    self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
164                                     (table_index, offset, len(swt) - 1))
165                if self.__config.check_shared_interrupts():
166                    lst = swt[table_index]
167                    if (param, func) in lst:
168                        self.__log.error("Attempting to register the same ISR/arg pair twice.")
169                    if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"):
170                        self.__log.error(f"Reached shared interrupt client limit. Maybe increase"
171                                         + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
172                else:
173                    if len(swt[table_index]) > 0:
174                        self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
175                                         + f"\nExisting handler 0x{swt[table_index][0][1]:x}, new handler 0x{func:x}"
176                                         + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
177                        )
178                swt[table_index].append((param, func))
179
180        return vt, swt, nvec
181
182    def __write_code_irq_vector_table(self, fp):
183        fp.write(self.source_assembly_header)
184
185        fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n")
186        for i in range(self.__nv):
187            func = self.__vt[i]
188
189            if func is None:
190                func = self.__config.vt_default_handler
191
192            if isinstance(func, int):
193                func_as_string = self.__config.get_sym_from_addr(func)
194            else:
195                func_as_string = func
196
197            fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string))
198        fp.write("}\n")
199
200    def __write_address_irq_vector_table(self, fp):
201        fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % self.__nv)
202        for i in range(self.__nv):
203            func = self.__vt[i]
204
205            if func is None:
206                func = self.__config.vt_default_handler
207
208            if isinstance(func, int):
209                fp.write("\t{},\n".format(func))
210            else:
211                fp.write("\t((uintptr_t)&{}),\n".format(func))
212
213        fp.write("};\n")
214
215    def __write_shared_table(self, fp):
216        fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table"
217                " z_shared_sw_isr_table[%d] = {\n" % self.__nv)
218
219        for i in range(self.__nv):
220            if self.__swt[i] is None:
221                client_num = 0
222                client_list = None
223            else:
224                client_num = len(self.__swt[i])
225                client_list = self.__swt[i]
226
227            if client_num <= 1:
228                fp.write("\t{ },\n")
229            else:
230                fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ")
231                for j in range(0, client_num):
232                    routine = client_list[j][1]
233                    arg = client_list[j][0]
234
235                    fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, "
236                            f".arg = (const void *){hex(arg)} }},")
237
238                fp.write(" },\n},\n")
239
240        fp.write("};\n")
241
242    def write_source(self, fp):
243        fp.write(self.source_header)
244
245        if self.__config.check_shared_interrupts():
246            self.__write_shared_table(fp)
247
248        if self.__vt:
249            if self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS"):
250                self.__write_address_irq_vector_table(fp)
251            elif self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE"):
252                self.__write_code_irq_vector_table(fp)
253            else:
254                self.__log.error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set")
255
256        if not self.__swt:
257            return
258
259        fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
260                % self.__nv)
261
262        level2_offset = self.__config.get_irq_baseoffset(2)
263        level3_offset = self.__config.get_irq_baseoffset(3)
264
265        for i in range(self.__nv):
266            if len(self.__swt[i]) == 0:
267                # Not used interrupt
268                param = "0x0"
269                func = self.__config.swt_spurious_handler
270            elif len(self.__swt[i]) == 1:
271                # Single interrupt
272                param = "{0:#x}".format(self.__swt[i][0][0])
273                func = self.__swt[i][0][1]
274            else:
275                # Shared interrupt
276                param = "&z_shared_sw_isr_table[{0}]".format(i)
277                func = self.__config.swt_shared_handler
278
279            if isinstance(func, int):
280                func_as_string = "{0:#x}".format(func)
281            else:
282                func_as_string = func
283
284            if level2_offset is not None and i == level2_offset:
285                fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n".
286                         format(level2_offset))
287            if level3_offset is not None and i == level3_offset:
288                fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
289                         format(level3_offset))
290
291            fp.write("\t{{(const void *){0}, (ISR){1}}}, /* {2} */\n".format(param, func_as_string, i))
292        fp.write("};\n")
293