diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b0af4e34a3f90a..82caef7726f6a12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1226,10 +1226,11 @@ if(CONFIG_GEN_ISR_TABLES) # isr_tables.c is generated from ${ZEPHYR_LINK_STAGE_EXECUTABLE} by # gen_isr_tables.py add_custom_command( - OUTPUT isr_tables.c + OUTPUT isr_tables.c isr_tables_vt.ld isr_tables_swi.ld COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/gen_isr_tables.py --output-source isr_tables.c + --linker-output-files isr_tables_vt.ld isr_tables_swi.ld --kernel $ --intlist-section .intList --intlist-section intList diff --git a/arch/arm/core/CMakeLists.txt b/arch/arm/core/CMakeLists.txt index 1b4fa58eae5c115..680e21fdb59d62e 100644 --- a/arch/arm/core/CMakeLists.txt +++ b/arch/arm/core/CMakeLists.txt @@ -34,3 +34,11 @@ else() zephyr_linker_sources(ROM_START SORT_KEY 0x0vectors vector_table.ld) zephyr_linker_sources(ROM_START SORT_KEY 0x1vectors cortex_m/vector_table_pad.ld) endif() + +if(CONFIG_GEN_SW_ISR_TABLE) + if(CONFIG_DYNAMIC_INTERRUPTS) + zephyr_linker_sources(RWDATA swi_tables.ld) + else() + zephyr_linker_sources(RODATA swi_tables.ld) + endif() +endif() diff --git a/arch/arm/core/swi_tables.ld b/arch/arm/core/swi_tables.ld new file mode 100644 index 000000000000000..c6ca94604dda957 --- /dev/null +++ b/arch/arm/core/swi_tables.ld @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#if LINKER_ZEPHYR_FINAL +INCLUDE zephyr/isr_tables_swi.ld +#endif diff --git a/arch/arm/core/vector_table.ld b/arch/arm/core/vector_table.ld index 615067dbf7483fa..47647fd61d042f3 100644 --- a/arch/arm/core/vector_table.ld +++ b/arch/arm/core/vector_table.ld @@ -53,4 +53,8 @@ KEEP(*(".exc_vector_table.*")) KEEP(*(.vectors)) +#if LINKER_ZEPHYR_FINAL +INCLUDE zephyr/isr_tables_vt.ld +#endif + _vector_end = .; diff --git a/arch/arm64/core/CMakeLists.txt b/arch/arm64/core/CMakeLists.txt index 1804556c1a72b1e..05e4be8c0ea6da8 100644 --- a/arch/arm64/core/CMakeLists.txt +++ b/arch/arm64/core/CMakeLists.txt @@ -55,3 +55,11 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU") endif() add_subdirectory_ifdef(CONFIG_XEN xen) + +if(CONFIG_GEN_SW_ISR_TABLE) + if(CONFIG_DYNAMIC_INTERRUPTS) + zephyr_linker_sources(RWDATA swi_tables.ld) + else() + zephyr_linker_sources(RODATA swi_tables.ld) + endif() +endif() diff --git a/arch/arm64/core/swi_tables.ld b/arch/arm64/core/swi_tables.ld new file mode 100644 index 000000000000000..c6ca94604dda957 --- /dev/null +++ b/arch/arm64/core/swi_tables.ld @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#if LINKER_ZEPHYR_FINAL +INCLUDE zephyr/isr_tables_swi.ld +#endif diff --git a/arch/common/isr_tables.c b/arch/common/isr_tables.c index 0311f81f25240ff..db5f11af0ec896d 100644 --- a/arch/common/isr_tables.c +++ b/arch/common/isr_tables.c @@ -15,6 +15,11 @@ struct int_list_header { uint32_t table_size; uint32_t offset; +#if IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION) + uint32_t swi_table_entry_size; + uint32_t shared_isr_table_entry_size; + uint32_t shared_isr_client_num_offset; +#endif /* IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION) */ }; /* These values are not included in the resulting binary, but instead form the @@ -24,6 +29,11 @@ struct int_list_header { Z_GENERIC_SECTION(.irq_info) struct int_list_header _iheader = { .table_size = IRQ_TABLE_SIZE, .offset = CONFIG_GEN_IRQ_START_VECTOR, +#if IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION) + .swi_table_entry_size = sizeof(struct _isr_table_entry), + .shared_isr_table_entry_size = sizeof(struct z_shared_isr_table_entry), + .shared_isr_client_num_offset = offsetof(struct z_shared_isr_table_entry, client_num), +#endif /* IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION) */ }; /* These are placeholder tables. They will be replaced by the real tables diff --git a/include/zephyr/arch/arm64/scripts/linker.ld b/include/zephyr/arch/arm64/scripts/linker.ld index fa08b7303047853..3269bfa2e1cdea0 100644 --- a/include/zephyr/arch/arm64/scripts/linker.ld +++ b/include/zephyr/arch/arm64/scripts/linker.ld @@ -107,6 +107,9 @@ SECTIONS KEEP(*(.vectors)) + #if LINKER_ZEPHYR_FINAL + INCLUDE zephyr/isr_tables_vt.ld + #endif _vector_end = .; *(.text) diff --git a/include/zephyr/sw_isr_table.h b/include/zephyr/sw_isr_table.h index 8410c40db06b4d5..cdeb576bd952e03 100644 --- a/include/zephyr/sw_isr_table.h +++ b/include/zephyr/sw_isr_table.h @@ -92,8 +92,8 @@ struct _isr_list_sname { #ifdef CONFIG_SHARED_INTERRUPTS struct z_shared_isr_client { - void (*isr)(const void *arg); const void *arg; + void (*isr)(const void *arg); }; struct z_shared_isr_table_entry { diff --git a/scripts/build/gen_isr_tables.py b/scripts/build/gen_isr_tables.py index 1a00eeb6f8a08c7..51da2878bf4c609 100755 --- a/scripts/build/gen_isr_tables.py +++ b/scripts/build/gen_isr_tables.py @@ -11,6 +11,7 @@ import struct import sys import os +import importlib from elftools.elf.elffile import ELFFile from elftools.elf.sections import SymbolTableSection @@ -44,10 +45,13 @@ class gen_isr_config: """ # Constants __ISR_FLAG_DIRECT = 1 << 0 - __swt_spurious_handler = "((uintptr_t)&z_irq_spurious)" - __swt_shared_handler = "((uintptr_t)&z_shared_isr)" + __swt_spurious_handler = "z_irq_spurious" + __swt_shared_handler = "z_shared_isr" __vt_spurious_handler = "z_irq_spurious" __vt_irq_handler = "_isr_wrapper" + __shared_array_name = "z_shared_sw_isr_table" + __sw_isr_array_name = "_sw_isr_table" + __irq_vector_array_name = "_irq_vector_table" @staticmethod def __bm(bits): @@ -134,6 +138,18 @@ def swt_shared_handler(self): def vt_default_handler(self): return self.__vt_default_handler + @property + def shared_array_name(self): + return self.__shared_array_name + + @property + def sw_isr_array_name(self): + return self.__sw_isr_array_name + + @property + def irq_vector_array_name(self): + return self.__irq_vector_array_name + @property def int_bits(self): return self.__int_bits @@ -227,285 +243,6 @@ def check_64b(self): return self.check_sym("CONFIG_64BIT") -class gen_isr_parser: - source_header = """ -/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */ - -#include -#include -#include -#include - -typedef void (* ISR)(const void *); -""" - - source_assembly_header = """ -#ifndef ARCH_IRQ_VECTOR_JUMP_CODE -#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined" -#endif -""" - - def __init__(self, intlist_data, config, log): - """Initialize the parser. - - The function prepares parser to work. - Parameters: - - intlist_data: The binnary data from intlist section - - config: The configuration object - - log: The logging object, has to have error and debug methods - """ - self.__config = config - self.__log = log - intlist = self.__read_intlist(intlist_data) - self.__vt, self.__swt, self.__nv = self.__parse_intlist(intlist) - - def __read_intlist(self, intlist_data): - """read a binary file containing the contents of the kernel's .intList - section. This is an instance of a header created by - include/zephyr/linker/intlist.ld: - - struct { - uint32_t num_vectors; <- typically CONFIG_NUM_IRQS - struct _isr_list isrs[]; <- Usually of smaller size than num_vectors - } - - Followed by instances of struct _isr_list created by IRQ_CONNECT() - calls: - - struct _isr_list { - /** IRQ line number */ - int32_t irq; - /** Flags for this IRQ, see ISR_FLAG_* definitions */ - int32_t flags; - /** ISR to call */ - void *func; - /** Parameter for non-direct IRQs */ - const void *param; - }; - """ - intlist = {} - prefix = self.__config.endian_prefix() - - # Extract header and the rest of the data - intlist_header_fmt = prefix + "II" - header_sz = struct.calcsize(intlist_header_fmt) - header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0) - self.__log.debug(str(header_raw)) - - intlist["num_vectors"] = header_raw[0] - intlist["offset"] = header_raw[1] - intdata = intlist_data[header_sz:] - - # Extract information about interrupts - if self.__config.check_64b(): - intlist_entry_fmt = prefix + "iiQQ" - else: - intlist_entry_fmt = prefix + "iiII" - - intlist["interrupts"] = [i for i in - struct.iter_unpack(intlist_entry_fmt, intdata)] - - self.__log.debug("Configured interrupt routing") - self.__log.debug("handler irq flags param") - self.__log.debug("--------------------------") - - for irq in intlist["interrupts"]: - self.__log.debug("{0:<10} {1:<3} {2:<3} {3}".format( - hex(irq[2]), irq[0], irq[1], hex(irq[3]))) - - return intlist - - def __parse_intlist(self, intlist): - """All the intlist data are parsed into swt and vt arrays. - - The vt array is prepared for hardware interrupt table. - Every entry in the selected position would contain None or the name of the function pointer - (address or string). - - The swt is a little more complex. At every position it would contain an array of parameter and - function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry. - If empty array is placed on selected position - it means that the application does not implement - this interrupt. - - Parameters: - - intlist: The preprocessed list of intlist section content (see read_intlist) - - Return: - vt, swt - parsed vt and swt arrays (see function description above) - """ - nvec = intlist["num_vectors"] - offset = intlist["offset"] - - if nvec > pow(2, 15): - raise ValueError('nvec is too large, check endianness.') - - self.__log.debug('offset is ' + str(offset)) - self.__log.debug('num_vectors is ' + str(nvec)) - - # Set default entries in both tables - if not(self.__config.args.sw_isr_table or self.__config.args.vector_table): - self.__log.error("one or both of -s or -V needs to be specified on command line") - if self.__config.args.vector_table: - vt = [None for i in range(nvec)] - else: - vt = None - if self.__config.args.sw_isr_table: - swt = [[] for i in range(nvec)] - else: - swt = None - - # Process intlist and write to the tables created - for irq, flags, func, param in intlist["interrupts"]: - if self.__config.test_isr_direct(flags): - if param != 0: - self.__log.error("Direct irq %d declared, but has non-NULL parameter" - % irq) - if not 0 <= irq - offset < len(vt): - self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % - (irq - offset, offset, len(vt) - 1)) - vt[irq - offset] = func - else: - # Regular interrupt - if not swt: - self.__log.error("Regular Interrupt %d declared with parameter 0x%x " - "but no SW ISR_TABLE in use" - % (irq, param)) - - table_index = self.__config.get_swt_table_index(offset, irq) - - if not 0 <= table_index < len(swt): - self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % - (table_index, offset, len(swt) - 1)) - if self.__config.check_shared_interrupts(): - lst = swt[table_index] - if (param, func) in lst: - self.__log.error("Attempting to register the same ISR/arg pair twice.") - if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"): - self.__log.error(f"Reached shared interrupt client limit. Maybe increase" - + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?") - else: - if len(swt[table_index]) > 0: - self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" - + f"\nExisting handler 0x{swt[table_index][0][1]:x}, new handler 0x{func:x}" - + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" - ) - swt[table_index].append((param, func)) - - return vt, swt, nvec - - def __write_code_irq_vector_table(self, fp): - fp.write(self.source_assembly_header) - - fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n") - for i in range(self.__nv): - func = self.__vt[i] - - if func is None: - func = self.__config.vt_default_handler - - if isinstance(func, int): - func_as_string = self.__config.get_sym_from_addr(func) - else: - func_as_string = func - - fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string)) - fp.write("}\n") - - def __write_address_irq_vector_table(self, fp): - fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % self.__nv) - for i in range(self.__nv): - func = self.__vt[i] - - if func is None: - func = self.__config.vt_default_handler - - if isinstance(func, int): - fp.write("\t{},\n".format(func)) - else: - fp.write("\t((uintptr_t)&{}),\n".format(func)) - - fp.write("};\n") - - def __write_shared_table(self, fp): - fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table" - " z_shared_sw_isr_table[%d] = {\n" % self.__nv) - - for i in range(self.__nv): - if self.__swt[i] is None: - client_num = 0 - client_list = None - else: - client_num = len(self.__swt[i]) - client_list = self.__swt[i] - - if client_num <= 1: - fp.write("\t{ },\n") - else: - fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ") - for j in range(0, client_num): - routine = client_list[j][1] - arg = client_list[j][0] - - fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, " - f".arg = (const void *){hex(arg)} }},") - - fp.write(" },\n},\n") - - fp.write("};\n") - - def write_source(self, fp): - fp.write(self.source_header) - - if self.__config.check_shared_interrupts(): - self.__write_shared_table(fp) - - if self.__vt: - if self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS"): - self.__write_address_irq_vector_table(fp) - elif self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE"): - self.__write_code_irq_vector_table(fp) - else: - self.__log.error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set") - - if not self.__swt: - return - - fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n" - % self.__nv) - - level2_offset = self.__config.get_irq_baseoffset(2) - level3_offset = self.__config.get_irq_baseoffset(3) - - for i in range(self.__nv): - if len(self.__swt[i]) == 0: - # Not used interrupt - param = "0x0" - func = self.__config.swt_spurious_handler - elif len(self.__swt[i]) == 1: - # Single interrupt - param = "{0:#x}".format(self.__swt[i][0][0]) - func = self.__swt[i][0][1] - else: - # Shared interrupt - param = "&z_shared_sw_isr_table[{0}]".format(i) - func = self.__config.swt_shared_handler - - if isinstance(func, int): - func_as_string = "{0:#x}".format(func) - else: - func_as_string = func - - if level2_offset is not None and i == level2_offset: - fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n". - format(level2_offset)) - if level3_offset is not None and i == level3_offset: - fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n". - format(level3_offset)) - - fp.write("\t{{(const void *){0}, (ISR){1}}},\n".format(param, func_as_string)) - fp.write("};\n") - - def get_symbols(obj): for section in obj.iter_sections(): if isinstance(section, SymbolTableSection): @@ -544,6 +281,12 @@ def parse_args(): help="Print additional debugging information") parser.add_argument("-o", "--output-source", required=True, help="Output source file") + parser.add_argument("-l", "--linker-output-files", + nargs=2, + metavar=("vector_table_link", "software_interrupt_link"), + help="Output linker files. " + "Used only if CONFIG_ISR_TABLES_LOCAL_DECLARATION is enabled. " + "In other case empty file would be generated.") parser.add_argument("-k", "--kernel", required=True, help="Zephyr kernel image") parser.add_argument("-s", "--sw-isr-table", action="store_true", @@ -566,11 +309,31 @@ def main(): config = gen_isr_config(args, get_symbols(kernel), log) intlist_data = read_intList_sect(kernel, config.get_intlist_snames()) - parser = gen_isr_parser(intlist_data, config, log) + if config.check_sym("CONFIG_ISR_TABLES_LOCAL_DECLARATION"): + sys.stdout.write( + "Warning: The EXPERIMENTAL ISR_TABLES_LOCAL_DECLARATION feature selected\n") + parser_module = importlib.import_module('gen_isr_tables_parser_local') + parser = parser_module.gen_isr_parser(intlist_data, config, log) + else: + parser_module = importlib.import_module('gen_isr_tables_parser_carrays') + parser = parser_module.gen_isr_parser(intlist_data, config, log) with open(args.output_source, "w") as fp: parser.write_source(fp) + if (args.linker_output_files is not None): + with open(args.linker_output_files[0], "w") as fp_vt, \ + open(args.linker_output_files[1], "w") as fp_swi: + if (hasattr(parser, 'write_linker_vt')): + parser.write_linker_vt(fp_vt) + else: + log.debug("Choosen parser does not support vector table linker file") + fp_vt.write('/* Empty */\n') + if (hasattr(parser, 'write_linker_swi')): + parser.write_linker_swi(fp_swi) + else: + log.debug("Choosen parser does not support software interrupt linker file") + fp_swi.write('/* Empty */\n') if __name__ == "__main__": main() diff --git a/scripts/build/gen_isr_tables_parser_carrays.py b/scripts/build/gen_isr_tables_parser_carrays.py new file mode 100644 index 000000000000000..09f826b18e9b32b --- /dev/null +++ b/scripts/build/gen_isr_tables_parser_carrays.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2017 Intel Corporation +# Copyright (c) 2018 Foundries.io +# Copyright (c) 2023 Nordic Semiconductor NA +# +# SPDX-License-Identifier: Apache-2.0 +# + +import struct + +class gen_isr_parser: + source_header = """ +/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */ + +#include +#include +#include +#include + +typedef void (* ISR)(const void *); +""" + + source_assembly_header = """ +#ifndef ARCH_IRQ_VECTOR_JUMP_CODE +#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined" +#endif +""" + + def __init__(self, intlist_data, config, log): + """Initialize the parser. + + The function prepares parser to work. + Parameters: + - intlist_data: The binnary data from intlist section + - config: The configuration object + - log: The logging object, has to have error and debug methods + """ + self.__config = config + self.__log = log + intlist = self.__read_intlist(intlist_data) + self.__vt, self.__swt, self.__nv = self.__parse_intlist(intlist) + + def __read_intlist(self, intlist_data): + """read a binary file containing the contents of the kernel's .intList + section. This is an instance of a header created by + include/zephyr/linker/intlist.ld: + + struct { + uint32_t num_vectors; <- typically CONFIG_NUM_IRQS + struct _isr_list isrs[]; <- Usually of smaller size than num_vectors + } + + Followed by instances of struct _isr_list created by IRQ_CONNECT() + calls: + + struct _isr_list { + /** IRQ line number */ + int32_t irq; + /** Flags for this IRQ, see ISR_FLAG_* definitions */ + int32_t flags; + /** ISR to call */ + void *func; + /** Parameter for non-direct IRQs */ + const void *param; + }; + """ + intlist = {} + prefix = self.__config.endian_prefix() + + # Extract header and the rest of the data + intlist_header_fmt = prefix + "II" + header_sz = struct.calcsize(intlist_header_fmt) + header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0) + self.__log.debug(str(header_raw)) + + intlist["num_vectors"] = header_raw[0] + intlist["offset"] = header_raw[1] + intdata = intlist_data[header_sz:] + + # Extract information about interrupts + if self.__config.check_64b(): + intlist_entry_fmt = prefix + "iiQQ" + else: + intlist_entry_fmt = prefix + "iiII" + + intlist["interrupts"] = [i for i in + struct.iter_unpack(intlist_entry_fmt, intdata)] + + self.__log.debug("Configured interrupt routing") + self.__log.debug("handler irq flags param") + self.__log.debug("--------------------------") + + for irq in intlist["interrupts"]: + self.__log.debug("{0:<10} {1:<3} {2:<3} {3}".format( + hex(irq[2]), irq[0], irq[1], hex(irq[3]))) + + return intlist + + def __parse_intlist(self, intlist): + """All the intlist data are parsed into swt and vt arrays. + + The vt array is prepared for hardware interrupt table. + Every entry in the selected position would contain None or the name of the function pointer + (address or string). + + The swt is a little more complex. At every position it would contain an array of parameter and + function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry. + If empty array is placed on selected position - it means that the application does not implement + this interrupt. + + Parameters: + - intlist: The preprocessed list of intlist section content (see read_intlist) + + Return: + vt, swt - parsed vt and swt arrays (see function description above) + """ + nvec = intlist["num_vectors"] + offset = intlist["offset"] + + if nvec > pow(2, 15): + raise ValueError('nvec is too large, check endianness.') + + self.__log.debug('offset is ' + str(offset)) + self.__log.debug('num_vectors is ' + str(nvec)) + + # Set default entries in both tables + if not(self.__config.args.sw_isr_table or self.__config.args.vector_table): + self.__log.error("one or both of -s or -V needs to be specified on command line") + if self.__config.args.vector_table: + vt = [None for i in range(nvec)] + else: + vt = None + if self.__config.args.sw_isr_table: + swt = [[] for i in range(nvec)] + else: + swt = None + + # Process intlist and write to the tables created + for irq, flags, func, param in intlist["interrupts"]: + if self.__config.test_isr_direct(flags): + if param != 0: + self.__log.error("Direct irq %d declared, but has non-NULL parameter" + % irq) + if not 0 <= irq - offset < len(vt): + self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % + (irq - offset, offset, len(vt) - 1)) + vt[irq - offset] = func + else: + # Regular interrupt + if not swt: + self.__log.error("Regular Interrupt %d declared with parameter 0x%x " + "but no SW ISR_TABLE in use" + % (irq, param)) + + table_index = self.__config.get_swt_table_index(offset, irq) + + if not 0 <= table_index < len(swt): + self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % + (table_index, offset, len(swt) - 1)) + if self.__config.check_shared_interrupts(): + lst = swt[table_index] + if (param, func) in lst: + self.__log.error("Attempting to register the same ISR/arg pair twice.") + if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"): + self.__log.error(f"Reached shared interrupt client limit. Maybe increase" + + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?") + else: + if len(swt[table_index]) > 0: + self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" + + f"\nExisting handler 0x{swt[table_index][0][1]:x}, new handler 0x{func:x}" + + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" + ) + swt[table_index].append((param, func)) + + return vt, swt, nvec + + def __write_code_irq_vector_table(self, fp): + fp.write(self.source_assembly_header) + + fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n") + for i in range(self.__nv): + func = self.__vt[i] + + if func is None: + func = self.__config.vt_default_handler + + if isinstance(func, int): + func_as_string = self.__config.get_sym_from_addr(func) + else: + func_as_string = func + + fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string)) + fp.write("}\n") + + def __write_address_irq_vector_table(self, fp): + fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % self.__nv) + for i in range(self.__nv): + func = self.__vt[i] + + if func is None: + func = self.__config.vt_default_handler + + if isinstance(func, int): + fp.write("\t{},\n".format(func)) + else: + fp.write("\t((uintptr_t)&{}),\n".format(func)) + + fp.write("};\n") + + def __write_shared_table(self, fp): + fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table" + " z_shared_sw_isr_table[%d] = {\n" % self.__nv) + + for i in range(self.__nv): + if self.__swt[i] is None: + client_num = 0 + client_list = None + else: + client_num = len(self.__swt[i]) + client_list = self.__swt[i] + + if client_num <= 1: + fp.write("\t{ },\n") + else: + fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ") + for j in range(0, client_num): + routine = client_list[j][1] + arg = client_list[j][0] + + fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, " + f".arg = (const void *){hex(arg)} }},") + + fp.write(" },\n},\n") + + fp.write("};\n") + + def write_source(self, fp): + fp.write(self.source_header) + + if self.__config.check_shared_interrupts(): + self.__write_shared_table(fp) + + if self.__vt: + if self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS"): + self.__write_address_irq_vector_table(fp) + elif self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE"): + self.__write_code_irq_vector_table(fp) + else: + self.__log.error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set") + + if not self.__swt: + return + + fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n" + % self.__nv) + + level2_offset = self.__config.get_irq_baseoffset(2) + level3_offset = self.__config.get_irq_baseoffset(3) + + for i in range(self.__nv): + if len(self.__swt[i]) == 0: + # Not used interrupt + param = "0x0" + func = self.__config.swt_spurious_handler + elif len(self.__swt[i]) == 1: + # Single interrupt + param = "{0:#x}".format(self.__swt[i][0][0]) + func = self.__swt[i][0][1] + else: + # Shared interrupt + param = "&z_shared_sw_isr_table[{0}]".format(i) + func = self.__config.swt_shared_handler + + if isinstance(func, int): + func_as_string = "{0:#x}".format(func) + else: + func_as_string = func + + if level2_offset is not None and i == level2_offset: + fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n". + format(level2_offset)) + if level3_offset is not None and i == level3_offset: + fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n". + format(level3_offset)) + + fp.write("\t{{(const void *){0}, (ISR){1}}},\n".format(param, func_as_string)) + fp.write("};\n") diff --git a/scripts/build/gen_isr_tables_parser_local.py b/scripts/build/gen_isr_tables_parser_local.py new file mode 100644 index 000000000000000..9aad8374c3f578f --- /dev/null +++ b/scripts/build/gen_isr_tables_parser_local.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2017 Intel Corporation +# Copyright (c) 2018 Foundries.io +# Copyright (c) 2023 Nordic Semiconductor NA +# +# SPDX-License-Identifier: Apache-2.0 +# + +import struct + +class gen_isr_parser: + source_header = """ +/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */ + +#include +#include +#include +#include + +""" + + shared_isr_table_header = """ + +/* For this parser to work, we have to be sure that shared interrupts table entry + * and the normal isr table entry have exactly the same layout + */ +BUILD_ASSERT(sizeof(struct _isr_table_entry) + == + sizeof(struct z_shared_isr_table_entry), + "Shared ISR and ISR table entries layout do not match"); +BUILD_ASSERT(offsetof(struct _isr_table_entry, arg) + == + offsetof(struct z_shared_isr_table_entry, arg), + "Shared ISR and ISR table entries layout do not match"); +BUILD_ASSERT(offsetof(struct _isr_table_entry, isr) + == + offsetof(struct z_shared_isr_table_entry, isr), + "Shared ISR and ISR table entries layout do not match"); + +""" + + def __init__(self, intlist_data, config, log): + """Initialize the parser. + + The function prepares parser to work. + Parameters: + - intlist_data: The binnary data from intlist section + - config: The configuration object + - log: The logging object, has to have error and debug methods + """ + self.__config = config + self.__log = log + intlist = self.__read_intlist(intlist_data) + self.__vt, self.__swt, self.__nv, header = self.__parse_intlist(intlist) + self.__swi_table_entry_size = header["swi_table_entry_size"] + self.__shared_isr_table_entry_size = header["shared_isr_table_entry_size"] + self.__shared_isr_client_num_offset = header["shared_isr_client_num_offset"] + + def __read_intlist(self, intlist_data): + """read an intList section from the elf file. + This is version 2 of a header created by include/zephyr/linker/intlist.ld: + + struct { + uint32_t num_vectors; <- typically CONFIG_NUM_IRQS + uint8_t stream[]; <- the stream with the interrupt data + }; + + The stream is contained from variable lenght records in a form: + + struct _isr_list_sname { + /** IRQ line number */ + int32_t irq; + /** Flags for this IRQ, see ISR_FLAG_* definitions */ + int32_t flags; + /** The section name */ + const char sname[]; + }; + + The flexible array member here (sname) contains the name of the section where the structure + with interrupt data is located. + It is always Null-terminated string thous we have to search through the input data for the + structue end. + + """ + intlist = {} + prefix = self.__config.endian_prefix() + + # Extract header and the rest of the data + intlist_header_fmt = prefix + "IIIII" + header_sz = struct.calcsize(intlist_header_fmt) + header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0) + self.__log.debug(str(header_raw)) + + intlist["num_vectors"] = header_raw[0] + intlist["offset"] = header_raw[1] + intlist["swi_table_entry_size"] = header_raw[2] + intlist["shared_isr_table_entry_size"] = header_raw[3] + intlist["shared_isr_client_num_offset"] = header_raw[4] + + intdata = intlist_data[header_sz:] + + # Extract information about interrupts + intlist_entry_fmt = prefix + "ii" + entry_sz = struct.calcsize(intlist_entry_fmt) + intlist["interrupts"] = [] + + while (len(intdata) > entry_sz): + entry_raw = struct.unpack_from(intlist_entry_fmt, intdata, 0) + intdata = intdata[entry_sz:] + null_idx = intdata.find(0) + if (null_idx < 0): + self.__log.error("Cannot find sname null termination at IRQ{}".format(entry_raw[0])) + bname = intdata[:null_idx] + intdata = intdata[null_idx + 1:] + sname = bname.decode() + intlist["interrupts"].append([entry_raw[0], entry_raw[1], sname]) + self.__log.debug("Unpacked IRQ{}, flags: {}, sname: \"{}\"\n".format( + entry_raw[0], entry_raw[1], sname)) + + # If any data left at the end - it has to be all the way 0 - this is just a check + if (len(intdata) and not(all([d == 0 for d in intdata]))): + self.__log.error("Non-zero data found at the end of the intList data.\n") + + self.__log.debug("Configured interrupt routing with linker") + self.__log.debug("irq flags sname") + self.__log.debug("--------------------------") + + for irq in intlist["interrupts"]: + self.__log.debug("{0:<3} {1:<5} {2}".format( + hex(irq[0]), irq[1], irq[2])) + + return intlist + + def __parse_intlist(self, intlist): + """All the intlist data are parsed into swt and vt arrays. + + The vt array is prepared for hardware interrupt table. + Every entry in the selected position would contain None or the name of the function pointer + (address or string). + + The swt is a little more complex. At every position it would contain an array of parameter and + function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry. + If empty array is placed on selected position - it means that the application does not implement + this interrupt. + + Parameters: + - intlist: The preprocessed list of intlist section content (see read_intlist) + + Return: + vt, swt - parsed vt and swt arrays (see function description above) + """ + nvec = intlist["num_vectors"] + offset = intlist["offset"] + header = { + "swi_table_entry_size": intlist["swi_table_entry_size"], + "shared_isr_table_entry_size": intlist["shared_isr_table_entry_size"], + "shared_isr_client_num_offset": intlist["shared_isr_client_num_offset"] + } + + if nvec > pow(2, 15): + raise ValueError('nvec is too large, check endianness.') + + self.__log.debug('offset is ' + str(offset)) + self.__log.debug('num_vectors is ' + str(nvec)) + + # Set default entries in both tables + if not(self.__config.args.sw_isr_table or self.__config.args.vector_table): + self.__log.error("one or both of -s or -V needs to be specified on command line") + if self.__config.args.vector_table: + vt = [None for i in range(nvec)] + else: + vt = None + if self.__config.args.sw_isr_table: + swt = [[] for i in range(nvec)] + else: + swt = None + + # Process intlist and write to the tables created + for irq, flags, sname in intlist["interrupts"]: + if self.__config.test_isr_direct(flags): + if not 0 <= irq - offset < len(vt): + self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % + (irq - offset, offset, len(vt) - 1)) + vt[irq - offset] = sname + else: + # Regular interrupt + if not swt: + self.__log.error("Regular Interrupt %d declared with section name %s " + "but no SW ISR_TABLE in use" + % (irq, sname)) + + table_index = self.__config.get_swt_table_index(offset, irq) + + if not 0 <= table_index < len(swt): + self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % + (table_index, offset, len(swt) - 1)) + # Check if the given section name does not repeat outside of current interrupt + for i in range(nvec): + if i == irq: + continue + if sname in swt[i]: + self.__log.error(("Attempting to register the same section name \"{}\"for" + + "different interrupts: {} and {}").format(sname, i, irq)) + if self.__config.check_shared_interrupts(): + lst = swt[table_index] + if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"): + self.__log.error(f"Reached shared interrupt client limit. Maybe increase" + + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?") + else: + if len(swt[table_index]) > 0: + self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" + + f"\nExisting section {swt[table_index]}, new section {sname}" + + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" + ) + swt[table_index].append(sname) + + return vt, swt, nvec, header + + @staticmethod + def __irq_spurious_section(irq): + return 'irq_spurious.0x{:x}'.format(irq) + + @staticmethod + def __isr_generated_section(irq): + return 'isr_generated.0x{:x}'.format(irq) + + @staticmethod + def __shared_entry_section(irq, ent): + return 'isr_shared.0x{:x}_0x{:x}'.format(irq, ent) + + @staticmethod + def __shared_client_num_section(irq): + return 'isr_shared.0x{:x}_client_num'.format(irq) + + def __isr_spurious_entry(self, irq): + return '_Z_ISR_TABLE_ENTRY({irq}, {func}, NULL, {sect});'.format( + irq = irq, + func = self.__config.swt_spurious_handler, + sect = self.__isr_generated_section(irq) + ) + + def __isr_shared_entry(self, irq): + return '_Z_ISR_TABLE_ENTRY({irq}, {func}, {arg}, {sect});'.format( + irq = irq, + arg = '&{}[{}]'.format(self.__config.shared_array_name, irq), + func = self.__config.swt_shared_handler, + sect = self.__isr_generated_section(irq) + ) + + def __irq_spurious_entry(self, irq): + return '_Z_ISR_DIRECT_TABLE_ENTRY({irq}, {func}, {sect});'.format( + irq = irq, + func = self.__config.vt_default_handler, + sect = self.__irq_spurious_section(irq) + ) + + def __write_isr_handlers(self, fp): + for i in range(self.__nv): + if len(self.__swt[i]) <= 0: + fp.write(self.__isr_spurious_entry(i) + '\n') + elif len(self.__swt[i]) > 1: + # Connect to shared handlers + fp.write(self.__isr_shared_entry(i) + '\n') + else: + fp.write('/* ISR: {} implemented in app in "{}" section. */\n'.format( + i, self.__swt[i][0])) + + def __write_irq_handlers(self, fp): + for i in range(self.__nv): + if self.__vt[i] is None: + fp.write(self.__irq_spurious_entry(i) + '\n') + else: + pf.write('/* ISR: {} implemented in app. */\n'.format(i)) + + def __write_shared_handlers(self, fp): + fp.write("extern struct z_shared_isr_table_entry " + "{}[{}];\n".format(self.__config.shared_array_name, self.__nv)) + + shared_cnt = self.__config.get_sym('CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS') + for i in range(self.__nv): + swt_len = len(self.__swt[i]) + for j in range(shared_cnt): + if (swt_len <= 1) or (swt_len <= j): + # Add all unused entry + fp.write('static Z_DECL_ALIGN(struct z_shared_isr_client)\n' + + '\tZ_GENERIC_SECTION({})\n'.format(self.__shared_entry_section(i, j)) + + '\t__used isr_shared_empty_entry_0x{:x}_0x{:x} = {{\n'.format(i, j) + + '\t\t.arg = (const void *)NULL,\n' + + '\t\t.isr = (void (*)(const void *))(void *)0\n' + + '};\n' + ) + else: + # Add information about entry implemented by application + fp.write('/* Shared isr {} entry {} implemented in "{}" section*/\n'.format( + i, j, self.__swt[i][j])) + + # Add information about clients count + fp.write(('static size_t Z_GENERIC_SECTION({}) __used\n' + + 'isr_shared_client_num_0x{:x} = {};\n\n').format( + self.__shared_client_num_section(i), + i, + 0 if swt_len < 2 else swt_len) + ) + + def write_source(self, fp): + fp.write(self.source_header) + + if self.__vt: + self.__write_irq_handlers(fp) + + if not self.__swt: + return + + if self.__config.check_shared_interrupts(): + self.__write_shared_handlers(fp) + + self.__write_isr_handlers(fp) + + def __write_linker_irq(self, fp): + fp.write('{} = .;\n'.format(self.__config.irq_vector_array_name)) + for i in range(self.__nv): + if self.__vt[i] is None: + sname = self.__irq_spurious_section(i) + else: + sname = self.__vt[i] + fp.write('KEEP(*("{}"))\n'.format(sname)) + + def __write_linker_shared(self, fp): + fp.write(". = ALIGN({});\n".format(self.__shared_isr_table_entry_size)) + fp.write('{} = .;\n'.format(self.__config.shared_array_name)) + shared_cnt = self.__config.get_sym('CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS') + client_num_pads = self.__shared_isr_client_num_offset - \ + shared_cnt * self.__swi_table_entry_size + if client_num_pads < 0: + self.__log.error("Invalid __shared_isr_client_num_offset header value") + for i in range(self.__nv): + swt_len = len(self.__swt[i]) + # Add all entries + for j in range(shared_cnt): + if (swt_len <= 1) or (swt_len <= j): + fp.write('KEEP(*("{}"))\n'.format(self.__shared_entry_section(i, j))) + else: + sname = self.__swt[i][j] + if (j != 0) and (sname in self.__swt[i][0:j]): + fp.write('/* Repetition of "{}" section */\n'.format(sname)) + else: + fp.write('KEEP(*("{}"))\n'.format(sname)) + fp.write('. = . + {};\n'.format(client_num_pads)) + fp.write('KEEP(*("{}"))\n'.format(self.__shared_client_num_section(i))) + fp.write(". = ALIGN({});\n".format(self.__shared_isr_table_entry_size)) + + def __write_linker_isr(self, fp): + fp.write(". = ALIGN({});\n".format(self.__swi_table_entry_size)) + fp.write('{} = .;\n'.format(self.__config.sw_isr_array_name)) + for i in range(self.__nv): + if (len(self.__swt[i])) == 1: + sname = self.__swt[i][0] + else: + sname = self.__isr_generated_section(i) + fp.write('KEEP(*("{}"))\n'.format(sname)) + + def write_linker_vt(self, fp): + if self.__vt: + self.__write_linker_irq(fp) + + def write_linker_swi(self, fp): + if self.__swt: + self.__write_linker_isr(fp) + + if self.__config.check_shared_interrupts(): + self.__write_linker_shared(fp)