# Linux Concurrency References - IRQ on x86 [TOC] ## TL;DR x86 interrupt control flow: IOAPIC->Local APIC->IDT->Interrupt Gate/ Trap Gate + GDT-> Interrupt handler ## References To understand how a segment selector works, to under stand how call gate work, to understand how the interrupt gate works. 1. [Privilege Rings - Segmentation & Segment Registers 1](https://youtu.be/ArDl_7zdMNI?si=jZMOiL0gPUrlTI3_) 2. [Global Descriptor Table (GDT) & Local Descriptor Table (LDT) 1 - Global Descriptor Table Register](https://youtu.be/ArDl_7zdMNI?si=_rfC8St2ZcUZZLJ9) 3. [Global Descriptor Table (GDT) & Local Descriptor Table (LDT) 3 - Local Descriptor Table Register](https://youtu.be/qTeweY_6TrA?si=P3m90Ip6GXh-LxrD): This describes how to get a GDT/LDT entry from a segment selector. A segment selector points to an entry in the GDT or LDT. If it is meant to point to an entry in GDT, then the top 13 bit describe which entry does this segment selector meant to point to. Note that the location of the GDT table is recorded in the GDTR register. If on the other hand, this is meant to point to and entry in the LDT, there will be a two-layer indrection process to resolve the address. It first find the location of the LDT by the LDTR, which is yet another segment selector pointing to a GDT entry containing the base location of the LDT. Once the base address of the LDT is found, the upper 13 bit of the segment selector is used to locate the offset of the LDT entry. 4. [Privilege Rings & Segmentation - Call Gates](https://youtu.be/D_vEiIlGfzU?si=dt-wDZJmoEaNilM9): Call gate is not in use now, but the address resolution process of an interrupt gate is very similiar to that of the call gate. Do a far call to call into a call gate. When the segment selector pointing to a call gate, its address part is ignored. The segment selector points to a GDT or LDT entry that contains another far pointer-sh structure. The segment selector part of that far pointer should pointer to a code segment descriptor. Because of how code segemntation works, the base address of the code segment descriptor is also unused, essentially doing the identity mapping from the far pointer-ish address offset in the GDT/LDT entry to the physical memory. 5. [Interrupts - Interrupts vs. Exceptions](https://youtu.be/imEJlEXVkj4?si=jTbc2g--TqIGjI_I) 6. [Interrupts - Software Interrupt Instructions](https://youtu.be/yFqrWCST_2g?si=zmsE2BwAmboRWtWu) 7. [Interrupts - Tasks and the Task State Segment (TSS)](https://youtu.be/arIy89fEXgA?si=BDxTU7AAGWf-YZRf): TSS hold the pointer to the stack where the saved context should start. This is used both `int` and `iret`. Note that each ring has its own stack pointer entry in this TSS. 8. [Interrupts - Interrupt Descriptor Table (IDT)](https://youtu.be/cFdOJ6coVvQ?si=5AhdpiJwiYkt7ad3): Location of the IDT is stored in a dedicated register canned IDTR. When interrupt happen, entry point for the interrupt handler is found using the IDT, and the context are pushed from the location recorded in a Task-State Segment, or TSS. The location of the TSS is described in a register Task Register, which points to a entry in the GDT pointing to the base location of the TSS. The TSS contains table of stack pointers meant to be used during the interrupt happening at the different priviledge rings. The IDTR is a 80-bit register. The most significant 64 bit is the beginning of the table, while the rest 16 bit is the size of this table. Each entry is a 16-byte descriptor, among which the first 32 is reserved for Intel processor, in Linux their handlers are named with `asm_exc_*` prefix. For example `asm_exc_divide_zero_error()`, `asm_exc_page_fault()`. The rest of them are user-defined. Linux divides rest of those into 2 parts. First 204 are handled `asm_common_interrupt()`, while the others 20 are reserved for APIC and SMP-related functionalities like IPI. 6. [Interrupts - Interrupt Descriptors](https://youtu.be/BTsKj8tiV8I?si=PtCMz7UsqgHbNkyT): Each entry in the IDT is a trap gate or a interrupt gate. Note that although the form is similar to that of a call gate, the address resolution is one indirection less. An IDT entry contains a far pointer-ish entry, whose segment selector pointing to an entry in GDT/LDT. Because the trap/interrupt gate behavior like a code segement, the base address for the GDT/LDT entry in ununsed, but the RPL is and it is how the interrupt handler gains privilege. This also essentially makes the address in the IDT is used directly without any offset. 7. [Interrupts - Interrupt Descriptors - What Did We Learn?](https://youtu.be/iMKO7EfA6hU?si=T4mlggffF4TgpxPB) 8. [Interrupts - Interrupt Masking](https://youtu.be/HzCHA0LUKxs?si=rapWDwRZrhYEtDfR) 9. [Interrupts - Conclusion](https://youtu.be/VBQjWDVgXxA?si=mkb9sSek5KR301Yd) ### [[x86] Linux Kernel Interrupt Delivery Configuration: How the Linux Kernel Interacts with Hardware - Adrian Huang, Lenovo](https://youtu.be/UqnixJCpHGM) {%youtube UqnixJCpHGM %}