Interrupt Descriptor Table: Difference between revisions

I felt this article could use somewhat of a rewrite as I found its formatting confusing and information kind of garbled (particularly information pertaining to 64-bit mode). Feel free to change formatting or wholly revert the edit if not welcome.
[unchecked revision][unchecked revision]
(→‎Structure AMD64: clarify GateType in long mode (see Intel manual 6.14.1))
(I felt this article could use somewhat of a rewrite as I found its formatting confusing and information kind of garbled (particularly information pertaining to 64-bit mode). Feel free to change formatting or wholly revert the edit if not welcome.)
Line 1:
The '''Interrupt Descriptor Table''' ('''IDT''') is a binary data structure specific to the [[IA32_Architecture_Family|IA-32]] and [[X86-64|x86-64]] architecturearchitectures. It is the [[Protected modeMode]] and Long Mode counterpart to the [[Real Mode]] Interrupt Vector Table ([[IVT]]) telling the CPU where the [[Interrupt Service Routines]] (ISR) are located (one per interrupt vector). It is similar to the [[Global Descriptor Table]] in structure.
 
The IDT entries are called gates. It can contain Interrupt Gates, Task Gates and Trap Gates.
Line 5:
Before you implement the IDT, make sure you have a working GDT.
 
== Location and SizeIDTR ==
 
LocationThe location of IDTthe (address and size)'''IDT''' is kept in the '''IDTR''' register. ofThis is loaded using the CPU'''LGDT''' assembly instruction, whichwhose canargument beis loaded/storeda usingpointer LIDT,to SIDTan instructions.'''IDT Descriptor''' structure:
{| {{wikitable}}
|+<b>IDTR</b>
|-
|Name || Bit || Description
|-
|Limit || 0..15 || Defines the length of the IDT in bytes - 1 (minimum value is 100h, a value of 1000h means 200h interrupts).
|-
|Base || 16..47 || This 32 bits are the linear address where the IDT starts (INT 0)
|}
This is similar to the [[GDT]], except:
* The first entry (at zero offset) is used in the IDT.
* There are 256 interrupts (0..255), so IDT should have 256 entries, each entry corresponding to a specific interrupt.
* It can contain more or less than 256 entries. More entries are ignored. When an interrupt or exception is invoked whose entry is not present, a [[Exceptions#General Protection Fault|GPF]] is raised that tells the number of the missing IDT entry, and even whether it was hardware or software interrupt. There should therefore be at least enough entries so a GPF can be caught.
 
== Structure IA-32 ==
 
The table contains 8-byte Gate entries. Each entry has a complex structure:
 
<source lang="c">
struct IDTDescr {
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t zero; // unused, set to 0
uint8_t type_attr; // type and attributes, see below
uint16_t offset_2; // offset bits 16..31
};
</source>
 
The <tt>offset</tt> is a 32 bit value, split in two parts. It represents the address of the entry point of the ISR.
 
The <tt>selector</tt> is a 16 bit value and must point to a valid descriptor in your [[GDT]].
 
The <tt>type_attr</tt> is specified here:
<pre>
IDT DESCRIPTOR (IDTR):
7 0
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+---+---+---+---+---+---+---+---+
┃79 (64-bit Mode) / 48 (32-bit Mode) 16│15 0┃
| P | DPL | S | GateType |
┠───────────────────────────────────────────────────────────────┼───────────────────────────────┨
+---+---+---+---+---+---+---+---+
┃Offset │Size ┃
┃63 (64-bit Mode) / 31 (32-bit Mode) 0│15 0┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
</pre>
The bit fields mean:
{| {{wikitable}}
|+<b>IDT entry, Interrupt Gates</b>
! Name
! Bit
! Full Name
! Description
|-
! Offset
| 48..63 || Offset 16..31 || Higher part of the offset.
|-
! P
| 47 || Present || Set to <b>0</b> for unused interrupts.
|-
! DPL
| 45,46 || Descriptor Privilege Level || Gate call protection. Specifies which privilege Level the calling [[Descriptor]] minimum should have. So hardware and CPU interrupts can be protected from being called out of userspace.
|-
! S
| 44 || Storage Segment || Set to <b>0</b> for interrupt and trap gates (see below).
|-
! Type
| 40..43 || Gate Type 0..3 || Possible IDT gate types :
{| {{wikitable}}
| 0b0101 || 0x5 || 5 || 80386 32 bit [[#I386 Task Gate|task gate]]
|-
| 0b0110 || 0x6 || 6 || 80286 16-bit [[#I386 Interrupt Gate|interrupt gate]]
|-
| 0b0111 || 0x7 || 7 || 80286 16-bit [[#I386 Trap Gate|trap gate]]
|-
| 0b1110 || 0xE || 14 || 80386 32-bit [[#I386 Interrupt Gate|interrupt gate]]
|-
| 0b1111 || 0xF || 15 || 80386 32-bit [[#I386 Trap Gate|trap gate]]
|}
|-
! 0
| 32..39 || Unused 0..7 || Have to be <b>0</b>.
|-
! Selector
| 16..31 || Selector 0..15 || [[Selector]] of the interrupt function (to make sense - the kernel's selector). The selector's descriptor's DPL field has to be <b>0</b> so the <b>iret</b> instruction won't throw a #GP exeption when executed.
|-
! Offset
| 0..15 || Offset 0..15 || Lower part of the interrupt function's offset address (also known as pointer).
|}
 
* '''Size''': One less than the size of the '''IDT''' in bytes.
== Structure AMD64 ==
* '''Offset''': The linear address of the '''Interrupt Descriptor Table''' (not the physical address, paging applies).
 
Note that the amount of data loaded by '''LGDT''' differs in 32-bit and 64-bit modes, '''Offset''' is 4 bytes long in 32-bit mode and 8 bytes long in 64-bit mode.
For AMD64 the entries are 16-bytes and looks like this:
 
This is similar to the '''[[GDT]]''', except:
<source lang="c">
* The first entry (at zero offset) is used in the '''IDT'''.
struct IDTDescr {
* There are 256 interrupt vectors (0..255), so the '''IDT''' should have 256 entries, each entry corresponding to a specific interrupt vector.
uint16_t offset_1; // offset bits 0..15
* Although the '''IDT''' can contain more than 256 entries, they are ignored.
uint16_t selector; // a code segment selector in GDT or LDT
* Although the '''IDT''' can contain less than 256 entries, any entries that are not present (due to this or other reasons) will generate a '''[[Exceptions#General Protection Fault|General Protection Fault]]''' when an attempt to access them is made. Ideally the '''IDT''' should contain enough entries so that this fault (which is itself an interrupt vector) can be handled.
uint8_t ist; // bits 0..2 holds Interrupt Stack Table offset, rest of bits zero.
uint8_t type_attr; // type and attributes
uint16_t offset_2; // offset bits 16..31
uint32_t offset_3; // offset bits 32..63
uint32_t zero; // reserved
};
</source>
 
For more information, see '''Section 2.4.3: IDTR Interrupt Descriptor Table Register''' and '''Figure 2-6: Memory Management Registers''' of the Intel Software Developer Manual, Volume 3-A.
The <tt>offset</tt> is a 64 bit value, split in three parts. It represents the address of the entry point of the ISR.
 
== Structure on IA-32 ==
The <tt>selector</tt> is a 16 bit value and must point to a valid descriptor in your [[GDT]].
 
=== Table ===
The <tt>ist</tt> is specified here:
<pre>
7 0
+---+---+---+---+---+---+---+---+
| Zero | IST |
+---+---+---+---+---+---+---+---+
</pre>
 
On 32-bit processors, the entries in the '''IDT''' are 8 bytes long and form a table like this:
The low three bits act as an offset into the Interrupt Stack Table, which is stored in the [[TSS]]. If the bits are all set to zero, the IST mechanism is not used.
 
The <tt>type_attr</tt> is specified here:
<pre>
Table: 7 0Address:
┏━━━━━━━━━━━┓
+---+---+---+---+---+---+---+---+
┃ Entry 0 ┃ IDTR Offset + 0
| P | DPL | Z | GateType |
┠───────────┨
+---+---+---+---+---+---+---+---+
┃ Entry 1 ┃ IDTR Offset + 8
┠───────────┨
┃ Entry 2 ┃ IDTR Offset + 16
┠───────────┨
┃ ... ┃ ...
┠───────────┨
┃ Entry 255 ┃ IDTR Offset + 2040
┗━━━━━━━━━━━┛
</pre>
 
The corresponding entry for a given '''Interrupt Vector''' is pointed to in memory by scaling the vector by 8 and adding it to the value in the '''Offset''' field of the '''IDTR'''.
In long mode only 64-bit interrupt and trap gates are valid. The 32-bit interrupt and trap gates (<tt>0xe</tt> and <tt>0xf</tt>) are redefined as 64-bit interrupt and trap gates.
 
=== Gate TypesEntries ===
 
Each entry has a complex structure:
There are basically two kinds of code execution interruption: when it is caused by a faulty instruction, or when it caused by an unrelated event. In the first case we must save the address of the CURRENT faulting instruction so that we can retry. These are called "traps". The second case could be caused by an IRQ, or by using an "int" instruction, and here we must return to the NEXT instruction. Another difference is, that with traps new interrupts might occur. However when the CPU is serving an IRQ, further interrupts must be masked. How a certain interrupt is served depends on which kind of gate you put in the IDT entry.
 
<pre>
=== I386 Interrupt Gate ===
32-BIT INTERRUPT DESCRIPTOR
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━┯━━━━━┯━━┯━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━┓
┃63 48│47│46 45│44│43 40│39 32┃
┠───────────────────────────────────────────────┼──┼─────┼──┼───────────┼───────────────────────┨
┃Offset │P │DPL │0 │Gate Type │Reserved ┃
┃31 16│ │1 0│ │3 0│ 0┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━┷━━━━━┷━━┷━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃31 16│15 0┃
┠───────────────────────────────────────────────┼───────────────────────────────────────────────┨
┃Segment Selector │Offset ┃
┃15 0│15 0┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
</pre>
 
* '''Offset:''' A 32-bit value, split in two parts. It represents the address of the entry point of the '''[[Interrupt Service Routines|Interrupt Service Routine]]'''.
The Interrupt Gate is used to specify an [[Interrupt Service Routines|interrupt service routine]]. When you do <tt>[[INT]] 50</tt> in assembly, running in protected mode, the CPU looks up the 50th entry (located at 50 * 8) in the IDT. Then the Interrupt Gates selector and offset value is loaded. The selector and offset is used to call the interrupt service routine. When the <tt>[[IRET]]</tt> instruction is read, it returns. If running in 32 bit mode and the specified selector is a 16 bit selector, then the CPU will go in 16 bit protected mode after calling the interrupt service routine. To return you need to do <tt>O32 IRET</tt>, else the CPU doesn't know that it should do a 32 bit return (reading 32 bit offset of the [[stack]] instead of 16 bit).
* '''Selector:''' A 16-bit value with multiple fields which must point to a valid code segment in your '''[[GDT]]'''. For more information, see '''Section 3.4.2: Segment Selectors''' and '''Figure 3-6: Segment Selector''' of the Intel Software Developer Manual, Volume 3-A.
* '''Gate Type:''' A 4-bit value which defines the type of gate this '''Interrupt Descriptor''' represents. There are five valid type values:
** '''0b0101''' or '''0x5''': Task Gate, note that in this case, the '''Offset''' value is unused and should be set to zero.
** '''0b0110''' or '''0x6''': 16-bit Interrupt Gate
** '''0b0111''' or '''0x7''': 16-bit Trap Gate
** '''0b1110''' or '''0xE''': 32-bit Interrupt Gate
** '''0b1111''' or '''0xF''': 32-bit Trap Gate
* '''DPL:''' A 2-bit value which defines the '''[[Security#Rings|CPU Privilege Levels]]''' which are allowed to access this interrupt via the '''INT''' instruction. Hardware interrupts ignore this mechanism.
* '''P:''' Present bit. Must be set ('''1''') for the descriptor to be valid.
 
For more information, see '''Section 6.11: IDT Descriptors''' and '''Figure 6-2: IDT Gate Descriptors''' of the Intel Software Developer Manual, Volume 3-A.
{| {{wikitable}}
! [[Descriptors#type_attr|type_attr]]
! Type
|-
| 0b1110=0xE
| 32-bit interrupt gate
|-
| 0b0110=0x6
| 16-bit interrupt gate
|}
 
=== Example Code ===
Here are some pre-cooked type_attr values people are likely to use (assuming DPL=0):
* 32-bit Interrupt gate: 0x8E ( P=1, DPL=00b, S=0, type=1110b => type_attr=1000_1110b=0x8E)
 
C Struct:
=== I386 Trap Gate ===
 
<source lang="c">
When an exception occurs, that should correspond to a Trap Gate, and the CPU places the return info on the stack (EFLAGS, CS, EIP), so the interrupt service routine can resume the interrupted instruction when it calls IRET.
struct InterruptDescriptor32 {
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t zero; // unused, set to 0
uint8_t type_attributes; // gate type, dpl, and p fields
uint16_t offset_2; // offset bits 16..31
};
</source>
 
Example ''type_attributes'' values that people are likely to use (assuming DPL is 0):
Then, execution is transferred to the given selector:offset from the gate descriptor.
* 32-bit '''Interrupt Gate: 0x8E''' (p=1, dpl=0b00, type=0b1110 => type_attributes=0b1000_1110='''0x8E''')
* 32-bit '''Trap Gate: 0x8F''' (p=1, dpl=0b00, type=0b1111 => type_attributes=1000_1111b='''0x8F''')
* '''Task Gate: 0x85''' (p=1, dpl=0b00, type=0b0101 => type_attributes=0b1000_0101='''0x85''')
 
== Structure on x86-64 ==
For some exceptions, an error code is also pushed on the stack, which must be POPped before doing IRET.
 
=== Table ===
 
On 64-bit processors, the entries in the '''IDT''' are 16 bytes long and form a table like this:
Trap and Interrupt gates are similar, and their descriptors are structurally the same, they differ only in the "type" field. The difference is that for interrupt gates, interrupts are automatically disabled upon entry and reenabled upon IRET which restores the saved EFLAGS.
 
<pre>
Choosing type_attr values: (See [[Descriptors#type_attr]])
Table: Address:
{| {{wikitable}}
┏━━━━━━━━━━━┓
! [[Descriptors#type_attr|type_attr]]
┃ Entry 0 ┃ IDTR Offset + 0
! Type
┠───────────┨
|-
┃ Entry 1 ┃ IDTR Offset + 16
| 0b1111=0xf
┠───────────┨
| 32-bit trap gate
┃ Entry 2 ┃ IDTR Offset + 32
|-
┠───────────┨
| 0b0111=0x7
┃ ... ┃ ...
| 16-bit trap gate
┠───────────┨
|}
┃ Entry 255 ┃ IDTR Offset + 4080
┗━━━━━━━━━━━┛
</pre>
 
The corresponding entry for a given '''Interrupt Vector''' is pointed to in memory by scaling the vector by 16 and adding it to the value in the '''Offset''' field of the '''IDTR'''.
 
=== Entries ===
Here are some pre-cooked type_attr values people are likely to use (assuming DPL=0):
 
Each entry has a complex structure:
* 32-bit Trap gate: 0x8F ( P=1, DPL=00b, S=0, type=1111b => type_attr=1000_1111b=0x8F)
 
<pre>
Thus, Trap and Interrupt gate descriptors hold the following data (other than type_attr):
64-bit INTERRUPT DESCRIPTOR:
* 16-bit selector of a code segment in GDT or LDT
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
* 32-bit offset into that segment - address of the handler, where execution will be transferred
┃127 96┃
┠───────────────────────────────────────────────────────────────────────────────────────────────┨
┃Reserved ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃95 64┃
┠───────────────────────────────────────────────────────────────────────────────────────────────┨
┃Offset ┃
┃63 32┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━┯━━━━━┯━━┯━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━┓
┃63 48│47│46 45│44│43 40│39 35│34 32┃
┠───────────────────────────────────────────────┼──┼─────┼──┼───────────┼──────────────┼────────┨
┃Offset │P │DPL │0 │Gate Type │Reserved │IST ┃
┃31 16│ │1 0│ │3 0│ │2 0┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━┷━━━━━┷━━┷━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━┛
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃31 16│15 0┃
┠───────────────────────────────────────────────┼───────────────────────────────────────────────┨
┃Selector │Offset ┃
┃15 0│15 0┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
</pre>
 
* '''Offset:''' A 64-bit value, split in three parts. It represents the address of the entry point of the '''[[Interrupt Service Routines|Interrupt Service Routine]]'''.
=== I386 Task Gate ===
* '''Selector:''' A 16-bit value with multiple fields which must point to a valid 64-bit code segment in your '''[[GDT]]'''. For more information, see '''Section 3.4.2: Segment Selectors''' and '''Figure 3-6: Segment Selector''' of the Intel Software Developer Manual, Volume 3-A.
* '''IST:''' A 3-bit value which is an offset into the '''Interrupt Stack Table''', which is stored in the '''[[Task State Segment]]'''. If the bits are all set to zero, the '''Interrupt Stack Table''' is not used.
* '''Gate Type:''' A 4-bit value which defines the type of gate this '''Interrupt Descriptor''' represents. In long mode there are two valid type values:
** '''0b1110''' or '''0xE''': 64-bit Interrupt Gate
** '''0b1111''' or '''0xF''': 64-bit Trap Gate
* '''DPL:''' A 2-bit value which defines the '''[[Security#Rings|CPU Privilege Levels]]''' which are allowed to access this interrupt via the '''INT''' instruction. Hardware interrupts ignore this mechanism.
* '''P:''' Present bit. Must be set ('''1''') for the descriptor to be valid.
 
In your '''[[Interrupt Service Routines|Interrupt Service Routines]]''', remember to return from the interrupt using the '''IRETQ''' instruction instead of '''IRET''', as assemblers will not translate that for you. Many 64-bit IDT related problems on the forum are caused by that missing 'Q'. Don't let this happen to you.
In the Task Gate descriptor the <tt>offset</tt> values are not used. Set them to 0.
 
For more information, see '''Section 6.14.1: 64-Bit Mode IDT''' and '''Figure 6-8: 64-Bit IDT Gate Descriptors''' of the Intel Software Developer Manual, Volume 3-A.
When an interrupt/exception occurs whose entry is a Task Gate, a task switch results.
 
=== Example Code ===
{{Quotation|"A task gate in the IDT references a TSS descriptor in the GDT. A switch to the handler task is handled in the same manner as an ordinary task switch. (..) The link back to the interrupted task is stored in the previous task link field of the handler task's TSS. If an exception caused an error code to be generated, this error code is copied to the stack of the new task."|Intel manual (vol.3 p.5-19)}}
 
C Struct:
{{Quotation|"*NOTE* Because IA-32 tasks are not re-entrant, an interrupt-handler task must disable interrupts between the time it completes handling the interrupt and the time it executes the IRET instruction. This action prevents another interrupt from occurring while the interrupt task's TSS is still marked busy, which would cause a general-protection (#GP) exception."|Intel manual}}
 
<source lang="c">
struct InterruptDescriptor64 {
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t ist; // bits 0..2 holds Interrupt Stack Table offset, rest of bits zero.
uint8_t type_attributes; // gate type, dpl, and p fields
uint16_t offset_2; // offset bits 16..31
uint32_t offset_3; // offset bits 32..63
uint32_t zero; // reserved
};
</source>
 
Example ''type_attributes'' values that people are likely to use (assuming DPL is 0):
Choosing type_attr values: (See
* 64-bit '''Interrupt Gate: 0x8E''' (p=1, dpl=0b00, type=0b1110 => type_attributes=0b1000_1110='''0x8E''')
{| {{wikitable}}
* 64-bit '''Trap Gate: 0x8F''' (p=1, dpl=0b00, type=0b1111 => type_attributes=1000_1111b='''0x8F''')
! [[Descriptors#type_attr|type_attr]]
! Type
|-
| 0b0101=0x5
| task gate
|}
 
== Gate Types ==
For DPL=0, type_attr=0x85=0b0101
 
There are basically two kinds of interrupts: ones that occur when code execution has encountered an '''[[Exceptions|Exception]]''' due to bad code, or ones that occur to handle events unrelated to currently executing code. In the first case it is pertinent to save the address of the ''currently'' executing instruction so that it can be retried, these are called '''Traps'''. In the second case it is pertinent to save the address of the ''next'' instruction so that execution can be resumed where it left off. These could be caused by an IRQ or other hardware event, or by use of the '''INT''' instruction. Another difference to note is that with '''Traps''', new interrupts might occur during the service routine, but when the CPU is serving an IRQ, further interrupts are masked until an '''End of Interrupt''' signal is sent. How a certain interrupt is served depends on which kind of gate you put in the IDT entry.
Thus, a TSS selector is the only custom piece of data you need for a Task Gate descriptor.
 
=== Interrupt Gate ===
Advantages over using trap/interrupt gates:
 
An '''Interrupt Gate''' is used to specify an '''[[Interrupt Service Routines|Interrupt Service Routine]]'''. For example, when the assembly instruction '''INT 50''' is performed while running in protected mode, the CPU looks up the 50th entry (located at 50 * 8) in the '''IDT'''. Then the Interrupt Gate's '''Selector''' and '''Offset''' values are loaded. The '''Selector''' and '''Offset''' are used to call the '''Interrupt Service Routine'''. When the '''IRET''' instruction is performed, the CPU returns from the interrupt. If the CPU was running in 32-bit mode and the specified selector is a 16-bit gate, then the CPU will go in 16-bit '''Protected Mode''' after calling the '''ISR'''. To return in this case, the '''O32 IRET''' instruction should be used, or else the CPU will not know that it should do a 32-bit return (reading 32-bit values off the [[stack]] instead of 16 bit).
* The entire context of the interrupted task is saved automatically (no need to worry about registers)
* The handler can be isolated from other tasks in a separate address space in LDT.
* ''"A new tss permits the handler to use a new privilege level 0 stack when handling the exception or interrupt. If an exception or interrupt occurs when the current privilege level 0 stack is corrupted, accessing the handler through a task gate can prevent a system crash by providing the handler with a new privilege level 0 stack"'' --Intel manual
 
=== Trap Gate ===
Disadvantage:
 
A '''Trap Gate''' should be used to handle '''[[Exceptions|Exceptions]]'''. When such an exception occurs, there can sometimes be an error code placed on the stack, which should be popped before returning from the interrupt.
* Saving the entire task context into TSS is slower than using a trap/interrupt gate (where the handler can save only what it needs).
** Is it that much faster if the handler does PUSHAD or pushes registers one by one?
** Does it make a difference, considering a non-dummy, non-trivial handler?
 
'''Trap Gates''' and '''Interrupt Gates''' are similar, and their descriptors are structurally the same, differing only in the '''Gate Type''' field. The difference is that for '''Interrupt Gates''', interrupts are automatically disabled upon entry and reenabled upon '''IRET''', whereas this does not occur for '''Trap Gates'''.
==Loading/Storing==
 
=== Task Gate ===
The IDT is loaded using the <tt>LIDT</tt> assembly instruction. It expects the address of a IDT description structure:
 
A '''Task Gate''' is a gate type specific to IA-32 that is used for hardware task switching. For a '''Task Gate''' the '''Selector''' value should refer to a position in the '''[[GDT]]''' which specifies a '''[[Task State Segment]]''' rather than a code segment, and the '''Offset''' value is unused and should be set to zero. Rather than jumping to a service routine, when the CPU processes this interrupt, it will perform a hardware task switch to the specified task. A pointer back to the task which was interrupted will be stored in the '''Task Link''' field in the '''TSS'''.
Byte:
+---------------+---------------+
0 | Size |
+---------------+---------------+
+---------------+---------------+---------------+---------------+
2 | Offset |
+---------------+---------------+---------------+---------------+
 
The <tt>offset</tt> is the virtual address of the table itself. The <tt>size</tt> is the size of the table itself subtracted by 1. This structure can be stored to memory again with the <tt>SIDT</tt> instruction.
 
To define the structure, one would write:
<source lang="asm">
idt_info:
dw idt_end - idt_start - 1
dd idt_start
</source>
 
To load the IDT, one would write:
<source lang="asm">
lidt [idt_info]
</source>
 
For the IDT itself, you can use a normal C struct array, fill it up statically or dynamically, or you could just "hardwire" the entire table into Assembly:
<source lang="asm">
idt_start:
irq0:
dw isr0
dw 0x0008
db 0x00
db 10101110b
dw 0x0000
irq1:
dw isr1
dw 0x0008
db 0x00
db 10101110b
dw 0x0000
irq2:
...
</source>
 
{{Quotation|"*NOTE* Because IA-32 tasks are not re-entrant, an interrupt-handler task must disable interrupts between the time it completes handling the interrupt and the time it executes the IRET instruction. This action prevents another interrupt from occurring while the interrupt task's TSS is still marked busy, which would cause a general-protection (#GP) exception."|Intel Software Developer Manual}}
==IDT in IA-32e Mode (64-bit IDT)==
When in long or compatibility mode (once the EFER.LME flag has been set) the IDT's structure changes slightly.
The IDTR structure's (used by <tt>LIDT</tt> and <tt>SIDT</tt>) <tt>base</tt> field changes to 64-bits to allow the IDT to reside anywhere in memory, and each entry in the IDT grows by 64-bits. The first 32-bit value is the high bits of the address, while the second is zero.
{| {{wikitable}}
|+<b>IDTR</b>
|-
|Offset || Size || Description
|-
|0 || 2 || Limit - Maximum addressable byte in table
|-
|2 || 8 || Offset - Linear (paged) base address of IDT
|}
{| {{wikitable}}
|+<b>IDT Descriptor</b>
|-
|Offset || Size || Description
|-
|0 || 2 || Offset low bits (0..15)
|-
|2 || 2 || Selector (Code segment selector)
|-
|4 || 1 || Zero
|-
|5 || 1 || Type and Attributes (same as before)
|-
|6 || 2 || Offset middle bits (16..31)
|-
|8 || 4 || Offset high bits (32..63)
|-
|12 || 4 || Zero
|}
 
This type of gate is not often used as hardware task switching is slow and has little to no optimization on modern processors. As well, it has been entirely removed on [[X86-64|x86-64]].
In your interrupt handler routines, '''remember to use IRETQ instead of IRET''', as nasm won't translate that for you. Many 64bit IDT related problems on the forum are caused by that missing 'Q'. Don't let this happen to you.
 
==See Also==
Line 307 ⟶ 223:
* [[IDT_problems|IDT problems]]
 
=== External referencesReferences ===
 
* [http://www.logix.cz/michal/doc/i386/chp09-00.htm Michal Ludvig's Intel 80386 Programmer's Reference Manual chapter 9]
53

edits