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:
parent
8ac992bfcd
commit
1927b3d020
3
Makefile
3
Makefile
|
@ -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)
|
||||
|
|
48
arch/Kconfig
48
arch/Kconfig
|
@ -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"
|
||||
|
|
|
@ -1 +1 @@
|
|||
obj-y += $(ARCH)/
|
||||
obj-y += common/ $(ARCH)/
|
||||
|
|
1
arch/common/Makefile
Normal file
1
arch/common/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_GEN_ISR_TABLES) += isr_tables.o
|
68
arch/common/Makefile.gen_isr_tables
Normal file
68
arch/common/Makefile.gen_isr_tables
Normal 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
190
arch/common/gen_isr_tables.py
Executable 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
52
arch/common/isr_tables.c
Normal 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
53
include/linker/intlist.ld
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue