mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-05 01:16:14 +00:00
If dynamic interrupts are enabled, a set of trampoline stubs are generated which transfer control to a common dynamic interrupt handler function, which then looks up the proper handler and parameter and then executes the interrupt. Based on the prior x86 dynamic interrupt implementation which was removed from the kernel some time ago, and adapted to changes in the common interrupt handling code, build system, and IDT generation tools. An alternative approach could be to read the currently executing vector out of the APIC, but this is a much slower operation. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
294 lines
8.4 KiB
Python
Executable File
294 lines
8.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2017 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import argparse
|
|
import sys
|
|
import struct
|
|
import os
|
|
import elftools
|
|
from distutils.version import LooseVersion
|
|
from elftools.elf.elffile import ELFFile
|
|
from elftools.elf.sections import SymbolTableSection
|
|
|
|
if LooseVersion(elftools.__version__) < LooseVersion('0.24'):
|
|
sys.stderr.write("pyelftools is out of date, need version 0.24 or later\n")
|
|
sys.exit(1)
|
|
|
|
# This will never change, first selector in the GDT after the null selector
|
|
KERNEL_CODE_SEG = 0x08
|
|
|
|
# These exception vectors push an error code onto the stack.
|
|
ERR_CODE_VECTORS = [8, 10, 11, 12, 13, 14, 17]
|
|
|
|
|
|
def debug(text):
|
|
if not args.verbose:
|
|
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")
|
|
sys.exit(1)
|
|
|
|
|
|
# See Section 6.11 of the Intel Architecture Software Developer's Manual
|
|
gate_desc_format = "<HHBBH"
|
|
|
|
|
|
def create_irq_gate(handler, dpl):
|
|
present = 1
|
|
gate_type = 0xE # 32-bit interrupt gate
|
|
type_attr = gate_type | (dpl << 5) | (present << 7)
|
|
|
|
offset_hi = handler >> 16
|
|
offset_lo = handler & 0xFFFF
|
|
|
|
data = struct.pack(gate_desc_format, offset_lo, KERNEL_CODE_SEG, 0,
|
|
type_attr, offset_hi)
|
|
return data
|
|
|
|
|
|
def create_task_gate(tss, dpl):
|
|
present = 1
|
|
gate_type = 0x5 # 32-bit task gate
|
|
type_attr = gate_type | (dpl << 5) | (present << 7)
|
|
|
|
data = struct.pack(gate_desc_format, 0, tss, 0, type_attr, 0)
|
|
return data
|
|
|
|
|
|
def create_idt_binary(idt_config, filename):
|
|
with open(filename, "wb") as fp:
|
|
for handler, tss, dpl in idt_config:
|
|
if handler and tss:
|
|
error("entry specifies both handler function and tss")
|
|
|
|
if not handler and not tss:
|
|
error("entry does not specify either handler or tss")
|
|
|
|
if handler:
|
|
data = create_irq_gate(handler, dpl)
|
|
else:
|
|
data = create_task_gate(tss, dpl)
|
|
|
|
fp.write(data)
|
|
|
|
|
|
map_fmt = "<B"
|
|
|
|
|
|
def create_irq_vec_map_binary(irq_vec_map, filename):
|
|
with open(filename, "wb") as fp:
|
|
for i in irq_vec_map:
|
|
fp.write(struct.pack(map_fmt, i))
|
|
|
|
|
|
def priority_range(prio):
|
|
# Priority levels are represented as groups of 16 vectors within the IDT
|
|
base = 32 + (prio * 16)
|
|
return range(base, base + 16)
|
|
|
|
|
|
def update_irq_vec_map(irq_vec_map, irq, vector, max_irq):
|
|
# No IRQ associated; exception or software interrupt
|
|
if irq == -1:
|
|
return
|
|
|
|
if irq >= max_irq:
|
|
error("irq %d specified, but CONFIG_MAX_IRQ_LINES is %d" %
|
|
(irq, max_irq))
|
|
|
|
# This table will never have values less than 32 since those are for
|
|
# exceptions; 0 means unconfigured
|
|
if irq_vec_map[irq] != 0:
|
|
error("multiple vector assignments for interrupt line %d", irq)
|
|
|
|
debug("assign IRQ %d to vector %d" % (irq, vector))
|
|
irq_vec_map[irq] = vector
|
|
|
|
|
|
def setup_idt(spur_code, spur_nocode, intlist, max_vec, max_irq):
|
|
irq_vec_map = [0 for i in range(max_irq)]
|
|
vectors = [None for i in range(max_vec)]
|
|
|
|
# Pass 1: sanity check and set up hard-coded interrupt vectors
|
|
for handler, irq, prio, vec, dpl, tss in intlist:
|
|
if vec == -1:
|
|
if prio == -1:
|
|
error("entry does not specify vector or priority level")
|
|
continue
|
|
|
|
if vec >= max_vec:
|
|
error("Vector %d specified, but size of IDT is only %d vectors" %
|
|
(vec, max_vec))
|
|
|
|
if vectors[vec] is not None:
|
|
error("Multiple assignments for vector %d" % vec)
|
|
|
|
vectors[vec] = (handler, tss, dpl)
|
|
update_irq_vec_map(irq_vec_map, irq, vec, max_irq)
|
|
|
|
# Pass 2: set up priority-based interrupt vectors
|
|
for handler, irq, prio, vec, dpl, tss in intlist:
|
|
if vec != -1:
|
|
continue
|
|
|
|
for vi in priority_range(prio):
|
|
if vi >= max_vec:
|
|
break
|
|
if vectors[vi] is None:
|
|
vec = vi
|
|
break
|
|
|
|
if vec == -1:
|
|
error("can't find a free vector in priority level %d" % prio)
|
|
|
|
vectors[vec] = (handler, tss, dpl)
|
|
update_irq_vec_map(irq_vec_map, irq, vec, max_irq)
|
|
|
|
# Pass 3: fill in unused vectors with spurious handler at dpl=0
|
|
for i in range(max_vec):
|
|
if vectors[i] is not None:
|
|
continue
|
|
|
|
if i in ERR_CODE_VECTORS:
|
|
handler = spur_code
|
|
else:
|
|
handler = spur_nocode
|
|
|
|
vectors[i] = (handler, 0, 0)
|
|
|
|
return vectors, irq_vec_map
|
|
|
|
|
|
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()}
|
|
|
|
raise LookupError("Could not find symbol table")
|
|
|
|
# struct genidt_header_s {
|
|
# uint32_t spurious_addr;
|
|
# uint32_t spurious_no_error_addr;
|
|
# int32_t num_entries;
|
|
# };
|
|
|
|
|
|
intlist_header_fmt = "<II"
|
|
|
|
# struct genidt_entry_s {
|
|
# uint32_t isr;
|
|
# int32_t irq;
|
|
# int32_t priority;
|
|
# int32_t vector_id;
|
|
# int32_t dpl;
|
|
# int32_t tss;
|
|
# };
|
|
|
|
intlist_entry_fmt = "<Iiiiii"
|
|
|
|
|
|
def get_intlist(elf):
|
|
intdata = elf.get_section_by_name("intList").data()
|
|
|
|
header_sz = struct.calcsize(intlist_header_fmt)
|
|
header = struct.unpack_from(intlist_header_fmt, intdata, 0)
|
|
intdata = intdata[header_sz:]
|
|
|
|
spurious_code = header[0]
|
|
spurious_nocode = header[1]
|
|
|
|
debug("spurious handler (code) : %s" % hex(header[0]))
|
|
debug("spurious handler (no code) : %s" % hex(header[1]))
|
|
|
|
intlist = [i for i in
|
|
struct.iter_unpack(intlist_entry_fmt, intdata)]
|
|
|
|
debug("Configured interrupt routing")
|
|
debug("handler irq pri vec dpl")
|
|
debug("--------------------------")
|
|
|
|
for irq in intlist:
|
|
debug("{0:<10} {1:<3} {2:<3} {3:<3} {4:<2}".format(
|
|
hex(irq[0]),
|
|
"-" if irq[1] == -1 else irq[1],
|
|
"-" if irq[2] == -1 else irq[2],
|
|
"-" if irq[3] == -1 else irq[3],
|
|
irq[4]))
|
|
|
|
return (spurious_code, spurious_nocode, intlist)
|
|
|
|
|
|
def parse_args():
|
|
global args
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
|
|
parser.add_argument("-m", "--vector-map", required=True,
|
|
help="Output file mapping IRQ lines to IDT vectors")
|
|
parser.add_argument("-o", "--output-idt", required=True,
|
|
help="Output file containing IDT binary")
|
|
parser.add_argument("-a", "--output-vectors-alloc", required=False,
|
|
help="Output file indicating allocated vectors")
|
|
parser.add_argument("-k", "--kernel", required=True,
|
|
help="Zephyr kernel image")
|
|
parser.add_argument("-v", "--verbose", action="store_true",
|
|
help="Print extra debugging information")
|
|
args = parser.parse_args()
|
|
if "VERBOSE" in os.environ:
|
|
args.verbose = 1
|
|
|
|
|
|
def create_irq_vectors_allocated(vectors, spur_code, spur_nocode, filename):
|
|
# Construct a bitfield over all the IDT vectors, where if bit n is 1,
|
|
# that vector is free. those vectors have either of the two spurious
|
|
# interrupt handlers installed, they are free for runtime installation
|
|
# of interrupts
|
|
num_chars = (len(vectors) + 7) // 8
|
|
vbits = [0 for i in range(num_chars)]
|
|
for i in range(len(vectors)):
|
|
handler, _, _ = vectors[i]
|
|
if handler != spur_code and handler != spur_nocode:
|
|
continue
|
|
|
|
vbit_index = i // 8
|
|
vbit_val = 1 << (i % 8)
|
|
vbits[vbit_index] = vbits[vbit_index] | vbit_val
|
|
|
|
with open(filename, "wb") as fp:
|
|
for char in vbits:
|
|
fp.write(struct.pack("<B", char))
|
|
|
|
|
|
def main():
|
|
parse_args()
|
|
|
|
with open(args.kernel, "rb") as fp:
|
|
kernel = ELFFile(fp)
|
|
|
|
syms = get_symbols(kernel)
|
|
spur_code, spur_nocode, intlist = get_intlist(kernel)
|
|
|
|
max_irq = syms["CONFIG_MAX_IRQ_LINES"]
|
|
max_vec = syms["CONFIG_IDT_NUM_VECTORS"]
|
|
|
|
vectors, irq_vec_map = setup_idt(spur_code, spur_nocode, intlist, max_vec,
|
|
max_irq)
|
|
|
|
create_idt_binary(vectors, args.output_idt)
|
|
create_irq_vec_map_binary(irq_vec_map, args.vector_map)
|
|
if args.output_vectors_alloc:
|
|
create_irq_vectors_allocated(vectors, spur_code, spur_nocode,
|
|
args.output_vectors_alloc)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|