forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgen_idt.py
executable file
·293 lines (213 loc) · 8.43 KB
/
gen_idt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#!/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()