From 0b8d42a2ac753333d1331f851395e2ec0ca78454 Mon Sep 17 00:00:00 2001 From: nitroflap Date: Tue, 31 May 2022 17:46:05 +0300 Subject: [PATCH 1/4] feat: idt --- pages/idt.adoc | 171 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 pages/idt.adoc diff --git a/pages/idt.adoc b/pages/idt.adoc new file mode 100644 index 0000000..47f6857 --- /dev/null +++ b/pages/idt.adoc @@ -0,0 +1,171 @@ +--- +title: Interrupt descriptor table +tags: assembly, x86, x64, sysv, msvc +category: Interrupts +description: IDT spec and tutorial +--- +:source-language: c + +== Interrupt Descriptor Table + +// TODO: articles "GDT" and "Interrupts" are not written yet + +Interrupt Descriptor Table is a binary data structure specific to IA-32 (xref:x86[x86]) and x86_64 architectures. +It is used by the CPU to lookup an Interrupt Service Routine (ISR) when an xref:interrupts[Interrupt] is issued. +It is Protected Mode and Long Mode counterpart to Real Mode Interrupt Vector Table (IVT). + +IDT entries are called gates. There are three types of gates: + +- Interrupt Gates +- Task gates +- Trap gates + + +IMPORTANT: Before implementing IDT you must have a proper xref:gdt[GDT] working. + +=== IDTR +IDTR is a register containing IDT's address and limit. + +[cols="1,3"] +|=== +| Size +| One less than the size of the IDT in bytes. + +| Offset +| The linear address of the Interrupt Descriptor Table (not the physical address, paging applies). +|=== + +==== Layout + +[cols="1,1,1"] +|=== +| Architecture +| Size +| Offset + +| IA-32 +| 16 (0 -- 15) +| 31 (16 -- 48) + +| x86_64 +| 16 (0 -- 15) +| 63 (16 -- 79) +|=== + +==== Things to know +IDT can contain up to 256 (0..255) ISR vectors. +The first entry in the contrast to GDT is used. +And although IDT can contain more than 256 entries, they are ignored. + +=== IDT entry + + +==== IA-32 +On 32-bit processors, IDT entries are 8-bytes long, therefore to access the particular entry the index must be scaled by 8 and added to the IDTR Offset. + +Example of an IDT + +[cols="1,4"] +|=== +| Index +| Address +| 0 +| Offset + 0 + +| 1 +| Offset + 8 + +| 2 +| Offset + 16 + +| ... +| ... + +| 255 +| Offset + 2040 +|=== + + +===== Gate descriptor + +[cols="2,1"] +|=== +| Name +| Position (in bits) + +| Offset (0 -- 15) +| 0 -- 15 + +| Segment selector +| 16 -- 31 + +| Reserved +| 32 -- 39 + +| Gate Type +| 40 -- 43 + +| 0 +| 44 + +| DPL +| 45 -- 46 + +| Present +| 47 + +| Offset (16 -- 31) +| 48 -- 63 +|=== + +- *Offset*: 32-bit value containing the entry point of ISR. +- *Segment*: A GDT segment selector +- *Gate Type*: A 4-bit value containing the type of the gate + * `0x5` (`0b0101`): Task Gate. *Offset* is unused and should be set to 0. + * `0x6` (`0b0110`): 16-bit Interrupt Gate + * `0x7` (`0b0111`): 16-bit Trap Gate + * `0xE` (`0b1110`): 32-bit Interrupt Gate + * `0xF` (`0b1111`): 32-bit Trap Gate +- *DPL*: 2-bit value which specifies CPU Privilege Levels allowed to access this interrupt via `int` call. Hardware interrupts ignore this mechanism. +- *Present*: Present bit. Must be set to *1* for the descriptor to be valid. + +===== Example +[source,c] +struct InterruptDescriptor { + uint16_t offset_lo; + uint16_t selector; + uint8_t zero; + uint8_t type_attr; + uint8_t offset_hi; +}__attribute__((packed)); // __attribute__((packed)) is used to tell the compiler to not include the byte paddings, as they corrupt the binary structure of the entry. + +`type_attr` values values that people are likely to use (DPL=0): + +* 32-bit Interrupt Gate: `0x8E` (present=`1` dpl=`0` type=`0b1110` => `0b1000_1110` => `0x8E`) + +* 32-bit Trap Gate: `0x8F` (present=`1` dpl=`0` type=`0b1111` => `0b1000_1111` => `0x8F`) + +* Task Gate: `0x85` (present=`1` dpl=`0` type=`0b0101` => `0b1000_0101` => `0x85`) + + +==== x86_64 + + +=== Gate types + +==== Interrupt Gate +An Interrupt Gate is used to specify Interrupt Service Routines (ISR). + +When an assembly interrupt call `int ` is issued CPU looks up the specified ISR in the IDT, pushes necessary registers for `iret` to the stack and jumps to the entry point. + +If CPU was running in 32-bit mode, and the selector is a 16-bit gate, it will switch to 16-bit Protected Mode and jump to the entry point. To return back to 32-bit mode `o32 iret` instruction should be used, otherwise CPU would not know how to perform a 32-bit return (reading 32-bit instead of 16-bit values from the stack). + +==== Trap Gate +Trap gate is similar to Interrupt Gate. It is commonly used for syscalls and exceptions. The difference is that for a Trap Gate CPU does not disable hardware interrupts, while upon entering Interrupt Gate CPU automatically disables hardware interrupts and reenables them on return. + +==== Task Gate +Task Gate is a gate type specific to IA-32 that is used for hardware task switching. +In 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. + +When processing this interrupt, the CPU it will perform a hardware task switch to the specified task, rather than jumping to an ISR. A pointer back to the task which was interrupted will be stored in the Task Link field in TSS. \ No newline at end of file From bf8818bd9021a366817c9d5c3bd374315f0f08dc Mon Sep 17 00:00:00 2001 From: nitroflap Date: Wed, 1 Jun 2022 00:04:05 +0300 Subject: [PATCH 2/4] feat(idt): review changes and refactoring --- pages/idt.adoc | 119 +++++++++++++++++++------------------------------ 1 file changed, 45 insertions(+), 74 deletions(-) diff --git a/pages/idt.adoc b/pages/idt.adoc index 47f6857..3b9f079 100644 --- a/pages/idt.adoc +++ b/pages/idt.adoc @@ -11,28 +11,21 @@ description: IDT spec and tutorial // TODO: articles "GDT" and "Interrupts" are not written yet Interrupt Descriptor Table is a binary data structure specific to IA-32 (xref:x86[x86]) and x86_64 architectures. -It is used by the CPU to lookup an Interrupt Service Routine (ISR) when an xref:interrupts[Interrupt] is issued. -It is Protected Mode and Long Mode counterpart to Real Mode Interrupt Vector Table (IVT). +It is used by the CPU to lookup an interrupt service routines (ISR) when an xref:interrupts[interrupt] is issued. +It is protected mode and long mode counterpart to real mode interrupt vector table (IVT). -IDT entries are called gates. There are three types of gates: - -- Interrupt Gates -- Task gates -- Trap gates - - -IMPORTANT: Before implementing IDT you must have a proper xref:gdt[GDT] working. +IMPORTANT: IDT requires a working xref:gdt[GDT] === IDTR -IDTR is a register containing IDT's address and limit. +IDTR is a register containing the IDT's address and limit. [cols="1,3"] |=== | Size -| One less than the size of the IDT in bytes. +| One less than the size of the IDT in bytes | Offset -| The linear address of the Interrupt Descriptor Table (not the physical address, paging applies). +| The linear address of the IDT |=== ==== Layout @@ -53,119 +46,97 @@ IDTR is a register containing IDT's address and limit. |=== ==== Things to know -IDT can contain up to 256 (0..255) ISR vectors. -The first entry in the contrast to GDT is used. -And although IDT can contain more than 256 entries, they are ignored. +* IDT can contain up to 256 (0..255) ISR vectors (entries above this limit will be ignored) +* In contrast to GDT, the first entry of the IDT is valid and used. === IDT entry ==== IA-32 -On 32-bit processors, IDT entries are 8-bytes long, therefore to access the particular entry the index must be scaled by 8 and added to the IDTR Offset. - -Example of an IDT - -[cols="1,4"] -|=== -| Index -| Address -| 0 -| Offset + 0 - -| 1 -| Offset + 8 - -| 2 -| Offset + 16 - -| ... -| ... - -| 255 -| Offset + 2040 -|=== +On 32-bit processors, IDT entries are 8-bytes long, therefore to access the particular entry the raw index must be multiplied by 8 and added to the IDTR Offset. ===== Gate descriptor -[cols="2,1"] +[cols="2,1,3"] |=== | Name | Position (in bits) +| Description | Offset (0 -- 15) | 0 -- 15 +| ISR entry point address. In trap gates should be set to zero. | Segment selector | 16 -- 31 +| GDT segment selector. In trap gates used to specify a TSS selector. | Reserved | 32 -- 39 +| -- | Gate Type | 40 -- 43 +| Type of the gate. Described below in detail. | 0 | 44 +| -- | DPL | 45 -- 46 +| Descriptor privilege level. Ignored by hardware interrupts. | Present | 47 +| Indicates whether entry is valid | Offset (16 -- 31) | 48 -- 63 +| -- |=== -- *Offset*: 32-bit value containing the entry point of ISR. -- *Segment*: A GDT segment selector -- *Gate Type*: A 4-bit value containing the type of the gate - * `0x5` (`0b0101`): Task Gate. *Offset* is unused and should be set to 0. - * `0x6` (`0b0110`): 16-bit Interrupt Gate - * `0x7` (`0b0111`): 16-bit Trap Gate - * `0xE` (`0b1110`): 32-bit Interrupt Gate - * `0xF` (`0b1111`): 32-bit Trap Gate -- *DPL*: 2-bit value which specifies CPU Privilege Levels allowed to access this interrupt via `int` call. Hardware interrupts ignore this mechanism. -- *Present*: Present bit. Must be set to *1* for the descriptor to be valid. - -===== Example -[source,c] -struct InterruptDescriptor { - uint16_t offset_lo; - uint16_t selector; - uint8_t zero; - uint8_t type_attr; - uint8_t offset_hi; -}__attribute__((packed)); // __attribute__((packed)) is used to tell the compiler to not include the byte paddings, as they corrupt the binary structure of the entry. -`type_attr` values values that people are likely to use (DPL=0): +// TODO: x86_64 -* 32-bit Interrupt Gate: `0x8E` (present=`1` dpl=`0` type=`0b1110` => `0b1000_1110` => `0x8E`) -* 32-bit Trap Gate: `0x8F` (present=`1` dpl=`0` type=`0b1111` => `0b1000_1111` => `0x8F`) +=== Gate types -* Task Gate: `0x85` (present=`1` dpl=`0` type=`0b0101` => `0b1000_0101` => `0x85`) +[cols="1,3"] +|=== +| `0x5` +| Task gate +| `0x6` +| 16-bit interrupt gate -==== x86_64 +| `0x7` +| 16-bit trap gate +| `0xE` +| 32-bit interrupt gate -=== Gate types +| `0xF` +| 32-bit trap gate +|=== ==== Interrupt Gate -An Interrupt Gate is used to specify Interrupt Service Routines (ISR). +An Interrupt Gate is used to indicate an interrupt service routine (ISR). + +When an assembly interrupt call is issued, the CPU looks up the ISR by specified index in the IDT, stores necessary registers in the stack and jumps to the entry point. -When an assembly interrupt call `int ` is issued CPU looks up the specified ISR in the IDT, pushes necessary registers for `iret` to the stack and jumps to the entry point. +IMPORTANT: Usage of 16-bit gates is highly unadvised, as the CPU cannot properly return to 32-bit mode using `iret`. -If CPU was running in 32-bit mode, and the selector is a 16-bit gate, it will switch to 16-bit Protected Mode and jump to the entry point. To return back to 32-bit mode `o32 iret` instruction should be used, otherwise CPU would not know how to perform a 32-bit return (reading 32-bit instead of 16-bit values from the stack). +If CPU was running in 32-bit mode, and the selector is a 16-bit gate, it will switch to 16-bit Protected Mode and jump to the entry point. ==== Trap Gate -Trap gate is similar to Interrupt Gate. It is commonly used for syscalls and exceptions. The difference is that for a Trap Gate CPU does not disable hardware interrupts, while upon entering Interrupt Gate CPU automatically disables hardware interrupts and reenables them on return. +Trap gate is sometimes used for syscalls and exceptions. It is very similar to interrupt gate, however, in contrast to interrupt gate it does not set/clear interrupt flag. ==== Task Gate -Task Gate is a gate type specific to IA-32 that is used for hardware task switching. -In 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. +IMPORTANT: It is highly unadvised to use task gates and hardware task switching as a whole. Hardware task switching is very slow and partially dropped on x86_64 CPUs. + +Task gate is used for hardware task switching and is specific to IA-32. + +When processing a task gate interrupt, the CPU will perform a hardware task switch to the task specified by *Selector* field. The pointer to the interrupted task will be stored in the Link field of the new TSS. -When processing this interrupt, the CPU it will perform a hardware task switch to the specified task, rather than jumping to an ISR. A pointer back to the task which was interrupted will be stored in the Task Link field in TSS. \ No newline at end of file From 2dbcf3a2de7a829b33fcc70a151652befae998ec Mon Sep 17 00:00:00 2001 From: nitroflap Date: Wed, 1 Jun 2022 00:12:02 +0300 Subject: [PATCH 3/4] feat(idt): grammar and stylistic changes --- pages/idt.adoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pages/idt.adoc b/pages/idt.adoc index 3b9f079..296b151 100644 --- a/pages/idt.adoc +++ b/pages/idt.adoc @@ -46,14 +46,14 @@ IDTR is a register containing the IDT's address and limit. |=== ==== Things to know -* IDT can contain up to 256 (0..255) ISR vectors (entries above this limit will be ignored) +* IDT can contain up to 256 (0..255) ISR vectors. Entries above this limit will be ignored. * In contrast to GDT, the first entry of the IDT is valid and used. === IDT entry ==== IA-32 -On 32-bit processors, IDT entries are 8-bytes long, therefore to access the particular entry the raw index must be multiplied by 8 and added to the IDTR Offset. +On 32-bit processors, IDT entries are 8-bytes long, therefore to access a particular entry the raw index must be multiplied by 8 and added to the IDTR Offset. ===== Gate descriptor @@ -78,7 +78,7 @@ On 32-bit processors, IDT entries are 8-bytes long, therefore to access the part | Gate Type | 40 -- 43 -| Type of the gate. Described below in detail. +| Type of the gate. See below. | 0 | 44 @@ -90,7 +90,7 @@ On 32-bit processors, IDT entries are 8-bytes long, therefore to access the part | Present | 47 -| Indicates whether entry is valid +| Indicates whether the entry is valid | Offset (16 -- 31) | 48 -- 63 @@ -122,19 +122,19 @@ On 32-bit processors, IDT entries are 8-bytes long, therefore to access the part |=== ==== Interrupt Gate -An Interrupt Gate is used to indicate an interrupt service routine (ISR). +An interrupt gate is used to indicate an interrupt service routine (ISR). When an assembly interrupt call is issued, the CPU looks up the ISR by specified index in the IDT, stores necessary registers in the stack and jumps to the entry point. IMPORTANT: Usage of 16-bit gates is highly unadvised, as the CPU cannot properly return to 32-bit mode using `iret`. -If CPU was running in 32-bit mode, and the selector is a 16-bit gate, it will switch to 16-bit Protected Mode and jump to the entry point. +If the CPU was running in 32-bit mode, and the specified selector is a 16-bit gate, it will switch to 16-bit Protected Mode and jump to the entry point. ==== Trap Gate -Trap gate is sometimes used for syscalls and exceptions. It is very similar to interrupt gate, however, in contrast to interrupt gate it does not set/clear interrupt flag. +Trap gate is sometimes used for syscalls and exceptions. It is very similar to interrupt gate, however, in contrast to interrupt gate, it does not set/clear interrupt flag. ==== Task Gate -IMPORTANT: It is highly unadvised to use task gates and hardware task switching as a whole. Hardware task switching is very slow and partially dropped on x86_64 CPUs. +IMPORTANT: It is highly unadvised to use task gates and hardware task switching as a whole. Hardware task switching is very slow and is removed on x86_64 CPUs. Task gate is used for hardware task switching and is specific to IA-32. From e494d28d7a22176717e69124349dcd8069618c8b Mon Sep 17 00:00:00 2001 From: nitroflap Date: Wed, 1 Jun 2022 00:12:56 +0300 Subject: [PATCH 4/4] fix(idt): tags and description --- pages/idt.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/idt.adoc b/pages/idt.adoc index 296b151..b6dadcc 100644 --- a/pages/idt.adoc +++ b/pages/idt.adoc @@ -1,8 +1,8 @@ --- title: Interrupt descriptor table -tags: assembly, x86, x64, sysv, msvc +tags: x86, x64 category: Interrupts -description: IDT spec and tutorial +description: IDT specification --- :source-language: c