| #!/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 argparse |
| import sys |
| import os |
| import importlib |
| from elftools.elf.elffile import ELFFile |
| from elftools.elf.sections import SymbolTableSection |
| |
| |
| class gen_isr_log: |
| |
| def __init__(self, debug = False): |
| self.__debug = debug |
| |
| def debug(self, text): |
| """Print debug message if debugging is enabled. |
| |
| Note - this function requires config global variable to be initialized. |
| """ |
| if self.__debug: |
| sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
| |
| @staticmethod |
| def error(text): |
| sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n") |
| |
| def set_debug(self, state): |
| self.__debug = state |
| |
| |
| log = gen_isr_log() |
| |
| |
| class gen_isr_config: |
| """All the constants and configuration gathered in single class for readability. |
| """ |
| # Constants |
| __ISR_FLAG_DIRECT = 1 << 0 |
| __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): |
| return (1 << bits) - 1 |
| |
| def __init__(self, args, syms, log): |
| """Initialize the configuration object. |
| |
| The configuration object initialization takes only arguments as a parameter. |
| This is done to allow debug function work as soon as possible. |
| """ |
| # Store the arguments required for work |
| self.__args = args |
| self.__syms = syms |
| self.__log = log |
| |
| # Select the default interrupt vector handler |
| if self.args.sw_isr_table: |
| self.__vt_default_handler = self.__vt_irq_handler |
| else: |
| self.__vt_default_handler = self.__vt_spurious_handler |
| # Calculate interrupt bits |
| self.__int_bits = [8, 8, 8] |
| # The below few hardware independent magic numbers represent various |
| # levels of interrupts in a multi-level interrupt system. |
| # 0x000000FF - represents the 1st level (i.e. the interrupts |
| # that directly go to the processor). |
| # 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel |
| # into 1 line which then goes into the 1st level) |
| # 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel |
| # into 1 line which then goes into the 2nd level) |
| self.__int_lvl_masks = [0x000000FF, 0x0000FF00, 0x00FF0000] |
| |
| self.__irq2_baseoffset = None |
| self.__irq3_baseoffset = None |
| self.__irq2_offsets = None |
| self.__irq3_offsets = None |
| |
| if self.check_multi_level_interrupts(): |
| self.__max_irq_per = self.get_sym("CONFIG_MAX_IRQ_PER_AGGREGATOR") |
| |
| self.__int_bits[0] = self.get_sym("CONFIG_1ST_LEVEL_INTERRUPT_BITS") |
| self.__int_bits[1] = self.get_sym("CONFIG_2ND_LEVEL_INTERRUPT_BITS") |
| self.__int_bits[2] = self.get_sym("CONFIG_3RD_LEVEL_INTERRUPT_BITS") |
| |
| if sum(self.int_bits) > 32: |
| raise ValueError("Too many interrupt bits") |
| |
| self.__int_lvl_masks[0] = self.__bm(self.int_bits[0]) |
| self.__int_lvl_masks[1] = self.__bm(self.int_bits[1]) << self.int_bits[0] |
| self.__int_lvl_masks[2] = self.__bm(self.int_bits[2]) << (self.int_bits[0] + self.int_bits[1]) |
| |
| self.__log.debug("Level Bits Bitmask") |
| self.__log.debug("----------------------------") |
| for i in range(3): |
| bitmask_str = "0x" + format(self.__int_lvl_masks[i], '08X') |
| self.__log.debug(f"{i + 1:>5} {self.__int_bits[i]:>7} {bitmask_str:>14}") |
| |
| if self.check_sym("CONFIG_2ND_LEVEL_INTERRUPTS"): |
| num_aggregators = self.get_sym("CONFIG_NUM_2ND_LEVEL_AGGREGATORS") |
| self.__irq2_baseoffset = self.get_sym("CONFIG_2ND_LVL_ISR_TBL_OFFSET") |
| self.__irq2_offsets = [self.get_sym('CONFIG_2ND_LVL_INTR_{}_OFFSET'. |
| format(str(i).zfill(2))) for i in |
| range(num_aggregators)] |
| |
| self.__log.debug('2nd level offsets: {}'.format(self.__irq2_offsets)) |
| |
| if self.check_sym("CONFIG_3RD_LEVEL_INTERRUPTS"): |
| num_aggregators = self.get_sym("CONFIG_NUM_3RD_LEVEL_AGGREGATORS") |
| self.__irq3_baseoffset = self.get_sym("CONFIG_3RD_LVL_ISR_TBL_OFFSET") |
| self.__irq3_offsets = [self.get_sym('CONFIG_3RD_LVL_INTR_{}_OFFSET'. |
| format(str(i).zfill(2))) for i in |
| range(num_aggregators)] |
| |
| self.__log.debug('3rd level offsets: {}'.format(self.__irq3_offsets)) |
| |
| @property |
| def args(self): |
| return self.__args |
| |
| @property |
| def swt_spurious_handler(self): |
| return self.__swt_spurious_handler |
| |
| @property |
| def swt_shared_handler(self): |
| return self.__swt_shared_handler |
| |
| @property |
| 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 |
| |
| @property |
| def int_lvl_masks(self): |
| return self.__int_lvl_masks |
| |
| def endian_prefix(self): |
| if self.args.big_endian: |
| return ">" |
| else: |
| return "<" |
| |
| def get_irq_baseoffset(self, lvl): |
| if lvl == 2: |
| return self.__irq2_baseoffset |
| if lvl == 3: |
| return self.__irq3_baseoffset |
| self.__log.error("Unsupported irq level: {}".format(lvl)) |
| |
| def get_irq_index(self, irq, lvl): |
| if lvl == 2: |
| offsets = self.__irq2_offsets |
| elif lvl == 3: |
| offsets = self.__irq3_offsets |
| else: |
| self.__log.error("Unsupported irq level: {}".format(lvl)) |
| try: |
| return offsets.index(irq) |
| except ValueError: |
| self.__log.error("IRQ {} not present in parent offsets ({}). ". |
| format(irq, offsets) + |
| " Recheck interrupt configuration.") |
| |
| def get_swt_table_index(self, offset, irq): |
| if not self.check_multi_level_interrupts(): |
| return irq - offset |
| # Calculate index for multi level interrupts |
| self.__log.debug('IRQ = ' + hex(irq)) |
| irq3 = (irq & self.int_lvl_masks[2]) >> (self.int_bits[0] + self.int_bits[1]) |
| irq2 = (irq & self.int_lvl_masks[1]) >> (self.int_bits[0]) |
| irq1 = irq & self.int_lvl_masks[0] |
| # Figure out third level interrupt position |
| if irq3: |
| list_index = self.get_irq_index(irq2 - 1, 3) |
| irq3_pos = self.get_irq_baseoffset(3) + self.__max_irq_per * list_index + irq3 - 1 |
| self.__log.debug('IRQ_level = 3') |
| self.__log.debug('IRQ_Indx = ' + str(irq3)) |
| self.__log.debug('IRQ_Pos = ' + str(irq3_pos)) |
| return irq3_pos - offset |
| # Figure out second level interrupt position |
| if irq2: |
| list_index = self.get_irq_index(irq1, 2) |
| irq2_pos = self.get_irq_baseoffset(2) + self.__max_irq_per * list_index + irq2 - 1 |
| self.__log.debug('IRQ_level = 2') |
| self.__log.debug('IRQ_Indx = ' + str(irq2)) |
| self.__log.debug('IRQ_Pos = ' + str(irq2_pos)) |
| return irq2_pos - offset |
| # Figure out first level interrupt position |
| self.__log.debug('IRQ_level = 1') |
| self.__log.debug('IRQ_Indx = ' + str(irq1)) |
| self.__log.debug('IRQ_Pos = ' + str(irq1)) |
| return irq1 - offset |
| |
| def get_intlist_snames(self): |
| return self.args.intlist_section |
| |
| def test_isr_direct(self, flags): |
| return flags & self.__ISR_FLAG_DIRECT |
| |
| def get_sym_from_addr(self, addr): |
| for key, value in self.__syms.items(): |
| if addr == value: |
| return key |
| return None |
| |
| def get_sym(self, name): |
| return self.__syms.get(name) |
| |
| def check_sym(self, name): |
| return name in self.__syms |
| |
| def check_multi_level_interrupts(self): |
| return self.check_sym("CONFIG_MULTI_LEVEL_INTERRUPTS") |
| |
| def check_shared_interrupts(self): |
| return self.check_sym("CONFIG_SHARED_INTERRUPTS") |
| |
| def check_64b(self): |
| return self.check_sym("CONFIG_64BIT") |
| |
| |
| def get_symbols(obj): |
| for section in obj.iter_sections(): |
| if isinstance(section, SymbolTableSection): |
| return {sym.name: sym.entry.st_value |
| for sym in section.iter_symbols()} |
| |
| log.error("Could not find symbol table") |
| |
| def read_intList_sect(elfobj, snames): |
| """ |
| Load the raw intList section data in a form of byte array. |
| """ |
| intList_sect = None |
| |
| for sname in snames: |
| intList_sect = elfobj.get_section_by_name(sname) |
| if intList_sect is not None: |
| log.debug("Found intlist section: \"{}\"".format(sname)) |
| break |
| |
| if intList_sect is None: |
| log.error("Cannot find the intlist section!") |
| |
| intdata = intList_sect.data() |
| |
| return intdata |
| |
| def parse_args(): |
| parser = argparse.ArgumentParser(description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) |
| |
| parser.add_argument("-e", "--big-endian", action="store_true", |
| help="Target encodes data in big-endian format (little endian is " |
| "the default)") |
| parser.add_argument("-d", "--debug", action="store_true", |
| 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", |
| help="Generate SW ISR table") |
| parser.add_argument("-V", "--vector-table", action="store_true", |
| help="Generate vector table") |
| parser.add_argument("-i", "--intlist-section", action="append", required=True, |
| help="The name of the section to search for the interrupt data. " |
| "This is accumulative argument. The first section found would be used.") |
| |
| return parser.parse_args() |
| |
| def main(): |
| args = parse_args() |
| # Configure logging as soon as possible |
| log.set_debug(args.debug) |
| |
| with open(args.kernel, "rb") as fp: |
| kernel = ELFFile(fp) |
| config = gen_isr_config(args, get_symbols(kernel), log) |
| intlist_data = read_intList_sect(kernel, config.get_intlist_snames()) |
| |
| if config.check_sym("CONFIG_ISR_TABLES_LOCAL_DECLARATION"): |
| 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("Chosen 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("Chosen parser does not support software interrupt linker file") |
| fp_swi.write('/* Empty */\n') |
| |
| if __name__ == "__main__": |
| main() |