arch: Add support for IRQ vector tables with jump opcodes

The whole mechanism of IRQ table generation is build around the
assumption that the IRQ vector table contains an array of addresses the
PC will be assigned to when the corresponding interrupt is triggered.

While this is correct for the majority of architectures (ARM, RISCV with
CLIC in vectored mode, etc...) this is not valid in general (for example
RISCV with CLINT/HLINT in vectored mode).

In this alternative format for the IRQ vector table, the pc will get
assigned by the hardware to the address of the vector table index
corresponding to the interrupt ID. From the vector table index, a
subsequent jump will occur from there to service the interrupt.

This means that the IRQ vector table contains an opcode that is a jump
instruction to a specific location instead of the address of the
location itself.

This patch is introducing support for this alternative IRQ vector table
format. The user can now select one format or the other one by acting on
IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS or IRQ_VECTOR_TABLE_JUMP_BY_CODE
Kconfig symbols.

Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
Carlo Caione 2022-06-27 10:43:22 +02:00 committed by Carles Cufí
parent e381170282
commit 86a67faeaa
3 changed files with 88 additions and 11 deletions

View file

@ -418,6 +418,24 @@ config ARCH_IRQ_VECTOR_TABLE_ALIGN
to be aligned to architecture specific size. The default
size is 0 for no alignment.
choice IRQ_VECTOR_TABLE_TYPE
prompt "IRQ vector table type"
depends on GEN_IRQ_VECTOR_TABLE
default IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS
config IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS
bool "Jump by address"
help
The IRQ vector table contains the address of the interrupt handler.
config IRQ_VECTOR_TABLE_JUMP_BY_CODE
bool "Jump by code"
help
The IRQ vector table contains the opcode of a jump instruction to the
interrupt handler address.
endchoice
config GEN_SW_ISR_TABLE
bool "Generate a software ISR table"
default y

View file

@ -125,6 +125,45 @@ def parse_args():
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! */
@ -142,10 +181,12 @@ def write_source_file(fp, vt, swt, intlist, syms):
nv = intlist["num_vectors"]
if vt:
fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % nv)
for i in range(nv):
fp.write("\t{},\n".format(vt[i]))
fp.write("};\n")
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
@ -224,26 +265,27 @@ def main():
if nvec > pow(2, 15):
raise ValueError('nvec is too large, check endianness.')
spurious_handler = "((uintptr_t)&z_irq_spurious)"
sw_irq_handler = "((uintptr_t)&_isr_wrapper)"
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 sw_irq_handler. If some entries
# 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 = [sw_irq_handler for i in range(nvec)]
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, spurious_handler) for i in range(nvec)]
swt = [(0, swt_spurious_handler) for i in range(nvec)]
else:
if args.vector_table:
vt = [spurious_handler for i in range(nvec)]
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
@ -302,7 +344,7 @@ def main():
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, spurious_handler):
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?"

View file

@ -50,9 +50,26 @@ Z_GENERIC_SECTION(.irq_info) struct int_list_header _iheader = {
#define IRQ_VECTOR_TABLE_DEFAULT_ISR z_irq_spurious
#endif /* CONFIG_GEN_SW_ISR_TABLE */
#ifdef CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE
/* Assembly code for a jump instruction. Must be set by the architecture. */
#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
#endif
#define BUILD_VECTOR(n, _) __asm(ARCH_IRQ_VECTOR_JUMP_CODE(IRQ_VECTOR_TABLE_DEFAULT_ISR))
/* The IRQ vector table contains the jump opcodes towards the vector routine */
void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {
LISTIFY(CONFIG_NUM_IRQS, BUILD_VECTOR, (;));
};
#else
/* The IRQ vector table is an array of vector addresses */
uintptr_t __irq_vector_table _irq_vector_table[IRQ_TABLE_SIZE] = {
[0 ...(IRQ_TABLE_SIZE - 1)] = (uintptr_t)&IRQ_VECTOR_TABLE_DEFAULT_ISR,
};
#endif /* CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE */
#endif /* CONFIG_GEN_IRQ_VECTOR_TABLE */
/* If there are no interrupts at all, or all interrupts are of the 'direct'