blob: e13ef19c1f841139d6398e045c29f4753773eb32 [file] [log] [blame]
#!/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 struct
class gen_isr_parser:
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 *);
"""
source_assembly_header = """
#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
#endif
"""
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:
intlist_entry_fmt = prefix + "iiII"
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:
vt = None
if self.__config.args.sw_isr_table:
swt = [[] for i in range(nvec)]
else:
swt = None
# Process intlist and write to the tables created
for irq, flags, func, param in intlist["interrupts"]:
if self.__config.test_isr_direct(flags):
if not vt:
self.__log.error("Direct Interrupt %d declared with parameter 0x%x "
"but no vector table in use"
% (irq, param))
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:
# 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))
table_index = self.__config.get_swt_table_index(offset, irq)
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:
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))
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:
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")