gen_isr_tables: New static interrupt build mechanism

This is a new mechanism for generating interrupt tables which will
be useful on many architectures. It replaces the old linker-based
mechanism for creating these tables and has a couple advantages:

 1) It is now possible to use enums as the IRQ line argument to
    IRQ_CONNECT(), which should ease CMSIS integration.
 2) The vector table itself is now generated, which lets us place
    interrupts directly into the vector table without having to
    hard-code them. This is a feature we have long enjoyed on x86
    and will enable 'direct' interrupts.
 3) More code is common, requiring less arch-specific code to
    support.

This patch introduces the common code for this mechanism. Follow-up
patches will enable it on various arches.

Issue: ZEP-1038, ZEP-1165
Change-Id: I9acd6e0de8b438fa9293f2e00563628f7510168a
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2017-02-08 17:16:29 -08:00 committed by Anas Nashif
parent 8ac992bfcd
commit 1927b3d020
11 changed files with 457 additions and 2 deletions

View file

@ -867,6 +867,9 @@ WARN_ABOUT_DEPRECATION := $(if $(CONFIG_BOARD_DEPRECATED),echo -e \
ifeq ($(ARCH),x86)
# X86 with its IDT has very special handling for interrupt tables
include $(srctree)/arch/x86/Makefile.idt
else ifeq ($(CONFIG_GEN_ISR_TABLES),y)
# Logic for interrupt tables created by scripts/gen_isr_tables.py
include $(srctree)/arch/common/Makefile.gen_isr_tables
else
# Otherwise, nothing to do, prebuilt kernel is the real one
$(KERNEL_ELF_NAME): $(PREBUILT_KERNEL)

View file

@ -110,6 +110,54 @@ config BOARD
if not found we look for the linker file in
arch/<arch>/soc/<family>/<series>
#
# Interrupt related configs
#
config GEN_ISR_TABLES
bool
prompt "Use generated IRQ tables"
default n
help
This option controls whether a platform uses the gen_isr_tables
script to generate its interrupt tables. This mechanism will create
an appropriate hardware vector table and/or software IRQ table.
config GEN_IRQ_VECTOR_TABLE
bool
prompt "Generate an interrupt vector table"
default y
depends on GEN_ISR_TABLES
help
This option controls whether a platform using gen_isr_tables
needs an interrupt vector table created. Only disable this if the
platform does not use a vector table at all, or requires the vector
table to be in a format that is not an array of function pointers
indexed by IRQ line. In the latter case, the vector table must be
supplied by the application or architecture code.
config GEN_SW_ISR_TABLE
bool
prompt "Generate a software ISR table"
default y
depends on GEN_ISR_TABLES
help
This option controls whether a platform using gen_isr_tables
needs a software ISR table table created. This is an array of struct
_isr_table_entry containing the interrupt service routine and supplied
parameter.
config GEN_IRQ_START_VECTOR
int
prompt "Starting vector for user-configurable interrupts"
default 0
depends on GEN_ISR_TABLES
help
On some architectures, part of the vector table may be reserved for
system exceptions and is declared separately from the tables
created by gen_isr_tables.py. When creating these tables, this value
will be subtracted from CONFIG_NUM_IRQS to properly size them.
source "arch/*/Kconfig"
source "boards/Kconfig"

View file

@ -1 +1 @@
obj-y += $(ARCH)/
obj-y += common/ $(ARCH)/

1
arch/common/Makefile Normal file
View file

@ -0,0 +1 @@
obj-$(CONFIG_GEN_ISR_TABLES) += isr_tables.o

View file

@ -0,0 +1,68 @@
GEN_ISR_TABLE := $(srctree)/arch/common/gen_isr_tables.py
OUTPUT_SRC := isr_tables.c
OUTPUT_OBJ := isr_tables.o
ifeq ($(ARCH),arm)
OUTPUT_FORMAT := elf32-littlearm
OUTPUT_ARCH := arm
else
$(error Output formats not defined for this architecture)
endif
GEN_ISR_TABLE_EXTRA_ARGS :=
ifeq ($(KBUILD_VERBOSE),1)
GEN_ISR_TABLE_EXTRA_ARGS += --debug
endif
ifeq ($(CONFIG_GEN_SW_ISR_TABLE),y)
GEN_ISR_TABLE_EXTRA_ARGS += --sw-isr-table
endif
ifeq ($(CONFIG_GEN_IRQ_VECTOR_TABLE),y)
GEN_ISR_TABLE_EXTRA_ARGS += --vector-table
endif
# Rule to extract the .intList section from the $(PREBUILT_KERNEL) binary
# and create the source file $(OUTPUT_SRC). This is a C file which contains
# the interrupt tables.
quiet_cmd_gen_irq = IRQ $@
cmd_gen_irq = \
( \
$(OBJCOPY) -I $(OUTPUT_FORMAT) -O binary --only-section=.intList \
$< isrList.bin && \
$(GEN_ISR_TABLE) --output-source $@ \
--intlist isrList.bin $(GEN_ISR_TABLE_EXTRA_ARGS) \
)
$(OUTPUT_SRC): $(PREBUILT_KERNEL) $(GEN_ISR_TABLE)
$(call cmd,gen_irq)
# Build system pattern rules will handle building $(OUTPUT_OBJ) from
# $(OUTPUT_SRC), nothing we need to do here explicitly for its compilation.
# Now link the kernel again, this time with the compiled interrupt tables
# included, replacing the dummy tables defined in arch/common/isr_tables.c
#
# On x86, we just strip out the intList with objcopy -j. However this is not
# very portable; for instance on ARM this results in a zero-sized program
# header segment which produces a linker warning and gives QEMU fits.
# Set to NOLOAD instead, now that we have extracted the information we need
# from it.
quiet_cmd_lnk_elf = LINK $@
cmd_lnk_elf = \
( \
$(CC) -T linker.cmd $(OUTPUT_OBJ) @$(KERNEL_NAME).lnk \
-o elf.tmp && \
$(OBJCOPY) -I $(OUTPUT_FORMAT) -O $(OUTPUT_FORMAT) \
--set-section-flags .intList=noload \
elf.tmp $@ && \
rm -f elf.tmp; \
)
$(KERNEL_ELF_NAME): $(OUTPUT_OBJ) linker.cmd
$(call cmd,lnk_elf)
@$(srctree)/scripts/check_link_map.py $(KERNEL_NAME).map
@$(WARN_ABOUT_ASSERT)
@$(WARN_ABOUT_DEPRECATION)

190
arch/common/gen_isr_tables.py Executable file
View file

@ -0,0 +1,190 @@
#!/usr/bin/env python3
#
# Copyright (c) 2017 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#
import argparse
import struct
import sys
import os
ISR_FLAG_DIRECT = (1 << 0)
def debug(text):
if not args.debug:
return
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;
uint32_t num_isrs;
uint32_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 */
int32_t irq;
/** Flags for this IRQ, see ISR_FLAG_* definitions */
int32_t flags;
/** ISR to call */
void *func;
/** Parameter for non-direct IRQs */
void *param;
};
"""
intlist = {}
prefix = endian_prefix()
intlist_header_fmt = prefix + "IIII"
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_isrs"] = header[2]
intlist["num_vectors"] = header[3]
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("-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 <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("uint32_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 main():
parse_args()
intlist = read_intlist(args.intlist)
nvec = intlist["num_vectors"]
prefix = endian_prefix()
# 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] = 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))
swt[irq] = (param, func)
with open(args.output_source, "w") as fp:
write_source_file(fp, vt, swt, intlist)
if __name__ == "__main__":
main()

52
arch/common/isr_tables.c Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <toolchain.h>
#include <sections.h>
#include <sw_isr_table.h>
#include <arch/cpu.h>
#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_GEN_IRQ_VECTOR_TABLE)
#define ISR_WRAPPER (&_isr_wrapper)
#else
#define ISR_WRAPPER NULL
#endif
/* These values are not included in the resulting binary, but instead form the
* header of the initList section, which is used by gen_isr_tables.py to create
* the vector and sw isr tables,
*/
_GENERIC_SECTION(.irq.spurious) void *_irq_spurious_ptr = &_irq_spurious;
_GENERIC_SECTION(.irq.handler) void *_irq_handler_ptr = ISR_WRAPPER;
_GENERIC_SECTION(.irq.tablesize) uint32_t _irq_table_size = IRQ_TABLE_SIZE;
/* These are placeholder tables. They will be replaced by the real tables
* generated by gen_isr_tables.py.
*/
/* Some arches don't use a vector table, they have a common exception entry
* point for all interrupts. Don't generate a table in this case.
*/
#ifdef CONFIG_GEN_IRQ_VECTOR_TABLE
uint32_t __irq_vector_table _irq_vector_table[IRQ_TABLE_SIZE] = {
[0 ...(IRQ_TABLE_SIZE - 1)] = 0xabababab,
};
#endif
/* If there are no interrupts at all, or all interrupts are of the 'direct'
* type and bypass the _sw_isr_table, then do not generate one.
*/
#ifdef CONFIG_GEN_SW_ISR_TABLE
struct _isr_table_entry __sw_isr_table _sw_isr_table[IRQ_TABLE_SIZE] = {
[0 ...(IRQ_TABLE_SIZE - 1)] = {(void *)0xcdcdcdcd, (void *)0xcdcdcdcd},
};
#endif
/* Linker needs this */
GEN_ABS_SYM_BEGIN(isr_tables_syms)
GEN_ABSOLUTE_SYM(__ISR_LIST_SIZEOF, sizeof(struct _isr_list));
GEN_ABS_SYM_END

53
include/linker/intlist.ld Normal file
View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/* This creates a special section which is not included by the final binary,
* instead it is consumed by the gen_isr_tables.py script.
*
* What we create here is a data structure:
*
* struct {
* void *spurious_irq_handler;
* void *sw_irq_handler;
* uint32_t num_isrs;
* uint32_t num_vectors;
* struct _isr_list isrs[]; <- of size num_isrs
* }
*
* Which indicates the memory address of the spurious IRQ handler and the
* number of isrs that were defined, the total number of IRQ lines in the
* system, followed by an appropriate number of instances of
* struct _isr_list. See include/sw_isr_table.h
*
* You will need to declare a bogus memory region for IDT_LIST. It doesn't
* matter where this region goes as it is stripped from the final ELF image.
* The address doesn't even have to be valid on the target. However, it
* shouldn't overlap any other regions. On most arches the following should be
* fine:
*
* MEMORY {
* .. other regions ..
* IDT_LIST : ORIGIN = 0xfffff7ff, LENGTH = 2K
* }
*/
/* We don't set NOLOAD here, as objcopy will not let us extract the intList
* section into a file if we do so; the resulting file is 0 bytes. We do so
* with objcopy in Makefile.gen_isr_tables after we have extracted the
* information we need.
*/
SECTION_PROLOGUE(.intList,,)
{
KEEP(*(.irq.spurious))
KEEP(*(.irq.handler))
LONG((__INT_LIST_END__ - __INT_LIST_START__) / __ISR_LIST_SIZEOF)
KEEP(*(.irq.tablesize))
__INT_LIST_START__ = .;
KEEP(*(.intList))
__INT_LIST_END__ = .;
} GROUP_LINK_IN(IDT_LIST)

View file

@ -15,6 +15,7 @@
#define __noinit __in_section_unique(NOINIT)
#define __irq_vector_table _GENERIC_SECTION(IRQ_VECTOR_TABLE)
#define __sw_isr_table _GENERIC_SECTION(SW_ISR_TABLE)
#if defined(CONFIG_ARM)
#define __scs_section __in_section_unique(SCS_SECTION)

View file

@ -40,8 +40,10 @@
#define NOINIT noinit
/* Interrupts */
#define IRQ_VECTOR_TABLE .irq_vector_table
#define IRQ_VECTOR_TABLE .gnu.linkonce.irq_vector_table
#define SW_ISR_TABLE .gnu.linkonce.sw_isr_table
/* Architecture-specific sections */
#if defined(CONFIG_ARM)
#define SCS_SECTION scs
#define SCP_SECTION scp

View file

@ -21,6 +21,9 @@ extern "C" {
#endif
#if !defined(_ASMLANGUAGE)
#include <stdint.h>
#include <toolchain.h>
/*
* Note the order: arg first, then ISR. This allows a table entry to be
* loaded arg -> r0, isr -> r3 in _isr_wrapper with one ldmia instruction,
@ -36,6 +39,40 @@ struct _isr_table_entry {
*/
extern struct _isr_table_entry _sw_isr_table[];
/*
* Data structure created in a special binary .intlist section for each
* configured interrupt. gen_irq_tables.py pulls this out of the binary and
* uses it to create the IRQ vector table and the _sw_isr_table.
*
* More discussion in include/linker/intlist.ld
*/
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 */
void *param;
};
/** This interrupt gets put directly in the vector table */
#define ISR_FLAG_DIRECT (1 << 0)
#define _MK_ISR_NAME(x, y) __isr_ ## x ## _irq_ ## y
/* Create an instance of struct _isr_list which gets put in the .intList
* section. This gets consumed by gen_isr_tables.py which creates the vector
* and/or SW ISR tables.
*/
#define _ISR_DECLARE(irq, flags, func, param) \
static struct _isr_list _GENERIC_SECTION(.intList) __used \
_MK_ISR_NAME(func, __COUNTER__) = \
{irq, flags, &func, (void *)param}
#define IRQ_TABLE_SIZE (CONFIG_NUM_IRQS - CONFIG_GEN_IRQ_START_VECTOR)
#endif /* _ASMLANGUAGE */
#ifdef __cplusplus