scripts: gen_isr_tables: Break code into classes

This commit breaks the code into functional classes.
This way the functionality is visibly splitted into
functional parts and it is easier to replace the
specific parser part to implement new code generators.

There is also common functionality to handle multi level interrupts
moved to configuration class.

Signed-off-by: Radosław Koppel <radoslaw.koppel@nordicsemi.no>
This commit is contained in:
Radoslaw Koppel 2023-11-09 09:41:43 +01:00 committed by Carles Cufí
commit 568cced14f

View file

@ -14,142 +14,227 @@ import os
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
ISR_FLAG_DIRECT = 1 << 0
# 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)
INTERRUPT_LVL_BITMASK = [0x000000FF, 0x0000FF00, 0x00FF0000]
class gen_isr_log:
INTERRUPT_BITS = [8, 8, 8]
def __init__(self, debug = False):
self.__debug = debug
swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
swt_shared_handler = "((uintptr_t)&z_shared_isr)"
vt_spurious_handler = "z_irq_spurious"
vt_irq_handler = "_isr_wrapper"
vt_default_handler = None
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
def debug(text):
if args.debug:
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
log = gen_isr_log()
def error(text):
sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n")
def endian_prefix():
if args.big_endian:
return ">"
else:
return "<"
def read_intlist(intlist_data, syms):
"""read a binary file containing the contents of the kernel's .intList
section. This is an instance of a header created by
include/zephyr/linker/intlist.ld:
struct {
uint32_t num_vectors; <- typically CONFIG_NUM_IRQS
struct _isr_list isrs[]; <- Usually of smaller size than num_vectors
}
Followed by instances of struct _isr_list created by IRQ_CONNECT()
calls:
struct _isr_list {
/** IRQ line number */
int32_t irq;
/** Flags for this IRQ, see ISR_FLAG_* definitions */
int32_t flags;
/** ISR to call */
void *func;
/** Parameter for non-direct IRQs */
const void *param;
};
class gen_isr_config:
"""All the constants and configuration gathered in single class for readability.
"""
intlist = {}
prefix = endian_prefix()
# Constants
__ISR_FLAG_DIRECT = 1 << 0
__swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
__swt_shared_handler = "((uintptr_t)&z_shared_isr)"
__vt_spurious_handler = "z_irq_spurious"
__vt_irq_handler = "_isr_wrapper"
# Extract header and the rest of the data
intlist_header_fmt = prefix + "II"
header_sz = struct.calcsize(intlist_header_fmt)
header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0)
debug(str(header_raw))
@staticmethod
def __bm(bits):
return (1 << bits) - 1
intlist["num_vectors"] = header_raw[0]
intlist["offset"] = header_raw[1]
intdata = intlist_data[header_sz:]
def __init__(self, args, syms, log):
"""Initialize the configuration object.
# Extract information about interrupts
if "CONFIG_64BIT" in syms:
intlist_entry_fmt = prefix + "iiQQ"
else:
intlist_entry_fmt = prefix + "iiII"
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
intlist["interrupts"] = [i for i in
struct.iter_unpack(intlist_entry_fmt, intdata)]
debug("Configured interrupt routing")
debug("handler irq flags param")
debug("--------------------------")
for irq in intlist["interrupts"]:
debug("{0:<10} {1:<3} {2:<3} {3}".format(
hex(irq[2]), irq[0], irq[1], hex(irq[3])))
return intlist
source_assembly_header = """
#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
#endif
"""
def get_symbol_from_addr(syms, addr):
for key, value in syms.items():
if addr == value:
return key
return None
def write_code_irq_vector_table(fp, vt, nv, syms):
fp.write(source_assembly_header)
fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n")
for i in range(nv):
func = vt[i]
if func is None:
func = vt_default_handler
if isinstance(func, int):
func_as_string = get_symbol_from_addr(syms, func)
# Select the default interrupt vector handler
if self.args.sw_isr_table:
self.__vt_default_handler = self.__vt_irq_handler
else:
func_as_string = func
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]
fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string))
fp.write("}\n")
self.__irq2_baseoffset = None
self.__irq3_baseoffset = None
self.__irq2_offsets = None
self.__irq3_offsets = None
def write_address_irq_vector_table(fp, vt, nv):
fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv)
for i in range(nv):
func = vt[i]
if self.check_multi_level_interrupts():
self.__max_irq_per = self.get_sym("CONFIG_MAX_IRQ_PER_AGGREGATOR")
if func is None:
func = vt_default_handler
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 isinstance(func, int):
fp.write("\t{},\n".format(func))
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 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:
fp.write("\t((uintptr_t)&{}),\n".format(func))
return "<"
fp.write("};\n")
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))
source_header = """
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, 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")
class gen_isr_parser:
source_header = """
/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
#include <zephyr/toolchain.h>
@ -160,270 +245,276 @@ source_header = """
typedef void (* ISR)(const void *);
"""
def write_shared_table(fp, swt, nv):
fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table"
" z_shared_sw_isr_table[%d] = {\n" % nv)
source_assembly_header = """
#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
#endif
"""
for i in range(nv):
if swt[i] is None:
client_num = 0
client_list = None
def __init__(self, intlist_data, config, log):
"""Initialize the parser.
The function prepares parser to work.
Parameters:
- intlist_data: The binnary data from intlist section
- config: The configuration object
- log: The logging object, has to have error and debug methods
"""
self.__config = config
self.__log = log
intlist = self.__read_intlist(intlist_data)
self.__vt, self.__swt, self.__nv = self.__parse_intlist(intlist)
def __read_intlist(self, intlist_data):
"""read a binary file containing the contents of the kernel's .intList
section. This is an instance of a header created by
include/zephyr/linker/intlist.ld:
struct {
uint32_t num_vectors; <- typically CONFIG_NUM_IRQS
struct _isr_list isrs[]; <- Usually of smaller size than num_vectors
}
Followed by instances of struct _isr_list created by IRQ_CONNECT()
calls:
struct _isr_list {
/** IRQ line number */
int32_t irq;
/** Flags for this IRQ, see ISR_FLAG_* definitions */
int32_t flags;
/** ISR to call */
void *func;
/** Parameter for non-direct IRQs */
const void *param;
};
"""
intlist = {}
prefix = self.__config.endian_prefix()
# Extract header and the rest of the data
intlist_header_fmt = prefix + "II"
header_sz = struct.calcsize(intlist_header_fmt)
header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0)
self.__log.debug(str(header_raw))
intlist["num_vectors"] = header_raw[0]
intlist["offset"] = header_raw[1]
intdata = intlist_data[header_sz:]
# Extract information about interrupts
if self.__config.check_64b():
intlist_entry_fmt = prefix + "iiQQ"
else:
client_num = len(swt[i])
client_list = swt[i]
intlist_entry_fmt = prefix + "iiII"
if client_num <= 1:
fp.write("\t{ },\n")
intlist["interrupts"] = [i for i in
struct.iter_unpack(intlist_entry_fmt, intdata)]
self.__log.debug("Configured interrupt routing")
self.__log.debug("handler irq flags param")
self.__log.debug("--------------------------")
for irq in intlist["interrupts"]:
self.__log.debug("{0:<10} {1:<3} {2:<3} {3}".format(
hex(irq[2]), irq[0], irq[1], hex(irq[3])))
return intlist
def __parse_intlist(self, intlist):
"""All the intlist data are parsed into swt and vt arrays.
The vt array is prepared for hardware interrupt table.
Every entry in the selected position would contain None or the name of the function pointer
(address or string).
The swt is a little more complex. At every position it would contain an array of parameter and
function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry.
If empty array is placed on selected position - it means that the application does not implement
this interrupt.
Parameters:
- intlist: The preprocessed list of intlist section content (see read_intlist)
Return:
vt, swt - parsed vt and swt arrays (see function description above)
"""
nvec = intlist["num_vectors"]
offset = intlist["offset"]
if nvec > pow(2, 15):
raise ValueError('nvec is too large, check endianness.')
self.__log.debug('offset is ' + str(offset))
self.__log.debug('num_vectors is ' + str(nvec))
# Set default entries in both tables
if not(self.__config.args.sw_isr_table or self.__config.args.vector_table):
self.__log.error("one or both of -s or -V needs to be specified on command line")
if self.__config.args.vector_table:
vt = [None for i in range(nvec)]
else:
fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ")
for j in range(0, client_num):
routine = client_list[j][1]
arg = client_list[j][0]
fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, "
f".arg = (const void *){hex(arg)} }},")
fp.write(" },\n},\n")
fp.write("};\n")
def write_source_file(fp, vt, swt, intlist, syms):
fp.write(source_header)
nv = intlist["num_vectors"]
if "CONFIG_SHARED_INTERRUPTS" in syms:
write_shared_table(fp, swt, nv)
if vt:
if "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS" in syms:
write_address_irq_vector_table(fp, vt, nv)
elif "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE" in syms:
write_code_irq_vector_table(fp, vt, nv, syms)
vt = None
if self.__config.args.sw_isr_table:
swt = [[] for i in range(nvec)]
else:
error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set")
swt = None
if not swt:
return
fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
% nv)
level2_offset = syms.get("CONFIG_2ND_LVL_ISR_TBL_OFFSET")
level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET")
for i in range(nv):
if len(swt[i]) == 0:
# Not used interrupt
param = "0x0"
func = swt_spurious_handler
elif len(swt[i]) == 1:
# Single interrupt
param = "{0:#x}".format(swt[i][0][0])
func = swt[i][0][1]
else:
# Shared interrupt
param = "&z_shared_sw_isr_table[{0}]".format(i)
func = swt_shared_handler
if isinstance(func, int):
func_as_string = "{0:#x}".format(func)
else:
func_as_string = func
if level2_offset is not None and i == level2_offset:
fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n".
format(level2_offset))
if level3_offset is not None and i == level3_offset:
fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
format(level3_offset))
fp.write("\t{{(const void *){0}, (ISR){1}}}, /* {2} */\n".format(param, func_as_string, i))
fp.write("};\n")
def getindex(irq, irq_aggregator_pos):
try:
return irq_aggregator_pos.index(irq)
except ValueError:
error("IRQ {} not present in parent offsets ({}). ".
format(irq, irq_aggregator_pos) +
" Recheck interrupt configuration.")
def bit_mask(bits):
return (1 << bits) - 1
def update_masks(syms):
if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"]
INTERRUPT_BITS[0] = syms["CONFIG_1ST_LEVEL_INTERRUPT_BITS"]
INTERRUPT_BITS[1] = syms["CONFIG_2ND_LEVEL_INTERRUPT_BITS"]
INTERRUPT_BITS[2] = syms["CONFIG_3RD_LEVEL_INTERRUPT_BITS"]
if sum(INTERRUPT_BITS) > 32:
raise ValueError("Too many interrupt bits")
INTERRUPT_LVL_BITMASK[0] = bit_mask(INTERRUPT_BITS[0])
INTERRUPT_LVL_BITMASK[1] = bit_mask(INTERRUPT_BITS[1]) << INTERRUPT_BITS[0]
INTERRUPT_LVL_BITMASK[2] = bit_mask(INTERRUPT_BITS[2]) << INTERRUPT_BITS[0] + INTERRUPT_BITS[1]
debug("Level Bits Bitmask")
debug("----------------------------")
for i in range(3):
bitmask_str = "0x" + format(INTERRUPT_LVL_BITMASK[i], '08X')
debug(f"{i + 1:>5} {INTERRUPT_BITS[i]:>7} {bitmask_str:>14}")
def update_vt_default_handler():
"""Update the vt default handler based on parsed arguments.
The default vt handler would be different depending on the fact if we have sw_isr_table.
If we have it - the default handler would lead to common handler.
If we do not - the default handler would be spurious handler
The result is writen into vt_default_handler variable.
"""
global vt_default_handler
if args.sw_isr_table:
vt_default_handler = vt_irq_handler
else:
vt_default_handler = vt_spurious_handler
def parse_intlist(intlist, syms):
"""All the intlist data are parsed into swt and vt arrays.
The vt array is prepared for hardware interrupt table.
Every entry in the selected position would contain None or the name of the function pointer
(address or string).
The swt is a little more complex. At every position it would contain an array of parameter and
function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry.
If empty array is placed on selected position - it means that the application does not implement
this interrupt.
Parameters:
- intlist: The preprocessed list of intlist section content (see read_intlist)
- syms: Symbols
Return:
vt, swt - parsed vt and swt arrays (see function description above)
"""
nvec = intlist["num_vectors"]
offset = intlist["offset"]
if nvec > pow(2, 15):
raise ValueError('nvec is too large, check endianness.')
debug('offset is ' + str(offset))
debug('num_vectors is ' + str(nvec))
# Set default entries in both tables
if not(args.sw_isr_table or args.vector_table):
error("one or both of -s or -V needs to be specified on command line")
if args.vector_table:
vt = [None for i in range(nvec)]
else:
vt = None
if args.sw_isr_table:
swt = [[] for i in range(nvec)]
else:
swt = None
# Gather info about multi level interrupts if enabled
if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"]
if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms:
num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"]
irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"]
list_2nd_lvl_offsets = [syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'.
format(str(i).zfill(2))] for i in
range(num_aggregators)]
debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets))
if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms:
num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"]
irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"]
list_3rd_lvl_offsets = [syms['CONFIG_3RD_LVL_INTR_{}_OFFSET'.
format(str(i).zfill(2))] for i in
range(num_aggregators)]
debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets))
# Process intlist and write to the tables created
for irq, flags, func, param in intlist["interrupts"]:
if flags & ISR_FLAG_DIRECT:
# Process intlist and write to the tables created
for irq, flags, func, param in intlist["interrupts"]:
if not vt:
error("Direct Interrupt %d declared with parameter 0x%x "
"but no vector table in use"
% (irq, param))
if param != 0:
error("Direct irq %d declared, but has non-NULL parameter"
% irq)
if not 0 <= irq - offset < len(vt):
error("IRQ %d (offset=%d) exceeds the maximum of %d" %
(irq - offset, offset, len(vt) - 1))
vt[irq - offset] = func
else:
# Regular interrupt
if not swt:
error("Regular Interrupt %d declared with parameter 0x%x "
"but no SW ISR_TABLE in use"
% (irq, param))
if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
table_index = irq - offset
if self.__config.test_isr_direct(flags):
if param != 0:
self.__log.error("Direct irq %d declared, but has non-NULL parameter"
% irq)
if not 0 <= irq - offset < len(vt):
self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
(irq - offset, offset, len(vt) - 1))
vt[irq - offset] = func
else:
# Figure out third level interrupt position
debug('IRQ = ' + hex(irq))
irq3 = (irq & INTERRUPT_LVL_BITMASK[2]) >> (INTERRUPT_BITS[0] + INTERRUPT_BITS[1])
irq2 = (irq & INTERRUPT_LVL_BITMASK[1]) >> (INTERRUPT_BITS[0])
irq1 = irq & INTERRUPT_LVL_BITMASK[0]
# Regular interrupt
if not swt:
self.__log.error("Regular Interrupt %d declared with parameter 0x%x "
"but no SW ISR_TABLE in use"
% (irq, param))
if irq3:
list_index = getindex(irq2, list_3rd_lvl_offsets)
irq3_pos = irq3_baseoffset + max_irq_per*list_index + irq3 - 1
debug('IRQ_level = 3')
debug('IRQ_Indx = ' + str(irq3))
debug('IRQ_Pos = ' + str(irq3_pos))
table_index = irq3_pos - offset
table_index = self.__config.get_swt_table_index(offset, irq)
# Figure out second level interrupt position
elif irq2:
list_index = getindex(irq1, list_2nd_lvl_offsets)
irq2_pos = irq2_baseoffset + max_irq_per*list_index + irq2 - 1
debug('IRQ_level = 2')
debug('IRQ_Indx = ' + str(irq2))
debug('IRQ_Pos = ' + str(irq2_pos))
table_index = irq2_pos - offset
# Figure out first level interrupt position
if not 0 <= table_index < len(swt):
self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
(table_index, offset, len(swt) - 1))
if self.__config.check_shared_interrupts():
lst = swt[table_index]
if (param, func) in lst:
self.__log.error("Attempting to register the same ISR/arg pair twice.")
if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"):
self.__log.error(f"Reached shared interrupt client limit. Maybe increase"
+ f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
else:
debug('IRQ_level = 1')
debug('IRQ_Indx = ' + str(irq1))
debug('IRQ_Pos = ' + str(irq1))
table_index = irq1 - offset
if len(swt[table_index]) > 0:
self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
+ f"\nExisting handler 0x{swt[table_index][0][1]:x}, new handler 0x{func:x}"
+ "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
)
swt[table_index].append((param, func))
if not 0 <= table_index < len(swt):
error("IRQ %d (offset=%d) exceeds the maximum of %d" %
(table_index, offset, len(swt) - 1))
if "CONFIG_SHARED_INTERRUPTS" in syms:
lst = swt[table_index]
if (param, func) in lst:
error("Attempting to register the same ISR/arg pair twice.")
if len(lst) >= syms["CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"]:
error(f"Reached shared interrupt client limit. Maybe increase"
+ f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
return vt, swt, nvec
def __write_code_irq_vector_table(self, fp):
fp.write(self.source_assembly_header)
fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n")
for i in range(self.__nv):
func = self.__vt[i]
if func is None:
func = self.__config.vt_default_handler
if isinstance(func, int):
func_as_string = self.__config.get_sym_from_addr(func)
else:
if len(swt[table_index]) > 0:
error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
+ f"\nExisting handler 0x{swt[table_index][0][1]:x}, new handler 0x{func:x}"
+ "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
)
swt[table_index].append((param, func))
func_as_string = func
fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string))
fp.write("}\n")
def __write_address_irq_vector_table(self, fp):
fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % self.__nv)
for i in range(self.__nv):
func = self.__vt[i]
if func is None:
func = self.__config.vt_default_handler
if isinstance(func, int):
fp.write("\t{},\n".format(func))
else:
fp.write("\t((uintptr_t)&{}),\n".format(func))
fp.write("};\n")
def __write_shared_table(self, fp):
fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table"
" z_shared_sw_isr_table[%d] = {\n" % self.__nv)
for i in range(self.__nv):
if self.__swt[i] is None:
client_num = 0
client_list = None
else:
client_num = len(self.__swt[i])
client_list = self.__swt[i]
if client_num <= 1:
fp.write("\t{ },\n")
else:
fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ")
for j in range(0, client_num):
routine = client_list[j][1]
arg = client_list[j][0]
fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, "
f".arg = (const void *){hex(arg)} }},")
fp.write(" },\n},\n")
fp.write("};\n")
def write_source(self, fp):
fp.write(self.source_header)
if self.__config.check_shared_interrupts():
self.__write_shared_table(fp)
if self.__vt:
if self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS"):
self.__write_address_irq_vector_table(fp)
elif self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE"):
self.__write_code_irq_vector_table(fp)
else:
self.__log.error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set")
if not self.__swt:
return
fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
% self.__nv)
level2_offset = self.__config.get_irq_baseoffset(2)
level3_offset = self.__config.get_irq_baseoffset(3)
for i in range(self.__nv):
if len(self.__swt[i]) == 0:
# Not used interrupt
param = "0x0"
func = self.__config.swt_spurious_handler
elif len(self.__swt[i]) == 1:
# Single interrupt
param = "{0:#x}".format(self.__swt[i][0][0])
func = self.__swt[i][0][1]
else:
# Shared interrupt
param = "&z_shared_sw_isr_table[{0}]".format(i)
func = self.__config.swt_shared_handler
if isinstance(func, int):
func_as_string = "{0:#x}".format(func)
else:
func_as_string = func
if level2_offset is not None and i == level2_offset:
fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n".
format(level2_offset))
if level3_offset is not None and i == level3_offset:
fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
format(level3_offset))
fp.write("\t{{(const void *){0}, (ISR){1}}}, /* {2} */\n".format(param, func_as_string, i))
fp.write("};\n")
return vt, swt
def get_symbols(obj):
for section in obj.iter_sections():
@ -431,7 +522,7 @@ def get_symbols(obj):
return {sym.name: sym.entry.st_value
for sym in section.iter_symbols()}
error("Could not find symbol table")
log.error("Could not find symbol table")
def read_intList_sect(elfobj, snames):
"""
@ -442,19 +533,17 @@ def read_intList_sect(elfobj, snames):
for sname in snames:
intList_sect = elfobj.get_section_by_name(sname)
if intList_sect is not None:
debug("Found intlist section: \"{}\"".format(sname))
log.debug("Found intlist section: \"{}\"".format(sname))
break
if intList_sect is None:
error("Cannot find the intlist section!")
log.error("Cannot find the intlist section!")
intdata = intList_sect.data()
return intdata
def parse_args():
global args
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
@ -475,26 +564,23 @@ def parse_args():
help="The name of the section to search for the interrupt data. "
"This is accumulative argument. The first section found would be used.")
args = parser.parse_args()
return parser.parse_args()
def main():
"""
All the input data are parsed to vt
"""
parse_args()
args = parse_args()
# Configure logging as soon as possible
log.set_debug(args.debug)
with open(args.kernel, "rb") as fp:
kernel = ELFFile(fp)
syms = get_symbols(kernel)
intlist_data = read_intList_sect(kernel, args.intlist_section)
intlist = read_intlist(intlist_data, syms)
config = gen_isr_config(args, get_symbols(kernel), log)
intlist_data = read_intList_sect(kernel, config.get_intlist_snames())
update_masks(syms)
update_vt_default_handler()
vt, swt = parse_intlist(intlist, syms)
parser = gen_isr_parser(intlist_data, config, log)
with open(args.output_source, "w") as fp:
write_source_file(fp, vt, swt, intlist, syms)
parser.write_source(fp)
if __name__ == "__main__":
main()