#!/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 consule 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 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 = "> 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 = "= 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 = "