You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
342 lines
12 KiB
342 lines
12 KiB
#!/usr/bin/env python3 |
|
# |
|
# Copyright (c) 2017 Intel Corporation |
|
# Copyright (c) 2018 Foundries.io |
|
# Copyright (c) 2023 Nordic Semiconductor NA |
|
# |
|
# SPDX-License-Identifier: Apache-2.0 |
|
# |
|
|
|
import argparse |
|
import sys |
|
import os |
|
import importlib |
|
from elftools.elf.elffile import ELFFile |
|
from elftools.elf.sections import SymbolTableSection |
|
|
|
|
|
class gen_isr_log: |
|
|
|
def __init__(self, debug = False): |
|
self.__debug = debug |
|
|
|
def debug(self, text): |
|
"""Print debug message if debugging is enabled. |
|
|
|
Note - this function requires config global variable to be initialized. |
|
""" |
|
if self.__debug: |
|
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
|
|
|
@staticmethod |
|
def error(text): |
|
sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n") |
|
|
|
def set_debug(self, state): |
|
self.__debug = state |
|
|
|
|
|
log = gen_isr_log() |
|
|
|
|
|
class gen_isr_config: |
|
"""All the constants and configuration gathered in single class for readability. |
|
""" |
|
# Constants |
|
__ISR_FLAG_DIRECT = 1 << 0 |
|
__swt_spurious_handler = "z_irq_spurious" |
|
__swt_shared_handler = "z_shared_isr" |
|
__vt_spurious_handler = "z_irq_spurious" |
|
__vt_irq_handler = "_isr_wrapper" |
|
__shared_array_name = "z_shared_sw_isr_table" |
|
__sw_isr_array_name = "_sw_isr_table" |
|
__irq_vector_array_name = "_irq_vector_table" |
|
|
|
@staticmethod |
|
def __bm(bits): |
|
return (1 << bits) - 1 |
|
|
|
def __init__(self, args, syms, log): |
|
"""Initialize the configuration object. |
|
|
|
The configuration object initialization takes only arguments as a parameter. |
|
This is done to allow debug function work as soon as possible. |
|
""" |
|
# Store the arguments required for work |
|
self.__args = args |
|
self.__syms = syms |
|
self.__log = log |
|
|
|
# Select the default interrupt vector handler |
|
if self.args.sw_isr_table: |
|
self.__vt_default_handler = self.__vt_irq_handler |
|
else: |
|
self.__vt_default_handler = self.__vt_spurious_handler |
|
# Calculate interrupt bits |
|
self.__int_bits = [8, 8, 8] |
|
# The below few hardware independent magic numbers represent various |
|
# levels of interrupts in a multi-level interrupt system. |
|
# 0x000000FF - represents the 1st level (i.e. the interrupts |
|
# that directly go to the processor). |
|
# 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel |
|
# into 1 line which then goes into the 1st level) |
|
# 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel |
|
# into 1 line which then goes into the 2nd level) |
|
self.__int_lvl_masks = [0x000000FF, 0x0000FF00, 0x00FF0000] |
|
|
|
self.__irq2_baseoffset = None |
|
self.__irq3_baseoffset = None |
|
self.__irq2_offsets = None |
|
self.__irq3_offsets = None |
|
|
|
if self.check_multi_level_interrupts(): |
|
self.__max_irq_per = self.get_sym("CONFIG_MAX_IRQ_PER_AGGREGATOR") |
|
|
|
self.__int_bits[0] = self.get_sym("CONFIG_1ST_LEVEL_INTERRUPT_BITS") |
|
self.__int_bits[1] = self.get_sym("CONFIG_2ND_LEVEL_INTERRUPT_BITS") |
|
self.__int_bits[2] = self.get_sym("CONFIG_3RD_LEVEL_INTERRUPT_BITS") |
|
|
|
if sum(self.int_bits) > 32: |
|
raise ValueError("Too many interrupt bits") |
|
|
|
self.__int_lvl_masks[0] = self.__bm(self.int_bits[0]) |
|
self.__int_lvl_masks[1] = self.__bm(self.int_bits[1]) << self.int_bits[0] |
|
self.__int_lvl_masks[2] = self.__bm(self.int_bits[2]) << (self.int_bits[0] + self.int_bits[1]) |
|
|
|
self.__log.debug("Level Bits Bitmask") |
|
self.__log.debug("----------------------------") |
|
for i in range(3): |
|
bitmask_str = "0x" + format(self.__int_lvl_masks[i], '08X') |
|
self.__log.debug(f"{i + 1:>5} {self.__int_bits[i]:>7} {bitmask_str:>14}") |
|
|
|
if self.check_sym("CONFIG_2ND_LEVEL_INTERRUPTS"): |
|
num_aggregators = self.get_sym("CONFIG_NUM_2ND_LEVEL_AGGREGATORS") |
|
self.__irq2_baseoffset = self.get_sym("CONFIG_2ND_LVL_ISR_TBL_OFFSET") |
|
self.__irq2_offsets = [self.get_sym('CONFIG_2ND_LVL_INTR_{}_OFFSET'. |
|
format(str(i).zfill(2))) for i in |
|
range(num_aggregators)] |
|
|
|
self.__log.debug('2nd level offsets: {}'.format(self.__irq2_offsets)) |
|
|
|
if self.check_sym("CONFIG_3RD_LEVEL_INTERRUPTS"): |
|
num_aggregators = self.get_sym("CONFIG_NUM_3RD_LEVEL_AGGREGATORS") |
|
self.__irq3_baseoffset = self.get_sym("CONFIG_3RD_LVL_ISR_TBL_OFFSET") |
|
self.__irq3_offsets = [self.get_sym('CONFIG_3RD_LVL_INTR_{}_OFFSET'. |
|
format(str(i).zfill(2))) for i in |
|
range(num_aggregators)] |
|
|
|
self.__log.debug('3rd level offsets: {}'.format(self.__irq3_offsets)) |
|
|
|
@property |
|
def args(self): |
|
return self.__args |
|
|
|
@property |
|
def swt_spurious_handler(self): |
|
return self.__swt_spurious_handler |
|
|
|
@property |
|
def swt_shared_handler(self): |
|
return self.__swt_shared_handler |
|
|
|
@property |
|
def vt_default_handler(self): |
|
return self.__vt_default_handler |
|
|
|
@property |
|
def shared_array_name(self): |
|
return self.__shared_array_name |
|
|
|
@property |
|
def sw_isr_array_name(self): |
|
return self.__sw_isr_array_name |
|
|
|
@property |
|
def irq_vector_array_name(self): |
|
return self.__irq_vector_array_name |
|
|
|
@property |
|
def int_bits(self): |
|
return self.__int_bits |
|
|
|
@property |
|
def int_lvl_masks(self): |
|
return self.__int_lvl_masks |
|
|
|
def endian_prefix(self): |
|
if self.args.big_endian: |
|
return ">" |
|
else: |
|
return "<" |
|
|
|
def get_irq_baseoffset(self, lvl): |
|
if lvl == 2: |
|
return self.__irq2_baseoffset |
|
if lvl == 3: |
|
return self.__irq3_baseoffset |
|
self.__log.error("Unsupported irq level: {}".format(lvl)) |
|
|
|
def get_irq_index(self, irq, lvl): |
|
if lvl == 2: |
|
offsets = self.__irq2_offsets |
|
elif lvl == 3: |
|
offsets = self.__irq3_offsets |
|
else: |
|
self.__log.error("Unsupported irq level: {}".format(lvl)) |
|
try: |
|
return offsets.index(irq) |
|
except ValueError: |
|
self.__log.error("IRQ {} not present in parent offsets ({}). ". |
|
format(irq, offsets) + |
|
" Recheck interrupt configuration.") |
|
|
|
def get_swt_table_index(self, offset, irq): |
|
if not self.check_multi_level_interrupts(): |
|
return irq - offset |
|
# Calculate index for multi level interrupts |
|
self.__log.debug('IRQ = ' + hex(irq)) |
|
irq3 = (irq & self.int_lvl_masks[2]) >> (self.int_bits[0] + self.int_bits[1]) |
|
irq2 = (irq & self.int_lvl_masks[1]) >> (self.int_bits[0]) |
|
irq1 = irq & self.int_lvl_masks[0] |
|
# Figure out third level interrupt position |
|
if irq3: |
|
list_index = self.get_irq_index(irq2 - 1, 3) |
|
irq3_pos = self.get_irq_baseoffset(3) + self.__max_irq_per * list_index + irq3 - 1 |
|
self.__log.debug('IRQ_level = 3') |
|
self.__log.debug('IRQ_Indx = ' + str(irq3)) |
|
self.__log.debug('IRQ_Pos = ' + str(irq3_pos)) |
|
return irq3_pos - offset |
|
# Figure out second level interrupt position |
|
if irq2: |
|
list_index = self.get_irq_index(irq1, 2) |
|
irq2_pos = self.get_irq_baseoffset(2) + self.__max_irq_per * list_index + irq2 - 1 |
|
self.__log.debug('IRQ_level = 2') |
|
self.__log.debug('IRQ_Indx = ' + str(irq2)) |
|
self.__log.debug('IRQ_Pos = ' + str(irq2_pos)) |
|
return irq2_pos - offset |
|
# Figure out first level interrupt position |
|
self.__log.debug('IRQ_level = 1') |
|
self.__log.debug('IRQ_Indx = ' + str(irq1)) |
|
self.__log.debug('IRQ_Pos = ' + str(irq1)) |
|
return irq1 - offset |
|
|
|
def get_intlist_snames(self): |
|
return self.args.intlist_section |
|
|
|
def test_isr_direct(self, flags): |
|
return flags & self.__ISR_FLAG_DIRECT |
|
|
|
def get_sym_from_addr(self, addr): |
|
for key, value in self.__syms.items(): |
|
if addr == value: |
|
return key |
|
return None |
|
|
|
def get_sym(self, name): |
|
return self.__syms.get(name) |
|
|
|
def check_sym(self, name): |
|
return name in self.__syms |
|
|
|
def check_multi_level_interrupts(self): |
|
return self.check_sym("CONFIG_MULTI_LEVEL_INTERRUPTS") |
|
|
|
def check_shared_interrupts(self): |
|
return self.check_sym("CONFIG_SHARED_INTERRUPTS") |
|
|
|
def check_64b(self): |
|
return self.check_sym("CONFIG_64BIT") |
|
|
|
|
|
def get_symbols(obj): |
|
for section in obj.iter_sections(): |
|
if isinstance(section, SymbolTableSection): |
|
return {sym.name: sym.entry.st_value |
|
for sym in section.iter_symbols()} |
|
|
|
log.error("Could not find symbol table") |
|
|
|
def read_intList_sect(elfobj, snames): |
|
""" |
|
Load the raw intList section data in a form of byte array. |
|
""" |
|
intList_sect = None |
|
|
|
for sname in snames: |
|
intList_sect = elfobj.get_section_by_name(sname) |
|
if intList_sect is not None: |
|
log.debug("Found intlist section: \"{}\"".format(sname)) |
|
break |
|
|
|
if intList_sect is None: |
|
log.error("Cannot find the intlist section!") |
|
|
|
intdata = intList_sect.data() |
|
|
|
return intdata |
|
|
|
def parse_args(): |
|
parser = argparse.ArgumentParser(description=__doc__, |
|
formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) |
|
|
|
parser.add_argument("-e", "--big-endian", action="store_true", |
|
help="Target encodes data in big-endian format (little endian is " |
|
"the default)") |
|
parser.add_argument("-d", "--debug", action="store_true", |
|
help="Print additional debugging information") |
|
parser.add_argument("-o", "--output-source", required=True, |
|
help="Output source file") |
|
parser.add_argument("-l", "--linker-output-files", |
|
nargs=2, |
|
metavar=("vector_table_link", "software_interrupt_link"), |
|
help="Output linker files. " |
|
"Used only if CONFIG_ISR_TABLES_LOCAL_DECLARATION is enabled. " |
|
"In other case empty file would be generated.") |
|
parser.add_argument("-k", "--kernel", required=True, |
|
help="Zephyr kernel image") |
|
parser.add_argument("-s", "--sw-isr-table", action="store_true", |
|
help="Generate SW ISR table") |
|
parser.add_argument("-V", "--vector-table", action="store_true", |
|
help="Generate vector table") |
|
parser.add_argument("-i", "--intlist-section", action="append", required=True, |
|
help="The name of the section to search for the interrupt data. " |
|
"This is accumulative argument. The first section found would be used.") |
|
|
|
return parser.parse_args() |
|
|
|
def main(): |
|
args = parse_args() |
|
# Configure logging as soon as possible |
|
log.set_debug(args.debug) |
|
|
|
with open(args.kernel, "rb") as fp: |
|
kernel = ELFFile(fp) |
|
config = gen_isr_config(args, get_symbols(kernel), log) |
|
intlist_data = read_intList_sect(kernel, config.get_intlist_snames()) |
|
|
|
if config.check_sym("CONFIG_ISR_TABLES_LOCAL_DECLARATION"): |
|
parser_module = importlib.import_module('gen_isr_tables_parser_local') |
|
parser = parser_module.gen_isr_parser(intlist_data, config, log) |
|
else: |
|
parser_module = importlib.import_module('gen_isr_tables_parser_carrays') |
|
parser = parser_module.gen_isr_parser(intlist_data, config, log) |
|
|
|
with open(args.output_source, "w") as fp: |
|
parser.write_source(fp) |
|
|
|
if args.linker_output_files is not None: |
|
with open(args.linker_output_files[0], "w") as fp_vt, \ |
|
open(args.linker_output_files[1], "w") as fp_swi: |
|
if hasattr(parser, 'write_linker_vt'): |
|
parser.write_linker_vt(fp_vt) |
|
else: |
|
log.debug("Chosen parser does not support vector table linker file") |
|
fp_vt.write('/* Empty */\n') |
|
if hasattr(parser, 'write_linker_swi'): |
|
parser.write_linker_swi(fp_swi) |
|
else: |
|
log.debug("Chosen parser does not support software interrupt linker file") |
|
fp_swi.write('/* Empty */\n') |
|
|
|
if __name__ == "__main__": |
|
main()
|
|
|