x86: allow IDT vectors to be task gates

This has one use-case: configuring the double-fault #DF
exception handler to do an IA task switch to a special
IA task with a known good stack, such that we can dump
diagnostic information and then panic.

Will be used for stack overflow detection in kernel mode,
as otherwise the CPU will triple-fault and reset.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
diff --git a/scripts/gen_idt.py b/scripts/gen_idt.py
index df6bc9a..3583031 100755
--- a/scripts/gen_idt.py
+++ b/scripts/gen_idt.py
@@ -27,7 +27,7 @@
     sys.exit(1)
 
 # See Section 6.11 of the Intel Architecture Software Developer's Manual
-irq_gate_desc_format = "<HHBBH"
+gate_desc_format = "<HHBBH"
 
 def create_irq_gate(handler, dpl):
     present = 1
@@ -37,14 +37,34 @@
     offset_hi = handler >> 16
     offset_lo = handler & 0xFFFF
 
-    data = struct.pack(irq_gate_desc_format, offset_lo, KERNEL_CODE_SEG, 0,
+    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, dpl in idt_config:
-            data = create_irq_gate(handler, dpl)
+        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"
@@ -83,7 +103,7 @@
     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:
+    for handler, irq, prio, vec, dpl, tss in intlist:
         if vec == -1:
             if prio == -1:
                 error("entry does not specify vector or priority level")
@@ -96,11 +116,11 @@
         if vectors[vec] != None:
             error("Multiple assignments for vector %d" % vec)
 
-        vectors[vec] = (handler, dpl)
+        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 in intlist:
+    for handler, irq, prio, vec, dpl, tss in intlist:
         if vec != -1:
             continue
 
@@ -114,7 +134,7 @@
         if vec == -1:
             error("can't find a free vector in priority level %d" % prio)
 
-        vectors[vec] = (handler, dpl)
+        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
@@ -127,7 +147,7 @@
         else:
             handler = spur_nocode
 
-        vectors[i] = (handler, 0)
+        vectors[i] = (handler, 0, 0)
 
     return vectors, irq_vec_map
 
@@ -154,9 +174,10 @@
 #	int32_t priority;
 #	int32_t vector_id;
 #	int32_t dpl;
+#	int32_t tss;
 # };
 
-intlist_entry_fmt = "<Iiiii"
+intlist_entry_fmt = "<Iiiiii"
 
 def get_intlist(elf):
     intdata = elf.get_section_by_name("intList").data()