Skip to content

Commit

Permalink
x86: convert gen_idt to Python
Browse files Browse the repository at this point in the history
This is one less host tool we have to compile for every build,
and makes the build tools more portable across host OSes.
The code is also much simpler to maintain.

Issue: ZEP-2063
Signed-off-by: Andrew Boie <[email protected]>
  • Loading branch information
Andrew Boie authored and Anas Nashif committed Jul 25, 2017
1 parent dff21de commit ddf9f4b
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 665 deletions.
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1276,10 +1276,8 @@ $(help-board-dirs): help-%:
host-tools:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)$(MAKE) $(build)=scripts/kconfig standalone
$(Q)$(MAKE) $(build)=scripts/gen_idt
@mkdir -p ${ZEPHYR_BASE}/bin
@cp scripts/basic/fixdep scripts/gen_idt/gen_idt scripts/kconfig/conf \
${ZEPHYR_BASE}/bin
@cp scripts/basic/fixdep scripts/kconfig/conf ${ZEPHYR_BASE}/bin


# Documentation targets
Expand Down
21 changes: 7 additions & 14 deletions arch/x86/Makefile.idt
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
ifeq ($(KBUILD_VERBOSE),1)
GENIDT_EXTRA_ARGS := -d
GENIDT_EXTRA_ARGS := --verbose
else
GENIDT_EXTRA_ARGS :=
endif

ifeq ($(PREBUILT_HOST_TOOLS),)
GENIDT := scripts/gen_idt/gen_idt
else
GENIDT := $(PREBUILT_HOST_TOOLS)/gen_idt
endif
GENIDT := $(srctree)/scripts/gen_idt.py

OUTPUT_FORMAT ?= elf32-i386
OUTPUT_ARCH ?= i386

quiet_cmd_gen_idt = SIDT $@
cmd_gen_idt = \
( \
$(OBJCOPY) -I $(OUTPUT_FORMAT) -O binary -j intList $< isrList.bin && \
$(GENIDT) -i isrList.bin -n $(CONFIG_IDT_NUM_VECTORS) -o staticIdt.bin \
-m irq_int_vector_map.bin \
-l $(CONFIG_MAX_IRQ_LINES) $(GENIDT_EXTRA_ARGS) && \
$(GENIDT) --kernel $(PREBUILT_KERNEL) \
--output-idt staticIdt.bin \
--vector-map irq_int_vector_map.bin \
$(GENIDT_EXTRA_ARGS) && \
$(OBJCOPY) -I binary -B $(OUTPUT_ARCH) -O $(OUTPUT_FORMAT) \
--rename-section .data=staticIdt staticIdt.bin staticIdt.o && \
$(OBJCOPY) -I binary -B $(OUTPUT_ARCH) -O $(OUTPUT_FORMAT) \
--rename-section .data=irq_int_vector_map irq_int_vector_map.bin \
irq_int_vector_map.o && \
rm staticIdt.bin irq_int_vector_map.bin isrList.bin \
rm staticIdt.bin irq_int_vector_map.bin \
)

$(GENIDT):
$(Q)$(MAKE) $(build)=scripts/gen_idt

staticIdt.o: $(PREBUILT_KERNEL) $(GENIDT)
$(call cmd,gen_idt)

Expand Down
228 changes: 228 additions & 0 deletions scripts/gen_idt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#!/usr/bin/env python3
#
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0

import argparse
import sys
import struct
import os
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection

# 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
irq_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(irq_gate_desc_format, offset_lo, KERNEL_CODE_SEG, 0,
type_attr, offset_hi)
return data

def create_idt_binary(idt_config, filename):
with open(filename, "wb") as fp:
for handler, dpl in idt_config:
data = create_irq_gate(handler, 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 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] != None:
error("Multiple assignments for vector %d" % vec)

vectors[vec] = (handler, 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 in intlist:
if vec != -1:
continue

for vi in priority_range(prio):
if vi >= max_vec:
break
if vectors[vi] == None:
vec = vi
break

if vec == -1:
error("can't find a free vector in priority level %d" % prio)

vectors[vec] = (handler, 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] != None:
continue

if i in ERR_CODE_VECTORS:
handler = spur_code
else:
handler = spur_nocode

vectors[i] = (handler, 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 = "<IIi"

# struct genidt_entry_s {
# uint32_t isr;
# int32_t irq;
# int32_t priority;
# int32_t vector_id;
# int32_t dpl;
# };

intlist_entry_fmt = "<Iiiii"

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("-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()


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 __name__ == "__main__":
main()

8 changes: 0 additions & 8 deletions scripts/gen_idt/Makefile

This file was deleted.

Loading

0 comments on commit ddf9f4b

Please sign in to comment.