|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Copyright (c) 2017 Intel Corporation | 
|  | # | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | """Generate Interrupt Descriptor Table for x86 CPUs. | 
|  |  | 
|  | This script generates the interrupt descriptor table (IDT) for x86. | 
|  | Please consult the IA Architecture SW Developer Manual, volume 3, | 
|  | for more details on this data structure. | 
|  |  | 
|  | This script accepts as input the zephyr_prebuilt.elf binary, | 
|  | which is a link of the Zephyr kernel without various build-time | 
|  | generated data structures (such as the IDT) inserted into it. | 
|  | This kernel image has been properly padded such that inserting | 
|  | these data structures will not disturb the memory addresses of | 
|  | other symbols. From the kernel binary we read a special section | 
|  | "intList" which contains the desired interrupt routing configuration | 
|  | for the kernel, populated by instances of the IRQ_CONNECT() macro. | 
|  |  | 
|  | This script outputs three binary tables: | 
|  |  | 
|  | 1. The interrupt descriptor table itself. | 
|  | 2. A bitfield indicating which vectors in the IDT are free for | 
|  | installation of dynamic interrupts at runtime. | 
|  | 3. An array which maps configured IRQ lines to their associated | 
|  | vector entries in the IDT, used to program the APIC at runtime. | 
|  | """ | 
|  |  | 
|  | import argparse | 
|  | import sys | 
|  | import struct | 
|  | import os | 
|  | import elftools | 
|  | from packaging import version | 
|  | from elftools.elf.elffile import ELFFile | 
|  | from elftools.elf.sections import SymbolTableSection | 
|  |  | 
|  | if version.parse(elftools.__version__) < version.parse('0.24'): | 
|  | sys.exit("pyelftools is out of date, need version 0.24 or later") | 
|  |  | 
|  | # 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.exit(os.path.basename(sys.argv[0]) + ": " + text) | 
|  |  | 
|  |  | 
|  | # 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, allow_abbrev=False) | 
|  |  | 
|  | 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 = num_chars*[0] | 
|  | for i, (handler, _, _) in enumerate(vectors): | 
|  | if handler not in (spur_code, 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() |