zephyr/scripts/build/gen_isr_tables.py

343 lines
12 KiB
Python
Executable File

#!/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()