x86: add dynamic interrupt support

If dynamic interrupts are enabled, a set of trampoline stubs
are generated which transfer control to a common dynamic
interrupt handler function, which then looks up the proper
handler and parameter and then executes the interrupt.

Based on the prior x86 dynamic interrupt implementation which
was removed from the kernel some time ago, and adapted to
changes in the common interrupt handling code, build system,
and IDT generation tools.

An alternative approach could be to read the currently executing
vector out of the APIC, but this is a much slower operation.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
diff --git a/scripts/gen_idt.py b/scripts/gen_idt.py
index c7c3eb0..b70bbd5 100755
--- a/scripts/gen_idt.py
+++ b/scripts/gen_idt.py
@@ -235,6 +235,8 @@
                         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",
@@ -244,6 +246,27 @@
         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()
 
@@ -261,6 +284,9 @@
 
     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__":