In the previous blog, the initialization of the exception and unshielded interrupt section in the Interrupt Descriptor Table (IDT) is described. This article will analyze the initialization of the interrupt section.
In the previous blog, we can see that in the setup_once Assembly segment, the kernel initially initializes the interrupt and exception parts, and uses the address of the early_idt_handlers function to initialize the exception gate descriptor, use the ignore_int function address to initialize the remaining interrupt gate descriptor. Then, the kernel further initializes the idt in the trap_init function, and uses a valid exception handler to initialize the descriptor with the interrupt vector number 0-31. You should be careful to find that during the initialization process, only exceptions and unshielded interruptions are initialized (that is, the first 32 interrupt vector numbers ), the last 256-32 = 224 interrupt gate descriptors were not initialized. That is to say, the last 244 interrupt gate descriptors still point to the useless ignore_int function. The final initialization of the interrupt gate descriptor will be analyzed below. First, we will introduce the interrupt global array, which contains all interrupt handlers, as shown below: (ARCH/x86/kernel/entrt_32.s)
1 .section .init.rodata,"a" 2 ENTRY(interrupt) 3 .section .entry.text, "ax" 4 .p2align 5 5 .p2align CONFIG_X86_L1_CACHE_SHIFT 6 ENTRY(irq_entries_start) 7 RING0_INT_FRAME 8 vector=FIRST_EXTERNAL_VECTOR 9 .rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/710 .balign 3211 .rept 712 .if vector < NR_VECTORS13 .if vector <> FIRST_EXTERNAL_VECTOR14 CFI_ADJUST_CFA_OFFSET -415 .endif16 1: pushl_cfi $(~vector+0x80) /* Note: always in signed byte range */17 .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 618 jmp 2f19 .endif20 .previous21 .long 1b22 .section .entry.text, "ax"23 vector=vector+124 .endif25 .endr26 2: jmp common_interrupt27 .endr28 END(irq_entries_start)29 30 .previous31 END(interrupt)32 .previous
This Code defines an interrupt Global Array. If you do not understand ATT assembly, this code looks very difficult. The following is a rough analysis of this Code section. Line 1 declares a data segment, and line 2 calls this data segment 'initupt'. Line 3 declares a code segment, the code segment is included in the data segment on the front. The code segment is named 'irq _ entries_start 'from row 1 '. The following 4-5 lines illustrate the code snippet alignment method. The next 7th lines assign values to the vector, Which is vector = 32. In fact, the array of interrup stores all external interruptions without exceptions, exception initialization has been completed in the trap_init function, and the vector Number of the External Interrupt starts from 32, so the vector value is 32. Next we can see the pseudo command. Rept in line 9 and 11. This pseudo command is a loop. You can think of it as a for loop. The number behind the command is the number of cycles. This pseudoinstruction actually tells the compiler to copy the following content several times in the memory. The 9th rows (NR_VECTORS-FIRST_EXTERNAL_VECTOR + 6)/7 value is 32, requiring the subsequent content to be replicated cyclically for 32 times. Therefore, the combination of rows 9th and 11th is equivalent to a dual for loop, with a total cycle of 32*7 = 224 times. This is exactly the number of external interrupt vector numbers. Each execution of an internal loop, put an external interrupt handler into an array element. Next, the. Previous pseudocommand is displayed in Row 3. This command indicates that the data is returned to the previous segment. Here, the data is returned to the interrupt Data Segment, with 21st rows. A long data type is defined in the interrupt Data Segment, the value is marked with 1, which is actually the address of the 16th line code. Then the second row goes back to the current code segment, adding 1 to the vector, and then 22nd goes into the next loop of the repeating loop. Line 2: the command of Line 2 is sandwiched between the two internal and external repeating cycles. This indicates that JMP common_interrupt is copied every 7 internal cycles. Then, the second row enters the next loop of the external recurrence. After a total of 32*7 cycles are executed, this code is complete. Use. the preivous pseudo command defines two arrays. One is the interrput array, and each element of the array is. long 1b (row 21st), and the other array is irq_entries_start. Each element of the array contains several Assembly commands (Lines 16-18 and 26 ). This is. previous usage. Every time an element is initialized in the irq_entries_start array, a pointer to the newly initialized element in the irq_entries_start array is immediately returned (. long 1B), used as an element of the interrupt array. Finally, the interrput array contains 224 pointers (the address of each interrupt handler), pointing to the corresponding elements in the irq_entries_start array respectively. Each element in the irq_entries_start array stores several Assembly commands (these assembly commands are the public part at the beginning of the interrupt handler ). In addition, you can see from row 3 that each element in the irq_entries_start array contains the JMP common_interrupt command and jumps into a piece of common code.
In the above work, the kernel only manages all the external interrupt handlers using two arrays. Next, we need to initialize all the external interrupt gate descriptors in the IDT (Interrupt Descriptor Table. The Code is as follows: (ARCH/x86/kernel/irqinit. c)
1 void __init native_init_IRQ(void) 2 { 3 int i; 4 5 /* Execute any quirks before the call gates are initialised: */ 6 x86_init.irqs.pre_vector_init(); 7 8 apic_intr_init(); 9 10 /*11 * Cover the whole vector space, no vector can escape12 * us. (some of these will be overridden and become13 * ‘special‘ SMP interrupts)14 */15 i = FIRST_EXTERNAL_VECTOR;16 for_each_clear_bit_from(i, used_vectors, NR_VECTORS) {17 /* IA32_SYSCALL_VECTOR could be used in trap_init already. */18 set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]);19 }20 21 if (!acpi_ioapic && !of_ioapic)22 setup_irq(2, &irq2);23 24 #ifdef CONFIG_X86_3225 irq_ctx_init(smp_processor_id());26 #endif27 }
Lines 16-19 show that all interrupt handler addresses in the interrupt array are used to initialize the interrupt gate descriptor of IDT. The set_intr_gate function has been analyzed in the previous blog and will not be analyzed here.
So far, the initialization of the exception and interrupt gate descriptor in all IDT is complete.