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 argparse 11import sys 12import os 13import importlib 14from elftools.elf.elffile import ELFFile 15from elftools.elf.sections import SymbolTableSection 16 17 18class gen_isr_log: 19 20 def __init__(self, debug = False): 21 self.__debug = debug 22 23 def debug(self, text): 24 """Print debug message if debugging is enabled. 25 26 Note - this function requires config global variable to be initialized. 27 """ 28 if self.__debug: 29 sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") 30 31 @staticmethod 32 def error(text): 33 sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n") 34 35 def set_debug(self, state): 36 self.__debug = state 37 38 39log = gen_isr_log() 40 41 42class gen_isr_config: 43 """All the constants and configuration gathered in single class for readability. 44 """ 45 # Constants 46 __ISR_FLAG_DIRECT = 1 << 0 47 __swt_spurious_handler = "z_irq_spurious" 48 __swt_shared_handler = "z_shared_isr" 49 __vt_spurious_handler = "z_irq_spurious" 50 __vt_irq_handler = "_isr_wrapper" 51 __shared_array_name = "z_shared_sw_isr_table" 52 __sw_isr_array_name = "_sw_isr_table" 53 __irq_vector_array_name = "_irq_vector_table" 54 55 @staticmethod 56 def __bm(bits): 57 return (1 << bits) - 1 58 59 def __init__(self, args, syms, log): 60 """Initialize the configuration object. 61 62 The configuration object initialization takes only arguments as a parameter. 63 This is done to allow debug function work as soon as possible. 64 """ 65 # Store the arguments required for work 66 self.__args = args 67 self.__syms = syms 68 self.__log = log 69 70 # Select the default interrupt vector handler 71 if self.args.sw_isr_table: 72 self.__vt_default_handler = self.__vt_irq_handler 73 else: 74 self.__vt_default_handler = self.__vt_spurious_handler 75 # Calculate interrupt bits 76 self.__int_bits = [8, 8, 8] 77 # The below few hardware independent magic numbers represent various 78 # levels of interrupts in a multi-level interrupt system. 79 # 0x000000FF - represents the 1st level (i.e. the interrupts 80 # that directly go to the processor). 81 # 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel 82 # into 1 line which then goes into the 1st level) 83 # 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel 84 # into 1 line which then goes into the 2nd level) 85 self.__int_lvl_masks = [0x000000FF, 0x0000FF00, 0x00FF0000] 86 87 self.__irq2_baseoffset = None 88 self.__irq3_baseoffset = None 89 self.__irq2_offsets = None 90 self.__irq3_offsets = None 91 92 if self.check_multi_level_interrupts(): 93 self.__max_irq_per = self.get_sym("CONFIG_MAX_IRQ_PER_AGGREGATOR") 94 95 self.__int_bits[0] = self.get_sym("CONFIG_1ST_LEVEL_INTERRUPT_BITS") 96 self.__int_bits[1] = self.get_sym("CONFIG_2ND_LEVEL_INTERRUPT_BITS") 97 self.__int_bits[2] = self.get_sym("CONFIG_3RD_LEVEL_INTERRUPT_BITS") 98 99 if sum(self.int_bits) > 32: 100 raise ValueError("Too many interrupt bits") 101 102 self.__int_lvl_masks[0] = self.__bm(self.int_bits[0]) 103 self.__int_lvl_masks[1] = self.__bm(self.int_bits[1]) << self.int_bits[0] 104 self.__int_lvl_masks[2] = self.__bm(self.int_bits[2]) << (self.int_bits[0] + self.int_bits[1]) 105 106 self.__log.debug("Level Bits Bitmask") 107 self.__log.debug("----------------------------") 108 for i in range(3): 109 bitmask_str = "0x" + format(self.__int_lvl_masks[i], '08X') 110 self.__log.debug(f"{i + 1:>5} {self.__int_bits[i]:>7} {bitmask_str:>14}") 111 112 if self.check_sym("CONFIG_2ND_LEVEL_INTERRUPTS"): 113 num_aggregators = self.get_sym("CONFIG_NUM_2ND_LEVEL_AGGREGATORS") 114 self.__irq2_baseoffset = self.get_sym("CONFIG_2ND_LVL_ISR_TBL_OFFSET") 115 self.__irq2_offsets = [self.get_sym('CONFIG_2ND_LVL_INTR_{}_OFFSET'. 116 format(str(i).zfill(2))) for i in 117 range(num_aggregators)] 118 119 self.__log.debug('2nd level offsets: {}'.format(self.__irq2_offsets)) 120 121 if self.check_sym("CONFIG_3RD_LEVEL_INTERRUPTS"): 122 num_aggregators = self.get_sym("CONFIG_NUM_3RD_LEVEL_AGGREGATORS") 123 self.__irq3_baseoffset = self.get_sym("CONFIG_3RD_LVL_ISR_TBL_OFFSET") 124 self.__irq3_offsets = [self.get_sym('CONFIG_3RD_LVL_INTR_{}_OFFSET'. 125 format(str(i).zfill(2))) for i in 126 range(num_aggregators)] 127 128 self.__log.debug('3rd level offsets: {}'.format(self.__irq3_offsets)) 129 130 @property 131 def args(self): 132 return self.__args 133 134 @property 135 def swt_spurious_handler(self): 136 return self.__swt_spurious_handler 137 138 @property 139 def swt_shared_handler(self): 140 return self.__swt_shared_handler 141 142 @property 143 def vt_default_handler(self): 144 return self.__vt_default_handler 145 146 @property 147 def shared_array_name(self): 148 return self.__shared_array_name 149 150 @property 151 def sw_isr_array_name(self): 152 return self.__sw_isr_array_name 153 154 @property 155 def irq_vector_array_name(self): 156 return self.__irq_vector_array_name 157 158 @property 159 def int_bits(self): 160 return self.__int_bits 161 162 @property 163 def int_lvl_masks(self): 164 return self.__int_lvl_masks 165 166 def endian_prefix(self): 167 if self.args.big_endian: 168 return ">" 169 else: 170 return "<" 171 172 def get_irq_baseoffset(self, lvl): 173 if lvl == 2: 174 return self.__irq2_baseoffset 175 if lvl == 3: 176 return self.__irq3_baseoffset 177 self.__log.error("Unsupported irq level: {}".format(lvl)) 178 179 def get_irq_index(self, irq, lvl): 180 if lvl == 2: 181 offsets = self.__irq2_offsets 182 elif lvl == 3: 183 offsets = self.__irq3_offsets 184 else: 185 self.__log.error("Unsupported irq level: {}".format(lvl)) 186 try: 187 return offsets.index(irq) 188 except ValueError: 189 self.__log.error("IRQ {} not present in parent offsets ({}). ". 190 format(irq, offsets) + 191 " Recheck interrupt configuration.") 192 193 def get_swt_table_index(self, offset, irq): 194 if not self.check_multi_level_interrupts(): 195 return irq - offset 196 # Calculate index for multi level interrupts 197 self.__log.debug('IRQ = ' + hex(irq)) 198 irq3 = (irq & self.int_lvl_masks[2]) >> (self.int_bits[0] + self.int_bits[1]) 199 irq2 = (irq & self.int_lvl_masks[1]) >> (self.int_bits[0]) 200 irq1 = irq & self.int_lvl_masks[0] 201 # Figure out third level interrupt position 202 if irq3: 203 list_index = self.get_irq_index(irq2 - 1, 3) 204 irq3_pos = self.get_irq_baseoffset(3) + self.__max_irq_per * list_index + irq3 - 1 205 self.__log.debug('IRQ_level = 3') 206 self.__log.debug('IRQ_Indx = ' + str(irq3)) 207 self.__log.debug('IRQ_Pos = ' + str(irq3_pos)) 208 return irq3_pos - offset 209 # Figure out second level interrupt position 210 if irq2: 211 list_index = self.get_irq_index(irq1, 2) 212 irq2_pos = self.get_irq_baseoffset(2) + self.__max_irq_per * list_index + irq2 - 1 213 self.__log.debug('IRQ_level = 2') 214 self.__log.debug('IRQ_Indx = ' + str(irq2)) 215 self.__log.debug('IRQ_Pos = ' + str(irq2_pos)) 216 return irq2_pos - offset 217 # Figure out first level interrupt position 218 self.__log.debug('IRQ_level = 1') 219 self.__log.debug('IRQ_Indx = ' + str(irq1)) 220 self.__log.debug('IRQ_Pos = ' + str(irq1)) 221 return irq1 - offset 222 223 def get_intlist_snames(self): 224 return self.args.intlist_section 225 226 def test_isr_direct(self, flags): 227 return flags & self.__ISR_FLAG_DIRECT 228 229 def get_sym_from_addr(self, addr): 230 for key, value in self.__syms.items(): 231 if addr == value: 232 return key 233 return None 234 235 def get_sym(self, name): 236 return self.__syms.get(name) 237 238 def check_sym(self, name): 239 return name in self.__syms 240 241 def check_multi_level_interrupts(self): 242 return self.check_sym("CONFIG_MULTI_LEVEL_INTERRUPTS") 243 244 def check_shared_interrupts(self): 245 return self.check_sym("CONFIG_SHARED_INTERRUPTS") 246 247 def check_64b(self): 248 return self.check_sym("CONFIG_64BIT") 249 250 251def get_symbols(obj): 252 for section in obj.iter_sections(): 253 if isinstance(section, SymbolTableSection): 254 return {sym.name: sym.entry.st_value 255 for sym in section.iter_symbols()} 256 257 log.error("Could not find symbol table") 258 259def read_intList_sect(elfobj, snames): 260 """ 261 Load the raw intList section data in a form of byte array. 262 """ 263 intList_sect = None 264 265 for sname in snames: 266 intList_sect = elfobj.get_section_by_name(sname) 267 if intList_sect is not None: 268 log.debug("Found intlist section: \"{}\"".format(sname)) 269 break 270 271 if intList_sect is None: 272 log.error("Cannot find the intlist section!") 273 274 intdata = intList_sect.data() 275 276 return intdata 277 278def parse_args(): 279 parser = argparse.ArgumentParser(description=__doc__, 280 formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) 281 282 parser.add_argument("-e", "--big-endian", action="store_true", 283 help="Target encodes data in big-endian format (little endian is " 284 "the default)") 285 parser.add_argument("-d", "--debug", action="store_true", 286 help="Print additional debugging information") 287 parser.add_argument("-o", "--output-source", required=True, 288 help="Output source file") 289 parser.add_argument("-l", "--linker-output-files", 290 nargs=2, 291 metavar=("vector_table_link", "software_interrupt_link"), 292 help="Output linker files. " 293 "Used only if CONFIG_ISR_TABLES_LOCAL_DECLARATION is enabled. " 294 "In other case empty file would be generated.") 295 parser.add_argument("-k", "--kernel", required=True, 296 help="Zephyr kernel image") 297 parser.add_argument("-s", "--sw-isr-table", action="store_true", 298 help="Generate SW ISR table") 299 parser.add_argument("-V", "--vector-table", action="store_true", 300 help="Generate vector table") 301 parser.add_argument("-i", "--intlist-section", action="append", required=True, 302 help="The name of the section to search for the interrupt data. " 303 "This is accumulative argument. The first section found would be used.") 304 305 return parser.parse_args() 306 307def main(): 308 args = parse_args() 309 # Configure logging as soon as possible 310 log.set_debug(args.debug) 311 312 with open(args.kernel, "rb") as fp: 313 kernel = ELFFile(fp) 314 config = gen_isr_config(args, get_symbols(kernel), log) 315 intlist_data = read_intList_sect(kernel, config.get_intlist_snames()) 316 317 if config.check_sym("CONFIG_ISR_TABLES_LOCAL_DECLARATION"): 318 sys.stdout.write( 319 "Warning: The EXPERIMENTAL ISR_TABLES_LOCAL_DECLARATION feature selected\n") 320 parser_module = importlib.import_module('gen_isr_tables_parser_local') 321 parser = parser_module.gen_isr_parser(intlist_data, config, log) 322 else: 323 parser_module = importlib.import_module('gen_isr_tables_parser_carrays') 324 parser = parser_module.gen_isr_parser(intlist_data, config, log) 325 326 with open(args.output_source, "w") as fp: 327 parser.write_source(fp) 328 329 if args.linker_output_files is not None: 330 with open(args.linker_output_files[0], "w") as fp_vt, \ 331 open(args.linker_output_files[1], "w") as fp_swi: 332 if hasattr(parser, 'write_linker_vt'): 333 parser.write_linker_vt(fp_vt) 334 else: 335 log.debug("Chosen parser does not support vector table linker file") 336 fp_vt.write('/* Empty */\n') 337 if hasattr(parser, 'write_linker_swi'): 338 parser.write_linker_swi(fp_swi) 339 else: 340 log.debug("Chosen parser does not support software interrupt linker file") 341 fp_swi.write('/* Empty */\n') 342 343if __name__ == "__main__": 344 main() 345