#!/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 not vt: self.__log.error("Direct Interrupt %d declared with parameter 0x%x " "but no vector table in use" % (irq, param)) 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}}}, /* {2} */\n".format(param, func_as_string, i)) fp.write("};\n")