1#!/usr/bin/env python3 2# 3# Copyright (c) 2017 Intel Corporation 4# Copyright (c) 2018 Foundries.io 5# 6# SPDX-License-Identifier: Apache-2.0 7# 8 9import argparse 10import struct 11import sys 12import os 13from elftools.elf.elffile import ELFFile 14from elftools.elf.sections import SymbolTableSection 15 16ISR_FLAG_DIRECT = 1 << 0 17 18# The below few hardware independent magic numbers represent various 19# levels of interrupts in a multi-level interrupt system. 20# 0x000000FF - represents the 1st level (i.e. the interrupts 21# that directly go to the processor). 22# 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel 23# into 1 line which then goes into the 1st level) 24# 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel 25# into 1 line which then goes into the 2nd level) 26FIRST_LVL_INTERRUPTS = 0x000000FF 27SECND_LVL_INTERRUPTS = 0x0000FF00 28THIRD_LVL_INTERRUPTS = 0x00FF0000 29 30INTERRUPT_BITS = [8, 8, 8] 31 32def debug(text): 33 if args.debug: 34 sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") 35 36def error(text): 37 sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n") 38 39def endian_prefix(): 40 if args.big_endian: 41 return ">" 42 else: 43 return "<" 44 45def read_intlist(intlist_path, syms): 46 """read a binary file containing the contents of the kernel's .intList 47 section. This is an instance of a header created by 48 include/zephyr/linker/intlist.ld: 49 50 struct { 51 uint32_t num_vectors; <- typically CONFIG_NUM_IRQS 52 struct _isr_list isrs[]; <- Usually of smaller size than num_vectors 53 } 54 55 Followed by instances of struct _isr_list created by IRQ_CONNECT() 56 calls: 57 58 struct _isr_list { 59 /** IRQ line number */ 60 int32_t irq; 61 /** Flags for this IRQ, see ISR_FLAG_* definitions */ 62 int32_t flags; 63 /** ISR to call */ 64 void *func; 65 /** Parameter for non-direct IRQs */ 66 const void *param; 67 }; 68 """ 69 70 intlist = {} 71 72 prefix = endian_prefix() 73 74 intlist_header_fmt = prefix + "II" 75 if "CONFIG_64BIT" in syms: 76 intlist_entry_fmt = prefix + "iiQQ" 77 else: 78 intlist_entry_fmt = prefix + "iiII" 79 80 with open(intlist_path, "rb") as fp: 81 intdata = fp.read() 82 83 header_sz = struct.calcsize(intlist_header_fmt) 84 header = struct.unpack_from(intlist_header_fmt, intdata, 0) 85 intdata = intdata[header_sz:] 86 87 debug(str(header)) 88 89 intlist["num_vectors"] = header[0] 90 intlist["offset"] = header[1] 91 92 intlist["interrupts"] = [i for i in 93 struct.iter_unpack(intlist_entry_fmt, intdata)] 94 95 debug("Configured interrupt routing") 96 debug("handler irq flags param") 97 debug("--------------------------") 98 99 for irq in intlist["interrupts"]: 100 debug("{0:<10} {1:<3} {2:<3} {3}".format( 101 hex(irq[2]), irq[0], irq[1], hex(irq[3]))) 102 103 return intlist 104 105 106def parse_args(): 107 global args 108 109 parser = argparse.ArgumentParser(description=__doc__, 110 formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) 111 112 parser.add_argument("-e", "--big-endian", action="store_true", 113 help="Target encodes data in big-endian format (little endian is " 114 "the default)") 115 parser.add_argument("-d", "--debug", action="store_true", 116 help="Print additional debugging information") 117 parser.add_argument("-o", "--output-source", required=True, 118 help="Output source file") 119 parser.add_argument("-k", "--kernel", required=True, 120 help="Zephyr kernel image") 121 parser.add_argument("-s", "--sw-isr-table", action="store_true", 122 help="Generate SW ISR table") 123 parser.add_argument("-V", "--vector-table", action="store_true", 124 help="Generate vector table") 125 parser.add_argument("-i", "--intlist", required=True, 126 help="Zephyr intlist binary for intList extraction") 127 128 args = parser.parse_args() 129 130source_assembly_header = """ 131#ifndef ARCH_IRQ_VECTOR_JUMP_CODE 132#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined" 133#endif 134""" 135 136def get_symbol_from_addr(syms, addr): 137 for key, value in syms.items(): 138 if addr == value: 139 return key 140 return None 141 142def write_code_irq_vector_table(fp, vt, nv, syms): 143 fp.write(source_assembly_header) 144 145 fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n") 146 for i in range(nv): 147 func = vt[i] 148 149 if isinstance(func, int): 150 func_as_string = get_symbol_from_addr(syms, func) 151 else: 152 func_as_string = func 153 154 fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string)) 155 fp.write("}\n") 156 157def write_address_irq_vector_table(fp, vt, nv): 158 fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv) 159 for i in range(nv): 160 func = vt[i] 161 162 if isinstance(func, int): 163 fp.write("\t{},\n".format(vt[i])) 164 else: 165 fp.write("\t((uintptr_t)&{}),\n".format(vt[i])) 166 167 fp.write("};\n") 168 169source_header = """ 170/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */ 171 172#include <zephyr/toolchain.h> 173#include <zephyr/linker/sections.h> 174#include <zephyr/sw_isr_table.h> 175#include <zephyr/arch/cpu.h> 176 177typedef void (* ISR)(const void *); 178""" 179 180def write_shared_table(fp, shared, nv): 181 fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table" 182 " z_shared_sw_isr_table[%d] = {\n" % nv) 183 184 for i in range(nv): 185 client_num = shared[i][1] 186 client_list = shared[i][0] 187 188 if not client_num: 189 fp.write("\t{ },\n") 190 else: 191 fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ") 192 for j in range(0, client_num): 193 routine = client_list[j][1] 194 arg = client_list[j][0] 195 196 fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, " 197 f".arg = (const void *){hex(arg)} }},") 198 199 fp.write(" },\n},\n") 200 201 fp.write("};\n") 202 203def write_source_file(fp, vt, swt, intlist, syms, shared): 204 fp.write(source_header) 205 206 nv = intlist["num_vectors"] 207 208 if "CONFIG_SHARED_INTERRUPTS" in syms: 209 write_shared_table(fp, shared, nv) 210 211 if vt: 212 if "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS" in syms: 213 write_address_irq_vector_table(fp, vt, nv) 214 elif "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE" in syms: 215 write_code_irq_vector_table(fp, vt, nv, syms) 216 else: 217 error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set") 218 219 if not swt: 220 return 221 222 fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n" 223 % nv) 224 225 level2_offset = syms.get("CONFIG_2ND_LVL_ISR_TBL_OFFSET") 226 level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET") 227 228 for i in range(nv): 229 param = "{0:#x}".format(swt[i][0]) 230 func = swt[i][1] 231 232 if isinstance (func, str) and "z_shared_isr" in func: 233 param = "&z_shared_sw_isr_table[{0}]".format(i) 234 if isinstance(func, int): 235 func_as_string = "{0:#x}".format(func) 236 else: 237 func_as_string = func 238 239 if level2_offset is not None and i == level2_offset: 240 fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n". 241 format(level2_offset)) 242 if level3_offset is not None and i == level3_offset: 243 fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n". 244 format(level3_offset)) 245 246 fp.write("\t{{(const void *){0}, (ISR){1}}},\n".format(param, func_as_string)) 247 fp.write("};\n") 248 249def get_symbols(obj): 250 for section in obj.iter_sections(): 251 if isinstance(section, SymbolTableSection): 252 return {sym.name: sym.entry.st_value 253 for sym in section.iter_symbols()} 254 255 error("Could not find symbol table") 256 257def getindex(irq, irq_aggregator_pos): 258 try: 259 return irq_aggregator_pos.index(irq) 260 except ValueError: 261 error("IRQ {} not present in parent offsets ({}). ". 262 format(irq, irq_aggregator_pos) + 263 " Recheck interrupt configuration.") 264 265def bit_mask(bits): 266 mask = 0 267 for _ in range(0, bits): 268 mask = (mask << 1) | 1 269 return mask 270 271def update_masks(): 272 global FIRST_LVL_INTERRUPTS 273 global SECND_LVL_INTERRUPTS 274 global THIRD_LVL_INTERRUPTS 275 276 if sum(INTERRUPT_BITS) > 32: 277 raise ValueError("Too many interrupt bits") 278 279 FIRST_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[0]) 280 SECND_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[1]) << INTERRUPT_BITS[0] 281 THIRD_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[2]) << INTERRUPT_BITS[0] + INTERRUPT_BITS[2] 282 283def main(): 284 parse_args() 285 286 with open(args.kernel, "rb") as fp: 287 kernel = ELFFile(fp) 288 syms = get_symbols(kernel) 289 290 if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: 291 max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"] 292 293 INTERRUPT_BITS[0] = syms["CONFIG_1ST_LEVEL_INTERRUPT_BITS"] 294 INTERRUPT_BITS[1] = syms["CONFIG_2ND_LEVEL_INTERRUPT_BITS"] 295 INTERRUPT_BITS[2] = syms["CONFIG_3RD_LEVEL_INTERRUPT_BITS"] 296 update_masks() 297 298 if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms: 299 num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"] 300 irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"] 301 list_2nd_lvl_offsets = [syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'. 302 format(str(i).zfill(2))] for i in 303 range(num_aggregators)] 304 305 debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets)) 306 307 if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms: 308 num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"] 309 irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"] 310 list_3rd_lvl_offsets = [syms['CONFIG_3RD_LVL_INTR_{}_OFFSET'. 311 format(str(i).zfill(2))] for i in 312 range(num_aggregators)] 313 314 debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets)) 315 316 intlist = read_intlist(args.intlist, syms) 317 nvec = intlist["num_vectors"] 318 offset = intlist["offset"] 319 320 if nvec > pow(2, 15): 321 raise ValueError('nvec is too large, check endianness.') 322 323 swt_spurious_handler = "((uintptr_t)&z_irq_spurious)" 324 swt_shared_handler = "((uintptr_t)&z_shared_isr)" 325 vt_spurious_handler = "z_irq_spurious" 326 vt_irq_handler = "_isr_wrapper" 327 328 debug('offset is ' + str(offset)) 329 debug('num_vectors is ' + str(nvec)) 330 331 # Set default entries in both tables 332 if args.sw_isr_table: 333 # All vectors just jump to the common vt_irq_handler. If some entries 334 # are used for direct interrupts, they will be replaced later. 335 if args.vector_table: 336 vt = [vt_irq_handler for i in range(nvec)] 337 else: 338 vt = None 339 # Default to spurious interrupt handler. Configured interrupts 340 # will replace these entries. 341 swt = [(0, swt_spurious_handler) for i in range(nvec)] 342 shared = [([], 0) for i in range(nvec)] 343 else: 344 if args.vector_table: 345 vt = [vt_spurious_handler for i in range(nvec)] 346 else: 347 error("one or both of -s or -V needs to be specified on command line") 348 swt = None 349 350 for irq, flags, func, param in intlist["interrupts"]: 351 if flags & ISR_FLAG_DIRECT: 352 if param != 0: 353 error("Direct irq %d declared, but has non-NULL parameter" 354 % irq) 355 if not 0 <= irq - offset < len(vt): 356 error("IRQ %d (offset=%d) exceeds the maximum of %d" % 357 (irq - offset, offset, len(vt) - 1)) 358 vt[irq - offset] = func 359 else: 360 # Regular interrupt 361 if not swt: 362 error("Regular Interrupt %d declared with parameter 0x%x " 363 "but no SW ISR_TABLE in use" 364 % (irq, param)) 365 366 if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: 367 table_index = irq - offset 368 else: 369 # Figure out third level interrupt position 370 debug('IRQ = ' + hex(irq)) 371 irq3 = (irq & THIRD_LVL_INTERRUPTS) >> INTERRUPT_BITS[0] + INTERRUPT_BITS[1] 372 irq2 = (irq & SECND_LVL_INTERRUPTS) >> INTERRUPT_BITS[0] 373 irq1 = irq & FIRST_LVL_INTERRUPTS 374 375 if irq3: 376 irq_parent = irq2 377 list_index = getindex(irq_parent, list_3rd_lvl_offsets) 378 irq3_pos = irq3_baseoffset + max_irq_per*list_index + irq3 - 1 379 debug('IRQ_level = 3') 380 debug('IRQ_Indx = ' + str(irq3)) 381 debug('IRQ_Pos = ' + str(irq3_pos)) 382 table_index = irq3_pos - offset 383 384 # Figure out second level interrupt position 385 elif irq2: 386 irq_parent = irq1 387 list_index = getindex(irq_parent, list_2nd_lvl_offsets) 388 irq2_pos = irq2_baseoffset + max_irq_per*list_index + irq2 - 1 389 debug('IRQ_level = 2') 390 debug('IRQ_Indx = ' + str(irq2)) 391 debug('IRQ_Pos = ' + str(irq2_pos)) 392 table_index = irq2_pos - offset 393 394 # Figure out first level interrupt position 395 else: 396 debug('IRQ_level = 1') 397 debug('IRQ_Indx = ' + str(irq1)) 398 debug('IRQ_Pos = ' + str(irq1)) 399 table_index = irq1 - offset 400 401 if not 0 <= table_index < len(swt): 402 error("IRQ %d (offset=%d) exceeds the maximum of %d" % 403 (table_index, offset, len(swt) - 1)) 404 if "CONFIG_SHARED_INTERRUPTS" in syms: 405 if swt[table_index] != (0, swt_spurious_handler): 406 # check client limit 407 if syms["CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"] == shared[table_index][1]: 408 error(f"Reached shared interrupt client limit. Maybe increase" 409 + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?") 410 lst = shared[table_index][0] 411 delta_size = 1 412 if not shared[table_index][1]: 413 lst.append(swt[table_index]) 414 # note: the argument will be fixed when writing the ISR table 415 # to isr_table.c 416 swt[table_index] = (0, swt_shared_handler) 417 delta_size += 1 418 if (param, func) in lst: 419 error("Attempting to register the same ISR/arg pair twice.") 420 lst.append((param, func)) 421 shared[table_index] = (lst, shared[table_index][1] + delta_size) 422 else: 423 swt[table_index] = (param, func) 424 else: 425 if swt[table_index] != (0, swt_spurious_handler): 426 error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" 427 + f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}" 428 + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" 429 ) 430 else: 431 swt[table_index] = (param, func) 432 433 with open(args.output_source, "w") as fp: 434 write_source_file(fp, vt, swt, intlist, syms, shared) 435 436if __name__ == "__main__": 437 main() 438