1e6adba9ef
In a scenario where a platform harbours multiple interrupts to the extent the core cannot support it, an interrupt controller is added as an additional level of interrupt. It typically combines several sources of interrupt into one line that is then routed to the parent controller. Signed-off-by: Rajavardhan Gundi <rajavardhan.gundi@intel.com>
287 lines
9.7 KiB
Python
Executable file
287 lines
9.7 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2017 Intel Corporation
|
|
# 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.stderr.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
|
|
raise Exception()
|
|
|
|
def endian_prefix():
|
|
if args.big_endian:
|
|
return ">"
|
|
else:
|
|
return "<"
|
|
|
|
def read_intlist(intlist_path):
|
|
"""read a binary file containing the contents of the kernel's .intList
|
|
section. This is an instance of a header created by
|
|
include/linker/intlist.ld:
|
|
|
|
struct {
|
|
void * spurious_irq_handler;
|
|
void * sw_irq_handler;
|
|
u32_t num_isrs;
|
|
u32_t num_vectors; <- typically CONFIG_NUM_IRQS
|
|
struct _isr_list isrs[]; <- of size num_isrs
|
|
}
|
|
|
|
Followed by instances of struct _isr_list created by IRQ_CONNECT()
|
|
calls:
|
|
|
|
struct _isr_list {
|
|
/** IRQ line number */
|
|
s32_t irq;
|
|
/** Flags for this IRQ, see ISR_FLAG_* definitions */
|
|
s32_t flags;
|
|
/** ISR to call */
|
|
void *func;
|
|
/** Parameter for non-direct IRQs */
|
|
void *param;
|
|
};
|
|
"""
|
|
|
|
intlist = {}
|
|
|
|
prefix = endian_prefix()
|
|
|
|
intlist_header_fmt = prefix + "IIIII"
|
|
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["spurious_handler"] = header[0]
|
|
intlist["sw_irq_handler"] = header[1]
|
|
intlist["num_vectors"] = header[2]
|
|
intlist["offset"] = header[3]
|
|
intlist["num_isrs"] = header[4]
|
|
|
|
debug("spurious handler: %s" % hex(header[0]))
|
|
|
|
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_header = """
|
|
/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
|
|
|
|
#include <toolchain.h>
|
|
#include <linker/sections.h>
|
|
#include <sw_isr_table.h>
|
|
|
|
"""
|
|
|
|
def write_source_file(fp, vt, swt, intlist):
|
|
fp.write(source_header)
|
|
|
|
nv = intlist["num_vectors"]
|
|
|
|
if vt:
|
|
fp.write("u32_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv)
|
|
for i in range(nv):
|
|
fp.write("\t0x%x,\n" % vt[i])
|
|
fp.write("};\n")
|
|
|
|
if not swt:
|
|
return
|
|
|
|
fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
|
|
% nv)
|
|
for i in range(nv):
|
|
param, func = swt[i]
|
|
fp.write("\t{(void *)0x%x, (void *)0x%x},\n" % (param, func))
|
|
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):
|
|
for indx, val in enumerate(irq_aggregator_pos):
|
|
if irq == irq_aggregator_pos[indx]:
|
|
return indx
|
|
error("The index %d has no match. Recheck interrupt configuration" % indx)
|
|
|
|
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:
|
|
if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms:
|
|
if "CONFIG_NUM_2ND_LEVEL_AGGREGATORS" in syms:
|
|
num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"]
|
|
list_2nd_lvl_offsets = []
|
|
for i in range(num_aggregators):
|
|
offset_str = 'CONFIG_2ND_LVL_INTR_' + str(i).zfill(2) + '_OFFSET'
|
|
if offset_str in syms:
|
|
list_2nd_lvl_offsets.append(syms[offset_str])
|
|
|
|
debug(str(list_2nd_lvl_offsets))
|
|
|
|
if syms["CONFIG_3RD_LEVEL_INTERRUPTS"]:
|
|
if "CONFIG_NUM_3RD_LEVEL_AGGREGATORS" in syms:
|
|
num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"]
|
|
list_3rd_lvl_offsets = []
|
|
for i in range(num_aggregators):
|
|
offset_str = 'CONFIG_3RD_LVL_INTR_' + str(i).zfill(2) + '_OFFSET'
|
|
if offset_str in syms:
|
|
list_3rd_lvl_offsets.append(syms[offset_str])
|
|
|
|
debug(str(list_3rd_lvl_offsets))
|
|
|
|
intlist = read_intlist(args.intlist)
|
|
nvec = intlist["num_vectors"]
|
|
offset = intlist["offset"]
|
|
prefix = endian_prefix()
|
|
numisrs = intlist["num_isrs"]
|
|
|
|
debug('offset is ' + str(offset))
|
|
debug('num_vectors is ' + str(nvec))
|
|
debug('num_isrs is ' + str(numisrs))
|
|
|
|
# Set default entries in both tables
|
|
if args.sw_isr_table:
|
|
# All vectors just jump to the common sw_irq_handler. If some entries
|
|
# are used for direct interrupts, they will be replaced later.
|
|
if args.vector_table:
|
|
vt = [intlist["sw_irq_handler"] for i in range(nvec)]
|
|
else:
|
|
vt = None
|
|
# Default to spurious interrupt handler. Configured interrupts
|
|
# will replace these entries.
|
|
swt = [(0, intlist["spurious_handler"]) for i in range(nvec)]
|
|
else:
|
|
if args.vector_table:
|
|
vt = [intlist["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)
|
|
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:
|
|
swt[irq - offset] = (param, func)
|
|
else:
|
|
# Figure out third level interrupt position
|
|
debug('IRQ = ' + hex(irq))
|
|
irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16
|
|
if irq3:
|
|
irq_parent = (irq & SECND_LVL_INTERRUPTS) >> 8
|
|
list_index = getindex(irq_parent, list_3rd_lvl_offsets)
|
|
irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"]
|
|
irq3_pos = irq3_baseoffset + 32*list_index + irq3 - 1
|
|
debug('IRQ_Indx = ' + str(irq3))
|
|
debug('IRQ_Pos = ' + str(irq3_pos))
|
|
swt[irq3_pos - offset] = (param, func)
|
|
|
|
# Figure out second level interrupt position
|
|
if not irq3:
|
|
irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8
|
|
if irq2:
|
|
irq_parent = (irq & FIRST_LVL_INTERRUPTS)
|
|
list_index = getindex(irq_parent, list_2nd_lvl_offsets)
|
|
irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"]
|
|
irq2_pos = irq2_baseoffset + 32*list_index + irq2 - 1
|
|
debug('IRQ_Indx = ' + str(irq2))
|
|
debug('IRQ_Pos = ' + str(irq2_pos))
|
|
swt[irq2_pos - offset] = (param, func)
|
|
|
|
# Figure out first level interrupt position
|
|
if not irq3 and not irq2:
|
|
irq1 = (irq & FIRST_LVL_INTERRUPTS)
|
|
debug('IRQ_Indx = ' + str(irq1))
|
|
debug('IRQ_Pos = ' + str(irq1))
|
|
swt[irq1 - offset] = (param, func)
|
|
|
|
with open(args.output_source, "w") as fp:
|
|
write_source_file(fp, vt, swt, intlist)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|