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/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) 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_header = """ 129/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */ 130 131#include <toolchain.h> 132#include <linker/sections.h> 133#include <sw_isr_table.h> 134#include <arch/cpu.h> 135 136#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_GEN_IRQ_VECTOR_TABLE) 137#define ISR_WRAPPER ((uintptr_t)&_isr_wrapper) 138#else 139#define ISR_WRAPPER NULL 140#endif 141 142typedef void (* ISR)(const void *); 143""" 144 145def write_source_file(fp, vt, swt, intlist, syms): 146 fp.write(source_header) 147 148 nv = intlist["num_vectors"] 149 150 if vt: 151 fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv) 152 for i in range(nv): 153 fp.write("\t{},\n".format(vt[i])) 154 fp.write("};\n") 155 156 if not swt: 157 return 158 159 fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n" 160 % nv) 161 162 level2_offset = syms.get("CONFIG_2ND_LVL_ISR_TBL_OFFSET") 163 level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET") 164 165 for i in range(nv): 166 param, func = swt[i] 167 if isinstance(func, int): 168 func_as_string = "{0:#x}".format(func) 169 else: 170 func_as_string = func 171 172 if level2_offset is not None and i == level2_offset: 173 fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n". 174 format(level2_offset)) 175 if level3_offset is not None and i == level3_offset: 176 fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n". 177 format(level3_offset)) 178 179 fp.write("\t{{(const void *){0:#x}, (ISR){1}}},\n".format(param, func_as_string)) 180 fp.write("};\n") 181 182def get_symbols(obj): 183 for section in obj.iter_sections(): 184 if isinstance(section, SymbolTableSection): 185 return {sym.name: sym.entry.st_value 186 for sym in section.iter_symbols()} 187 188 error("Could not find symbol table") 189 190def getindex(irq, irq_aggregator_pos): 191 try: 192 return irq_aggregator_pos.index(irq) 193 except ValueError: 194 error("IRQ {} not present in parent offsets ({}). ". 195 format(irq, irq_aggregator_pos) + 196 " Recheck interrupt configuration.") 197 198def main(): 199 parse_args() 200 201 with open(args.kernel, "rb") as fp: 202 kernel = ELFFile(fp) 203 syms = get_symbols(kernel) 204 205 if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: 206 max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"] 207 208 if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms: 209 num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"] 210 irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"] 211 list_2nd_lvl_offsets = [syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'. 212 format(str(i).zfill(2))] for i in 213 range(num_aggregators)] 214 215 debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets)) 216 217 if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms: 218 num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"] 219 irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"] 220 list_3rd_lvl_offsets = [syms['CONFIG_3RD_LVL_INTR_{}_OFFSET'. 221 format(str(i).zfill(2))] for i in 222 range(num_aggregators)] 223 224 debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets)) 225 226 intlist = read_intlist(args.intlist, syms) 227 nvec = intlist["num_vectors"] 228 offset = intlist["offset"] 229 230 if nvec > pow(2, 15): 231 raise ValueError('nvec is too large, check endianness.') 232 233 spurious_handler = "&z_irq_spurious" 234 sw_irq_handler = "ISR_WRAPPER" 235 236 debug('offset is ' + str(offset)) 237 debug('num_vectors is ' + str(nvec)) 238 239 # Set default entries in both tables 240 if args.sw_isr_table: 241 # All vectors just jump to the common sw_irq_handler. If some entries 242 # are used for direct interrupts, they will be replaced later. 243 if args.vector_table: 244 vt = [sw_irq_handler for i in range(nvec)] 245 else: 246 vt = None 247 # Default to spurious interrupt handler. Configured interrupts 248 # will replace these entries. 249 swt = [(0, spurious_handler) for i in range(nvec)] 250 else: 251 if args.vector_table: 252 vt = [spurious_handler for i in range(nvec)] 253 else: 254 error("one or both of -s or -V needs to be specified on command line") 255 swt = None 256 257 for irq, flags, func, param in intlist["interrupts"]: 258 if flags & ISR_FLAG_DIRECT: 259 if param != 0: 260 error("Direct irq %d declared, but has non-NULL parameter" 261 % irq) 262 if not 0 <= irq - offset < len(vt): 263 error("IRQ %d (offset=%d) exceeds the maximum of %d" % 264 (irq - offset, offset, len(vt) - 1)) 265 vt[irq - offset] = func 266 else: 267 # Regular interrupt 268 if not swt: 269 error("Regular Interrupt %d declared with parameter 0x%x " 270 "but no SW ISR_TABLE in use" 271 % (irq, param)) 272 273 if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: 274 table_index = irq - offset 275 else: 276 # Figure out third level interrupt position 277 debug('IRQ = ' + hex(irq)) 278 irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16 279 irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8 280 irq1 = (irq & FIRST_LVL_INTERRUPTS) 281 282 if irq3: 283 irq_parent = irq2 284 list_index = getindex(irq_parent, list_3rd_lvl_offsets) 285 irq3_pos = irq3_baseoffset + max_irq_per*list_index + irq3 - 1 286 debug('IRQ_level = 3') 287 debug('IRQ_Indx = ' + str(irq3)) 288 debug('IRQ_Pos = ' + str(irq3_pos)) 289 table_index = irq3_pos - offset 290 291 # Figure out second level interrupt position 292 elif irq2: 293 irq_parent = irq1 294 list_index = getindex(irq_parent, list_2nd_lvl_offsets) 295 irq2_pos = irq2_baseoffset + max_irq_per*list_index + irq2 - 1 296 debug('IRQ_level = 2') 297 debug('IRQ_Indx = ' + str(irq2)) 298 debug('IRQ_Pos = ' + str(irq2_pos)) 299 table_index = irq2_pos - offset 300 301 # Figure out first level interrupt position 302 else: 303 debug('IRQ_level = 1') 304 debug('IRQ_Indx = ' + str(irq1)) 305 debug('IRQ_Pos = ' + str(irq1)) 306 table_index = irq1 - offset 307 308 if not 0 <= table_index < len(swt): 309 error("IRQ %d (offset=%d) exceeds the maximum of %d" % 310 (table_index, offset, len(swt) - 1)) 311 if swt[table_index] != (0, spurious_handler): 312 error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" 313 + f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}" 314 + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" 315 ) 316 317 swt[table_index] = (param, func) 318 319 with open(args.output_source, "w") as fp: 320 write_source_file(fp, vt, swt, intlist, syms) 321 322if __name__ == "__main__": 323 main() 324