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 struct 11 12class gen_isr_parser: 13 source_header = """ 14/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */ 15 16#include <zephyr/toolchain.h> 17#include <zephyr/linker/sections.h> 18#include <zephyr/sw_isr_table.h> 19#include <zephyr/arch/cpu.h> 20 21""" 22 23 shared_isr_table_header = """ 24 25/* For this parser to work, we have to be sure that shared interrupts table entry 26 * and the normal isr table entry have exactly the same layout 27 */ 28BUILD_ASSERT(sizeof(struct _isr_table_entry) 29 == 30 sizeof(struct z_shared_isr_table_entry), 31 "Shared ISR and ISR table entries layout do not match"); 32BUILD_ASSERT(offsetof(struct _isr_table_entry, arg) 33 == 34 offsetof(struct z_shared_isr_table_entry, arg), 35 "Shared ISR and ISR table entries layout do not match"); 36BUILD_ASSERT(offsetof(struct _isr_table_entry, isr) 37 == 38 offsetof(struct z_shared_isr_table_entry, isr), 39 "Shared ISR and ISR table entries layout do not match"); 40 41""" 42 43 def __init__(self, intlist_data, config, log): 44 """Initialize the parser. 45 46 The function prepares parser to work. 47 Parameters: 48 - intlist_data: The binnary data from intlist section 49 - config: The configuration object 50 - log: The logging object, has to have error and debug methods 51 """ 52 self.__config = config 53 self.__log = log 54 intlist = self.__read_intlist(intlist_data) 55 self.__vt, self.__swt, self.__nv, header = self.__parse_intlist(intlist) 56 self.__swi_table_entry_size = header["swi_table_entry_size"] 57 self.__shared_isr_table_entry_size = header["shared_isr_table_entry_size"] 58 self.__shared_isr_client_num_offset = header["shared_isr_client_num_offset"] 59 60 def __read_intlist(self, intlist_data): 61 """read an intList section from the elf file. 62 This is version 2 of a header created by include/zephyr/linker/intlist.ld: 63 64 struct { 65 uint32_t num_vectors; <- typically CONFIG_NUM_IRQS 66 uint8_t stream[]; <- the stream with the interrupt data 67 }; 68 69 The stream is contained from variable length records in a form: 70 71 struct _isr_list_sname { 72 /** IRQ line number */ 73 int32_t irq; 74 /** Flags for this IRQ, see ISR_FLAG_* definitions */ 75 int32_t flags; 76 /** The section name */ 77 const char sname[]; 78 }; 79 80 The flexible array member here (sname) contains the name of the section where the structure 81 with interrupt data is located. 82 It is always Null-terminated string thus we have to search through the input data for the 83 structure end. 84 85 """ 86 intlist = {} 87 prefix = self.__config.endian_prefix() 88 89 # Extract header and the rest of the data 90 intlist_header_fmt = prefix + "IIIII" 91 header_sz = struct.calcsize(intlist_header_fmt) 92 header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0) 93 self.__log.debug(str(header_raw)) 94 95 intlist["num_vectors"] = header_raw[0] 96 intlist["offset"] = header_raw[1] 97 intlist["swi_table_entry_size"] = header_raw[2] 98 intlist["shared_isr_table_entry_size"] = header_raw[3] 99 intlist["shared_isr_client_num_offset"] = header_raw[4] 100 101 intdata = intlist_data[header_sz:] 102 103 # Extract information about interrupts 104 intlist_entry_fmt = prefix + "ii" 105 entry_sz = struct.calcsize(intlist_entry_fmt) 106 intlist["interrupts"] = [] 107 108 while len(intdata) > entry_sz: 109 entry_raw = struct.unpack_from(intlist_entry_fmt, intdata, 0) 110 intdata = intdata[entry_sz:] 111 null_idx = intdata.find(0) 112 if null_idx < 0: 113 self.__log.error("Cannot find sname null termination at IRQ{}".format(entry_raw[0])) 114 bname = intdata[:null_idx] 115 # Next structure starts with 4B alignment 116 next_idx = null_idx + 1 117 next_idx = (next_idx + 3) & ~3 118 intdata = intdata[next_idx:] 119 sname = bname.decode() 120 intlist["interrupts"].append([entry_raw[0], entry_raw[1], sname]) 121 self.__log.debug("Unpacked IRQ{}, flags: {}, sname: \"{}\"\n".format( 122 entry_raw[0], entry_raw[1], sname)) 123 124 # If any data left at the end - it has to be all the way 0 - this is just a check 125 if (len(intdata) and not all([d == 0 for d in intdata])): 126 self.__log.error("Non-zero data found at the end of the intList data.\n") 127 128 self.__log.debug("Configured interrupt routing with linker") 129 self.__log.debug("irq flags sname") 130 self.__log.debug("--------------------------") 131 132 for irq in intlist["interrupts"]: 133 self.__log.debug("{0:<3} {1:<5} {2}".format( 134 hex(irq[0]), irq[1], irq[2])) 135 136 return intlist 137 138 def __parse_intlist(self, intlist): 139 """All the intlist data are parsed into swt and vt arrays. 140 141 The vt array is prepared for hardware interrupt table. 142 Every entry in the selected position would contain None or the name of the function pointer 143 (address or string). 144 145 The swt is a little more complex. At every position it would contain an array of parameter and 146 function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry. 147 If empty array is placed on selected position - it means that the application does not implement 148 this interrupt. 149 150 Parameters: 151 - intlist: The preprocessed list of intlist section content (see read_intlist) 152 153 Return: 154 vt, swt - parsed vt and swt arrays (see function description above) 155 """ 156 nvec = intlist["num_vectors"] 157 offset = intlist["offset"] 158 header = { 159 "swi_table_entry_size": intlist["swi_table_entry_size"], 160 "shared_isr_table_entry_size": intlist["shared_isr_table_entry_size"], 161 "shared_isr_client_num_offset": intlist["shared_isr_client_num_offset"] 162 } 163 164 if nvec > pow(2, 15): 165 raise ValueError('nvec is too large, check endianness.') 166 167 self.__log.debug('offset is ' + str(offset)) 168 self.__log.debug('num_vectors is ' + str(nvec)) 169 170 # Set default entries in both tables 171 if not(self.__config.args.sw_isr_table or self.__config.args.vector_table): 172 self.__log.error("one or both of -s or -V needs to be specified on command line") 173 if self.__config.args.vector_table: 174 vt = [None for i in range(nvec)] 175 else: 176 vt = None 177 if self.__config.args.sw_isr_table: 178 swt = [[] for i in range(nvec)] 179 else: 180 swt = None 181 182 # Process intlist and write to the tables created 183 for irq, flags, sname in intlist["interrupts"]: 184 if self.__config.test_isr_direct(flags): 185 if not 0 <= irq - offset < len(vt): 186 self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % 187 (irq - offset, offset, len(vt) - 1)) 188 vt[irq - offset] = sname 189 else: 190 # Regular interrupt 191 if not swt: 192 self.__log.error("Regular Interrupt %d declared with section name %s " 193 "but no SW ISR_TABLE in use" 194 % (irq, sname)) 195 196 table_index = self.__config.get_swt_table_index(offset, irq) 197 198 if not 0 <= table_index < len(swt): 199 self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" % 200 (table_index, offset, len(swt) - 1)) 201 # Check if the given section name does not repeat outside of current interrupt 202 for i in range(nvec): 203 if i == irq: 204 continue 205 if sname in swt[i]: 206 self.__log.error(("Attempting to register the same section name \"{}\"for" + 207 "different interrupts: {} and {}").format(sname, i, irq)) 208 if self.__config.check_shared_interrupts(): 209 lst = swt[table_index] 210 if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"): 211 self.__log.error(f"Reached shared interrupt client limit. Maybe increase" 212 + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?") 213 else: 214 if len(swt[table_index]) > 0: 215 self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" 216 + f"\nExisting section {swt[table_index]}, new section {sname}" 217 + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" 218 ) 219 swt[table_index].append(sname) 220 221 return vt, swt, nvec, header 222 223 @staticmethod 224 def __irq_spurious_section(irq): 225 return '.irq_spurious.0x{:x}'.format(irq) 226 227 @staticmethod 228 def __isr_generated_section(irq): 229 return '.isr_generated.0x{:x}'.format(irq) 230 231 @staticmethod 232 def __shared_entry_section(irq, ent): 233 return '.isr_shared.0x{:x}_0x{:x}'.format(irq, ent) 234 235 @staticmethod 236 def __shared_client_num_section(irq): 237 return '.isr_shared.0x{:x}_client_num'.format(irq) 238 239 def __isr_spurious_entry(self, irq): 240 return '_Z_ISR_TABLE_ENTRY({irq}, {func}, NULL, "{sect}");'.format( 241 irq = irq, 242 func = self.__config.swt_spurious_handler, 243 sect = self.__isr_generated_section(irq) 244 ) 245 246 def __isr_shared_entry(self, irq): 247 return '_Z_ISR_TABLE_ENTRY({irq}, {func}, {arg}, "{sect}");'.format( 248 irq = irq, 249 arg = '&{}[{}]'.format(self.__config.shared_array_name, irq), 250 func = self.__config.swt_shared_handler, 251 sect = self.__isr_generated_section(irq) 252 ) 253 254 def __irq_spurious_entry(self, irq): 255 return '_Z_ISR_DIRECT_TABLE_ENTRY({irq}, {func}, "{sect}");'.format( 256 irq = irq, 257 func = self.__config.vt_default_handler, 258 sect = self.__irq_spurious_section(irq) 259 ) 260 261 def __write_isr_handlers(self, fp): 262 for i in range(self.__nv): 263 if len(self.__swt[i]) <= 0: 264 fp.write(self.__isr_spurious_entry(i) + '\n') 265 elif len(self.__swt[i]) > 1: 266 # Connect to shared handlers 267 fp.write(self.__isr_shared_entry(i) + '\n') 268 else: 269 fp.write('/* ISR: {} implemented in app in "{}" section. */\n'.format( 270 i, self.__swt[i][0])) 271 272 def __write_irq_handlers(self, fp): 273 for i in range(self.__nv): 274 if self.__vt[i] is None: 275 fp.write(self.__irq_spurious_entry(i) + '\n') 276 else: 277 fp.write('/* ISR: {} implemented in app. */\n'.format(i)) 278 279 def __write_shared_handlers(self, fp): 280 fp.write("extern struct z_shared_isr_table_entry " 281 "{}[{}];\n".format(self.__config.shared_array_name, self.__nv)) 282 283 shared_cnt = self.__config.get_sym('CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS') 284 for i in range(self.__nv): 285 swt_len = len(self.__swt[i]) 286 for j in range(shared_cnt): 287 if (swt_len <= 1) or (swt_len <= j): 288 # Add all unused entry 289 fp.write('static Z_DECL_ALIGN(struct _isr_table_entry)\n' + 290 '\tZ_GENERIC_SECTION({})\n'.format(self.__shared_entry_section(i, j)) + 291 '\t__used isr_shared_empty_entry_0x{:x}_0x{:x} = {{\n'.format(i, j) + 292 '\t\t.arg = (const void *)NULL,\n' + 293 '\t\t.isr = (void (*)(const void *))(void *)0\n' + 294 '};\n' 295 ) 296 else: 297 # Add information about entry implemented by application 298 fp.write('/* Shared isr {} entry {} implemented in "{}" section*/\n'.format( 299 i, j, self.__swt[i][j])) 300 301 # Add information about clients count 302 fp.write(('static size_t Z_GENERIC_SECTION({}) __used\n' + 303 'isr_shared_client_num_0x{:x} = {};\n\n').format( 304 self.__shared_client_num_section(i), 305 i, 306 0 if swt_len < 2 else swt_len) 307 ) 308 309 def write_source(self, fp): 310 fp.write(self.source_header) 311 312 if self.__vt: 313 self.__write_irq_handlers(fp) 314 315 if not self.__swt: 316 return 317 318 if self.__config.check_shared_interrupts(): 319 self.__write_shared_handlers(fp) 320 321 self.__write_isr_handlers(fp) 322 323 def __write_linker_irq(self, fp): 324 fp.write('{} = .;\n'.format(self.__config.irq_vector_array_name)) 325 for i in range(self.__nv): 326 if self.__vt[i] is None: 327 sname = self.__irq_spurious_section(i) 328 else: 329 sname = self.__vt[i] 330 fp.write('KEEP(*("{}"))\n'.format(sname)) 331 332 def __write_linker_shared(self, fp): 333 fp.write(". = ALIGN({});\n".format(self.__shared_isr_table_entry_size)) 334 fp.write('{} = .;\n'.format(self.__config.shared_array_name)) 335 shared_cnt = self.__config.get_sym('CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS') 336 client_num_pads = self.__shared_isr_client_num_offset - \ 337 shared_cnt * self.__swi_table_entry_size 338 if client_num_pads < 0: 339 self.__log.error("Invalid __shared_isr_client_num_offset header value") 340 for i in range(self.__nv): 341 swt_len = len(self.__swt[i]) 342 # Add all entries 343 for j in range(shared_cnt): 344 if (swt_len <= 1) or (swt_len <= j): 345 fp.write('KEEP(*("{}"))\n'.format(self.__shared_entry_section(i, j))) 346 else: 347 sname = self.__swt[i][j] 348 if (j != 0) and (sname in self.__swt[i][0:j]): 349 fp.write('/* Repetition of "{}" section */\n'.format(sname)) 350 else: 351 fp.write('KEEP(*("{}"))\n'.format(sname)) 352 fp.write('. = . + {};\n'.format(client_num_pads)) 353 fp.write('KEEP(*("{}"))\n'.format(self.__shared_client_num_section(i))) 354 fp.write(". = ALIGN({});\n".format(self.__shared_isr_table_entry_size)) 355 356 def __write_linker_isr(self, fp): 357 fp.write(". = ALIGN({});\n".format(self.__swi_table_entry_size)) 358 fp.write('{} = .;\n'.format(self.__config.sw_isr_array_name)) 359 for i in range(self.__nv): 360 if (len(self.__swt[i])) == 1: 361 sname = self.__swt[i][0] 362 else: 363 sname = self.__isr_generated_section(i) 364 fp.write('KEEP(*("{}"))\n'.format(sname)) 365 366 def write_linker_vt(self, fp): 367 if self.__vt: 368 self.__write_linker_irq(fp) 369 370 def write_linker_swi(self, fp): 371 if self.__swt: 372 self.__write_linker_isr(fp) 373 374 if self.__config.check_shared_interrupts(): 375 self.__write_linker_shared(fp) 376