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 30def debug(text): 31 if args.debug: 32 sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") 33 34def error(text): 35 sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n") 36 37def endian_prefix(): 38 if args.big_endian: 39 return ">" 40 else: 41 return "<" 42 43def read_intlist(intlist_path, syms): 44 """read a binary file containing the contents of the kernel's .intList 45 section. This is an instance of a header created by 46 include/zephyr/linker/intlist.ld: 47 48 struct { 49 uint32_t num_vectors; <- typically CONFIG_NUM_IRQS 50 struct _isr_list isrs[]; <- Usually of smaller size than num_vectors 51 } 52 53 Followed by instances of struct _isr_list created by IRQ_CONNECT() 54 calls: 55 56 struct _isr_list { 57 /** IRQ line number */ 58 int32_t irq; 59 /** Flags for this IRQ, see ISR_FLAG_* definitions */ 60 int32_t flags; 61 /** ISR to call */ 62 void *func; 63 /** Parameter for non-direct IRQs */ 64 const void *param; 65 }; 66 """ 67 68 intlist = {} 69 70 prefix = endian_prefix() 71 72 intlist_header_fmt = prefix + "II" 73 if "CONFIG_64BIT" in syms: 74 intlist_entry_fmt = prefix + "iiQQ" 75 else: 76 intlist_entry_fmt = prefix + "iiII" 77 78 with open(intlist_path, "rb") as fp: 79 intdata = fp.read() 80 81 header_sz = struct.calcsize(intlist_header_fmt) 82 header = struct.unpack_from(intlist_header_fmt, intdata, 0) 83 intdata = intdata[header_sz:] 84 85 debug(str(header)) 86 87 intlist["num_vectors"] = header[0] 88 intlist["offset"] = header[1] 89 90 intlist["interrupts"] = [i for i in 91 struct.iter_unpack(intlist_entry_fmt, intdata)] 92 93 debug("Configured interrupt routing") 94 debug("handler irq flags param") 95 debug("--------------------------") 96 97 for irq in intlist["interrupts"]: 98 debug("{0:<10} {1:<3} {2:<3} {3}".format( 99 hex(irq[2]), irq[0], irq[1], hex(irq[3]))) 100 101 return intlist 102 103 104def parse_args(): 105 global args 106 107 parser = argparse.ArgumentParser(description=__doc__, 108 formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) 109 110 parser.add_argument("-e", "--big-endian", action="store_true", 111 help="Target encodes data in big-endian format (little endian is " 112 "the default)") 113 parser.add_argument("-d", "--debug", action="store_true", 114 help="Print additional debugging information") 115 parser.add_argument("-o", "--output-source", required=True, 116 help="Output source file") 117 parser.add_argument("-k", "--kernel", required=True, 118 help="Zephyr kernel image") 119 parser.add_argument("-s", "--sw-isr-table", action="store_true", 120 help="Generate SW ISR table") 121 parser.add_argument("-V", "--vector-table", action="store_true", 122 help="Generate vector table") 123 parser.add_argument("-i", "--intlist", required=True, 124 help="Zephyr intlist binary for intList extraction") 125 126 args = parser.parse_args() 127 128source_assembly_header = """ 129#ifndef ARCH_IRQ_VECTOR_JUMP_CODE 130#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined" 131#endif 132""" 133 134def get_symbol_from_addr(syms, addr): 135 for key, value in syms.items(): 136 if addr == value: 137 return key 138 return None 139 140def write_code_irq_vector_table(fp, vt, nv, syms): 141 fp.write(source_assembly_header) 142 143 fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n") 144 for i in range(nv): 145 func = vt[i] 146 147 if isinstance(func, int): 148 func_as_string = get_symbol_from_addr(syms, func) 149 else: 150 func_as_string = func 151 152 fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string)) 153 fp.write("}\n") 154 155def write_address_irq_vector_table(fp, vt, nv): 156 fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv) 157 for i in range(nv): 158 func = vt[i] 159 160 if isinstance(func, int): 161 fp.write("\t{},\n".format(vt[i])) 162 else: 163 fp.write("\t((uintptr_t)&{}),\n".format(vt[i])) 164 165 fp.write("};\n") 166 167source_header = """ 168/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */ 169 170#include <zephyr/toolchain.h> 171#include <zephyr/linker/sections.h> 172#include <zephyr/sw_isr_table.h> 173#include <zephyr/arch/cpu.h> 174 175typedef void (* ISR)(const void *); 176""" 177 178def write_source_file(fp, vt, swt, intlist, syms): 179 fp.write(source_header) 180 181 nv = intlist["num_vectors"] 182 183 if vt: 184 if "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS" in syms: 185 write_address_irq_vector_table(fp, vt, nv) 186 elif "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE" in syms: 187 write_code_irq_vector_table(fp, vt, nv, syms) 188 else: 189 error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set") 190 191 if not swt: 192 return 193 194 fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n" 195 % nv) 196 197 level2_offset = syms.get("CONFIG_2ND_LVL_ISR_TBL_OFFSET") 198 level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET") 199 200 for i in range(nv): 201 param, func = swt[i] 202 if isinstance(func, int): 203 func_as_string = "{0:#x}".format(func) 204 else: 205 func_as_string = func 206 207 if level2_offset is not None and i == level2_offset: 208 fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n". 209 format(level2_offset)) 210 if level3_offset is not None and i == level3_offset: 211 fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n". 212 format(level3_offset)) 213 214 fp.write("\t{{(const void *){0:#x}, (ISR){1}}},\n".format(param, func_as_string)) 215 fp.write("};\n") 216 217def get_symbols(obj): 218 for section in obj.iter_sections(): 219 if isinstance(section, SymbolTableSection): 220 return {sym.name: sym.entry.st_value 221 for sym in section.iter_symbols()} 222 223 error("Could not find symbol table") 224 225def getindex(irq, irq_aggregator_pos): 226 try: 227 return irq_aggregator_pos.index(irq) 228 except ValueError: 229 error("IRQ {} not present in parent offsets ({}). ". 230 format(irq, irq_aggregator_pos) + 231 " Recheck interrupt configuration.") 232 233def main(): 234 parse_args() 235 236 with open(args.kernel, "rb") as fp: 237 kernel = ELFFile(fp) 238 syms = get_symbols(kernel) 239 240 if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: 241 max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"] 242 243 if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms: 244 num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"] 245 irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"] 246 list_2nd_lvl_offsets = [syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'. 247 format(str(i).zfill(2))] for i in 248 range(num_aggregators)] 249 250 debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets)) 251 252 if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms: 253 num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"] 254 irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"] 255 list_3rd_lvl_offsets = [syms['CONFIG_3RD_LVL_INTR_{}_OFFSET'. 256 format(str(i).zfill(2))] for i in 257 range(num_aggregators)] 258 259 debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets)) 260 261 intlist = read_intlist(args.intlist, syms) 262 nvec = intlist["num_vectors"] 263 offset = intlist["offset"] 264 265 if nvec > pow(2, 15): 266 raise ValueError('nvec is too large, check endianness.') 267 268 swt_spurious_handler = "((uintptr_t)&z_irq_spurious)" 269 vt_spurious_handler = "z_irq_spurious" 270 vt_irq_handler = "_isr_wrapper" 271 272 debug('offset is ' + str(offset)) 273 debug('num_vectors is ' + str(nvec)) 274 275 # Set default entries in both tables 276 if args.sw_isr_table: 277 # All vectors just jump to the common vt_irq_handler. If some entries 278 # are used for direct interrupts, they will be replaced later. 279 if args.vector_table: 280 vt = [vt_irq_handler for i in range(nvec)] 281 else: 282 vt = None 283 # Default to spurious interrupt handler. Configured interrupts 284 # will replace these entries. 285 swt = [(0, swt_spurious_handler) for i in range(nvec)] 286 else: 287 if args.vector_table: 288 vt = [vt_spurious_handler for i in range(nvec)] 289 else: 290 error("one or both of -s or -V needs to be specified on command line") 291 swt = None 292 293 for irq, flags, func, param in intlist["interrupts"]: 294 if flags & ISR_FLAG_DIRECT: 295 if param != 0: 296 error("Direct irq %d declared, but has non-NULL parameter" 297 % irq) 298 if not 0 <= irq - offset < len(vt): 299 error("IRQ %d (offset=%d) exceeds the maximum of %d" % 300 (irq - offset, offset, len(vt) - 1)) 301 vt[irq - offset] = func 302 else: 303 # Regular interrupt 304 if not swt: 305 error("Regular Interrupt %d declared with parameter 0x%x " 306 "but no SW ISR_TABLE in use" 307 % (irq, param)) 308 309 if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: 310 table_index = irq - offset 311 else: 312 # Figure out third level interrupt position 313 debug('IRQ = ' + hex(irq)) 314 irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16 315 irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8 316 irq1 = irq & FIRST_LVL_INTERRUPTS 317 318 if irq3: 319 irq_parent = irq2 320 list_index = getindex(irq_parent, list_3rd_lvl_offsets) 321 irq3_pos = irq3_baseoffset + max_irq_per*list_index + irq3 - 1 322 debug('IRQ_level = 3') 323 debug('IRQ_Indx = ' + str(irq3)) 324 debug('IRQ_Pos = ' + str(irq3_pos)) 325 table_index = irq3_pos - offset 326 327 # Figure out second level interrupt position 328 elif irq2: 329 irq_parent = irq1 330 list_index = getindex(irq_parent, list_2nd_lvl_offsets) 331 irq2_pos = irq2_baseoffset + max_irq_per*list_index + irq2 - 1 332 debug('IRQ_level = 2') 333 debug('IRQ_Indx = ' + str(irq2)) 334 debug('IRQ_Pos = ' + str(irq2_pos)) 335 table_index = irq2_pos - offset 336 337 # Figure out first level interrupt position 338 else: 339 debug('IRQ_level = 1') 340 debug('IRQ_Indx = ' + str(irq1)) 341 debug('IRQ_Pos = ' + str(irq1)) 342 table_index = irq1 - offset 343 344 if not 0 <= table_index < len(swt): 345 error("IRQ %d (offset=%d) exceeds the maximum of %d" % 346 (table_index, offset, len(swt) - 1)) 347 if swt[table_index] != (0, swt_spurious_handler): 348 error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" 349 + f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}" 350 + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" 351 ) 352 353 swt[table_index] = (param, func) 354 355 with open(args.output_source, "w") as fp: 356 write_source_file(fp, vt, swt, intlist, syms) 357 358if __name__ == "__main__": 359 main() 360