360 lines
12 KiB
Python
Executable File
360 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2017 Intel Corporation
|
|
# Copyright (c) 2018 Foundries.io
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
import argparse
|
|
import struct
|
|
import sys
|
|
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)
|
|
FIRST_LVL_INTERRUPTS = 0x000000FF
|
|
SECND_LVL_INTERRUPTS = 0x0000FF00
|
|
THIRD_LVL_INTERRUPTS = 0x00FF0000
|
|
|
|
def debug(text):
|
|
if args.debug:
|
|
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
|
|
|
|
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_path, 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;
|
|
};
|
|
"""
|
|
|
|
intlist = {}
|
|
|
|
prefix = endian_prefix()
|
|
|
|
intlist_header_fmt = prefix + "II"
|
|
if "CONFIG_64BIT" in syms:
|
|
intlist_entry_fmt = prefix + "iiQQ"
|
|
else:
|
|
intlist_entry_fmt = prefix + "iiII"
|
|
|
|
with open(intlist_path, "rb") as fp:
|
|
intdata = fp.read()
|
|
|
|
header_sz = struct.calcsize(intlist_header_fmt)
|
|
header = struct.unpack_from(intlist_header_fmt, intdata, 0)
|
|
intdata = intdata[header_sz:]
|
|
|
|
debug(str(header))
|
|
|
|
intlist["num_vectors"] = header[0]
|
|
intlist["offset"] = header[1]
|
|
|
|
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
|
|
|
|
|
|
def parse_args():
|
|
global args
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
|
|
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("-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", required=True,
|
|
help="Zephyr intlist binary for intList extraction")
|
|
|
|
args = parser.parse_args()
|
|
|
|
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 isinstance(func, int):
|
|
func_as_string = get_symbol_from_addr(syms, func)
|
|
else:
|
|
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(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 isinstance(func, int):
|
|
fp.write("\t{},\n".format(vt[i]))
|
|
else:
|
|
fp.write("\t((uintptr_t)&{}),\n".format(vt[i]))
|
|
|
|
fp.write("};\n")
|
|
|
|
source_header = """
|
|
/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
|
|
|
|
#include <zephyr/toolchain.h>
|
|
#include <zephyr/linker/sections.h>
|
|
#include <zephyr/sw_isr_table.h>
|
|
#include <zephyr/arch/cpu.h>
|
|
|
|
typedef void (* ISR)(const void *);
|
|
"""
|
|
|
|
def write_source_file(fp, vt, swt, intlist, syms):
|
|
fp.write(source_header)
|
|
|
|
nv = intlist["num_vectors"]
|
|
|
|
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)
|
|
else:
|
|
error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set")
|
|
|
|
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):
|
|
param, func = swt[i]
|
|
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:#x}, (ISR){1}}},\n".format(param, func_as_string))
|
|
fp.write("};\n")
|
|
|
|
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()}
|
|
|
|
error("Could not find symbol table")
|
|
|
|
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 main():
|
|
parse_args()
|
|
|
|
with open(args.kernel, "rb") as fp:
|
|
kernel = ELFFile(fp)
|
|
syms = get_symbols(kernel)
|
|
|
|
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))
|
|
|
|
intlist = read_intlist(args.intlist, syms)
|
|
nvec = intlist["num_vectors"]
|
|
offset = intlist["offset"]
|
|
|
|
if nvec > pow(2, 15):
|
|
raise ValueError('nvec is too large, check endianness.')
|
|
|
|
swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
|
|
vt_spurious_handler = "z_irq_spurious"
|
|
vt_irq_handler = "_isr_wrapper"
|
|
|
|
debug('offset is ' + str(offset))
|
|
debug('num_vectors is ' + str(nvec))
|
|
|
|
# Set default entries in both tables
|
|
if args.sw_isr_table:
|
|
# All vectors just jump to the common vt_irq_handler. If some entries
|
|
# are used for direct interrupts, they will be replaced later.
|
|
if args.vector_table:
|
|
vt = [vt_irq_handler for i in range(nvec)]
|
|
else:
|
|
vt = None
|
|
# Default to spurious interrupt handler. Configured interrupts
|
|
# will replace these entries.
|
|
swt = [(0, swt_spurious_handler) for i in range(nvec)]
|
|
else:
|
|
if args.vector_table:
|
|
vt = [vt_spurious_handler for i in range(nvec)]
|
|
else:
|
|
error("one or both of -s or -V needs to be specified on command line")
|
|
swt = None
|
|
|
|
for irq, flags, func, param in intlist["interrupts"]:
|
|
if flags & ISR_FLAG_DIRECT:
|
|
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
|
|
else:
|
|
# Figure out third level interrupt position
|
|
debug('IRQ = ' + hex(irq))
|
|
irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16
|
|
irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8
|
|
irq1 = (irq & FIRST_LVL_INTERRUPTS)
|
|
|
|
if irq3:
|
|
irq_parent = irq2
|
|
list_index = getindex(irq_parent, 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
|
|
|
|
# Figure out second level interrupt position
|
|
elif irq2:
|
|
irq_parent = irq1
|
|
list_index = getindex(irq_parent, 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
|
|
else:
|
|
debug('IRQ_level = 1')
|
|
debug('IRQ_Indx = ' + str(irq1))
|
|
debug('IRQ_Pos = ' + str(irq1))
|
|
table_index = irq1 - offset
|
|
|
|
if not 0 <= table_index < len(swt):
|
|
error("IRQ %d (offset=%d) exceeds the maximum of %d" %
|
|
(table_index, offset, len(swt) - 1))
|
|
if swt[table_index] != (0, swt_spurious_handler):
|
|
error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
|
|
+ f"\nExisting handler 0x{swt[table_index][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] = (param, func)
|
|
|
|
with open(args.output_source, "w") as fp:
|
|
write_source_file(fp, vt, swt, intlist, syms)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|