Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (c) 2017 Intel Corporation |
| 4 | # |
| 5 | # SPDX-License-Identifier: Apache-2.0 |
| 6 | |
| 7 | import argparse |
| 8 | import sys |
| 9 | import struct |
| 10 | import os |
Andrew Boie | 3aecba1 | 2017-07-25 09:44:30 -0700 | [diff] [blame] | 11 | import elftools |
| 12 | from distutils.version import LooseVersion |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 13 | from elftools.elf.elffile import ELFFile |
| 14 | from elftools.elf.sections import SymbolTableSection |
| 15 | |
Andrew Boie | 3aecba1 | 2017-07-25 09:44:30 -0700 | [diff] [blame] | 16 | if LooseVersion(elftools.__version__) < LooseVersion('0.24'): |
| 17 | sys.stderr.write("pyelftools is out of date, need version 0.24 or later\n") |
| 18 | sys.exit(1) |
| 19 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 20 | # This will never change, first selector in the GDT after the null selector |
| 21 | KERNEL_CODE_SEG = 0x08 |
| 22 | |
| 23 | # These exception vectors push an error code onto the stack. |
| 24 | ERR_CODE_VECTORS = [8, 10, 11, 12, 13, 14, 17] |
| 25 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 26 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 27 | def debug(text): |
| 28 | if not args.verbose: |
| 29 | return |
| 30 | sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
| 31 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 32 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 33 | def error(text): |
| 34 | sys.stderr.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
| 35 | sys.exit(1) |
| 36 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 37 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 38 | # See Section 6.11 of the Intel Architecture Software Developer's Manual |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 39 | gate_desc_format = "<HHBBH" |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 40 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 41 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 42 | def create_irq_gate(handler, dpl): |
| 43 | present = 1 |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 44 | gate_type = 0xE # 32-bit interrupt gate |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 45 | type_attr = gate_type | (dpl << 5) | (present << 7) |
| 46 | |
| 47 | offset_hi = handler >> 16 |
| 48 | offset_lo = handler & 0xFFFF |
| 49 | |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 50 | data = struct.pack(gate_desc_format, offset_lo, KERNEL_CODE_SEG, 0, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 51 | type_attr, offset_hi) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 52 | return data |
| 53 | |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 54 | |
| 55 | def create_task_gate(tss, dpl): |
| 56 | present = 1 |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 57 | gate_type = 0x5 # 32-bit task gate |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 58 | type_attr = gate_type | (dpl << 5) | (present << 7) |
| 59 | |
| 60 | data = struct.pack(gate_desc_format, 0, tss, 0, type_attr, 0) |
| 61 | return data |
| 62 | |
| 63 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 64 | def create_idt_binary(idt_config, filename): |
| 65 | with open(filename, "wb") as fp: |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 66 | for handler, tss, dpl in idt_config: |
| 67 | if handler and tss: |
| 68 | error("entry specifies both handler function and tss") |
| 69 | |
| 70 | if not handler and not tss: |
| 71 | error("entry does not specify either handler or tss") |
| 72 | |
| 73 | if handler: |
| 74 | data = create_irq_gate(handler, dpl) |
| 75 | else: |
| 76 | data = create_task_gate(tss, dpl) |
| 77 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 78 | fp.write(data) |
| 79 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 80 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 81 | map_fmt = "<B" |
| 82 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 83 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 84 | def create_irq_vec_map_binary(irq_vec_map, filename): |
| 85 | with open(filename, "wb") as fp: |
| 86 | for i in irq_vec_map: |
| 87 | fp.write(struct.pack(map_fmt, i)) |
| 88 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 89 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 90 | def priority_range(prio): |
| 91 | # Priority levels are represented as groups of 16 vectors within the IDT |
| 92 | base = 32 + (prio * 16) |
| 93 | return range(base, base + 16) |
| 94 | |
| 95 | |
| 96 | def update_irq_vec_map(irq_vec_map, irq, vector, max_irq): |
| 97 | # No IRQ associated; exception or software interrupt |
| 98 | if irq == -1: |
| 99 | return |
| 100 | |
| 101 | if irq >= max_irq: |
| 102 | error("irq %d specified, but CONFIG_MAX_IRQ_LINES is %d" % |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 103 | (irq, max_irq)) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 104 | |
| 105 | # This table will never have values less than 32 since those are for |
| 106 | # exceptions; 0 means unconfigured |
| 107 | if irq_vec_map[irq] != 0: |
| 108 | error("multiple vector assignments for interrupt line %d", irq) |
| 109 | |
| 110 | debug("assign IRQ %d to vector %d" % (irq, vector)) |
| 111 | irq_vec_map[irq] = vector |
| 112 | |
| 113 | |
| 114 | def setup_idt(spur_code, spur_nocode, intlist, max_vec, max_irq): |
| 115 | irq_vec_map = [0 for i in range(max_irq)] |
| 116 | vectors = [None for i in range(max_vec)] |
| 117 | |
| 118 | # Pass 1: sanity check and set up hard-coded interrupt vectors |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 119 | for handler, irq, prio, vec, dpl, tss in intlist: |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 120 | if vec == -1: |
| 121 | if prio == -1: |
| 122 | error("entry does not specify vector or priority level") |
| 123 | continue |
| 124 | |
| 125 | if vec >= max_vec: |
| 126 | error("Vector %d specified, but size of IDT is only %d vectors" % |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 127 | (vec, max_vec)) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 128 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 129 | if vectors[vec] is not None: |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 130 | error("Multiple assignments for vector %d" % vec) |
| 131 | |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 132 | vectors[vec] = (handler, tss, dpl) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 133 | update_irq_vec_map(irq_vec_map, irq, vec, max_irq) |
| 134 | |
| 135 | # Pass 2: set up priority-based interrupt vectors |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 136 | for handler, irq, prio, vec, dpl, tss in intlist: |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 137 | if vec != -1: |
| 138 | continue |
| 139 | |
| 140 | for vi in priority_range(prio): |
| 141 | if vi >= max_vec: |
| 142 | break |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 143 | if vectors[vi] is None: |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 144 | vec = vi |
| 145 | break |
| 146 | |
| 147 | if vec == -1: |
| 148 | error("can't find a free vector in priority level %d" % prio) |
| 149 | |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 150 | vectors[vec] = (handler, tss, dpl) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 151 | update_irq_vec_map(irq_vec_map, irq, vec, max_irq) |
| 152 | |
| 153 | # Pass 3: fill in unused vectors with spurious handler at dpl=0 |
| 154 | for i in range(max_vec): |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 155 | if vectors[i] is not None: |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 156 | continue |
| 157 | |
| 158 | if i in ERR_CODE_VECTORS: |
| 159 | handler = spur_code |
| 160 | else: |
| 161 | handler = spur_nocode |
| 162 | |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 163 | vectors[i] = (handler, 0, 0) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 164 | |
| 165 | return vectors, irq_vec_map |
| 166 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 167 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 168 | def get_symbols(obj): |
| 169 | for section in obj.iter_sections(): |
| 170 | if isinstance(section, SymbolTableSection): |
| 171 | return {sym.name: sym.entry.st_value |
| 172 | for sym in section.iter_symbols()} |
| 173 | |
| 174 | raise LookupError("Could not find symbol table") |
| 175 | |
| 176 | # struct genidt_header_s { |
| 177 | # uint32_t spurious_addr; |
| 178 | # uint32_t spurious_no_error_addr; |
| 179 | # int32_t num_entries; |
| 180 | # }; |
| 181 | |
| 182 | |
Sebastian Bøe | a1e806b | 2018-06-25 16:46:13 +0200 | [diff] [blame] | 183 | intlist_header_fmt = "<II" |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 184 | |
| 185 | # struct genidt_entry_s { |
| 186 | # uint32_t isr; |
| 187 | # int32_t irq; |
| 188 | # int32_t priority; |
| 189 | # int32_t vector_id; |
| 190 | # int32_t dpl; |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 191 | # int32_t tss; |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 192 | # }; |
| 193 | |
Andrew Boie | 8a102e4 | 2017-07-14 13:29:19 -0700 | [diff] [blame] | 194 | intlist_entry_fmt = "<Iiiiii" |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 195 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 196 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 197 | def get_intlist(elf): |
| 198 | intdata = elf.get_section_by_name("intList").data() |
| 199 | |
| 200 | header_sz = struct.calcsize(intlist_header_fmt) |
| 201 | header = struct.unpack_from(intlist_header_fmt, intdata, 0) |
| 202 | intdata = intdata[header_sz:] |
| 203 | |
| 204 | spurious_code = header[0] |
| 205 | spurious_nocode = header[1] |
| 206 | |
| 207 | debug("spurious handler (code) : %s" % hex(header[0])) |
| 208 | debug("spurious handler (no code) : %s" % hex(header[1])) |
| 209 | |
| 210 | intlist = [i for i in |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 211 | struct.iter_unpack(intlist_entry_fmt, intdata)] |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 212 | |
| 213 | debug("Configured interrupt routing") |
| 214 | debug("handler irq pri vec dpl") |
| 215 | debug("--------------------------") |
| 216 | |
| 217 | for irq in intlist: |
| 218 | debug("{0:<10} {1:<3} {2:<3} {3:<3} {4:<2}".format( |
| 219 | hex(irq[0]), |
| 220 | "-" if irq[1] == -1 else irq[1], |
| 221 | "-" if irq[2] == -1 else irq[2], |
| 222 | "-" if irq[3] == -1 else irq[3], |
| 223 | irq[4])) |
| 224 | |
| 225 | return (spurious_code, spurious_nocode, intlist) |
| 226 | |
| 227 | |
| 228 | def parse_args(): |
| 229 | global args |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 230 | parser = argparse.ArgumentParser( |
| 231 | description=__doc__, |
| 232 | formatter_class=argparse.RawDescriptionHelpFormatter) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 233 | |
| 234 | parser.add_argument("-m", "--vector-map", required=True, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 235 | help="Output file mapping IRQ lines to IDT vectors") |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 236 | parser.add_argument("-o", "--output-idt", required=True, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 237 | help="Output file containing IDT binary") |
Andrew Boie | 7bac15f | 2018-10-30 16:55:38 -0700 | [diff] [blame] | 238 | parser.add_argument("-a", "--output-vectors-alloc", required=False, |
| 239 | help="Output file indicating allocated vectors") |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 240 | parser.add_argument("-k", "--kernel", required=True, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 241 | help="Zephyr kernel image") |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 242 | parser.add_argument("-v", "--verbose", action="store_true", |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 243 | help="Print extra debugging information") |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 244 | args = parser.parse_args() |
Sebastian Bøe | 4971d2a | 2017-12-28 17:34:50 +0100 | [diff] [blame] | 245 | if "VERBOSE" in os.environ: |
| 246 | args.verbose = 1 |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 247 | |
| 248 | |
Andrew Boie | 7bac15f | 2018-10-30 16:55:38 -0700 | [diff] [blame] | 249 | def create_irq_vectors_allocated(vectors, spur_code, spur_nocode, filename): |
| 250 | # Construct a bitfield over all the IDT vectors, where if bit n is 1, |
| 251 | # that vector is free. those vectors have either of the two spurious |
| 252 | # interrupt handlers installed, they are free for runtime installation |
| 253 | # of interrupts |
| 254 | num_chars = (len(vectors) + 7) // 8 |
| 255 | vbits = [0 for i in range(num_chars)] |
| 256 | for i in range(len(vectors)): |
| 257 | handler, _, _ = vectors[i] |
| 258 | if handler != spur_code and handler != spur_nocode: |
| 259 | continue |
| 260 | |
| 261 | vbit_index = i // 8 |
| 262 | vbit_val = 1 << (i % 8) |
| 263 | vbits[vbit_index] = vbits[vbit_index] | vbit_val |
| 264 | |
| 265 | with open(filename, "wb") as fp: |
| 266 | for char in vbits: |
| 267 | fp.write(struct.pack("<B", char)) |
| 268 | |
| 269 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 270 | def main(): |
| 271 | parse_args() |
| 272 | |
| 273 | with open(args.kernel, "rb") as fp: |
| 274 | kernel = ELFFile(fp) |
| 275 | |
| 276 | syms = get_symbols(kernel) |
| 277 | spur_code, spur_nocode, intlist = get_intlist(kernel) |
| 278 | |
| 279 | max_irq = syms["CONFIG_MAX_IRQ_LINES"] |
| 280 | max_vec = syms["CONFIG_IDT_NUM_VECTORS"] |
| 281 | |
| 282 | vectors, irq_vec_map = setup_idt(spur_code, spur_nocode, intlist, max_vec, |
| 283 | max_irq) |
| 284 | |
| 285 | create_idt_binary(vectors, args.output_idt) |
| 286 | create_irq_vec_map_binary(irq_vec_map, args.vector_map) |
Andrew Boie | 7bac15f | 2018-10-30 16:55:38 -0700 | [diff] [blame] | 287 | if args.output_vectors_alloc: |
| 288 | create_irq_vectors_allocated(vectors, spur_code, spur_nocode, |
| 289 | args.output_vectors_alloc) |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 290 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 291 | |
Andrew Boie | ddf9f4b | 2017-07-14 12:48:35 -0700 | [diff] [blame] | 292 | if __name__ == "__main__": |
| 293 | main() |