Interrupt-interrupt handling program entry and exit (3) (based on 3.16-rc4), interrupt 3.16-rc4
In the previous blog, we analyzed the interrupt gate initialization process of the interrupt Descriptor Table. During the initialization process in the interrupt array, we can see that every interrupt handler will jump into common_interrupt. Next we will analyze the common_interrupt Assembly snippet (arch/x86/kernel/entrt_32.S ).
1 .p2align CONFIG_X86_L1_CACHE_SHIFT 2 common_interrupt: 3 ASM_CLAC 4 addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range */ 5 SAVE_ALL 6 TRACE_IRQS_OFF 7 movl %esp,%eax 8 call do_IRQ 9 jmp ret_from_intr10 ENDPROC(common_interrupt)11 CFI_ENDPROC
Row 5th SAVE_ALL is also an assembly clip (macro) used to load the current multiple registers into the stack, because these registers may be used in do_IRQ. Line 3 calls the do_IRQ function. Next we will analyze the do_IRQ function (arch/x86/kernel/irq. c ).
1 __visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs) 2 { 3 struct pt_regs *old_regs = set_irq_regs(regs); 4 5 /* high bit used in ret_from_ code */ 6 unsigned vector = ~regs->orig_ax; 7 unsigned irq; 8 9 irq_enter();10 exit_idle();11 12 irq = __this_cpu_read(vector_irq[vector]);13 14 if (!handle_irq(irq, regs)) {15 ack_APIC_irq();16 17 if (irq != VECTOR_RETRIGGERED) {18 pr_emerg_ratelimited("%s: %d.%d No irq handler for vector (irq %d)\n",19 __func__, smp_processor_id(),20 vector, irq);21 } else {22 __this_cpu_write(vector_irq[vector], VECTOR_UNDEFINED);23 }24 }25 26 irq_exit();27 28 set_irq_regs(old_regs);29 return 1;30 }
The vector_irq array in the row 12th stores the correspondence between the interrupt vector number and the interrupt line number (Interrupt number). The _ this_cpu_read function is used to obtain the interrupt number corresponding to the current interrupt vector number. The handle_irq function in row 14th uses the interrupt number irq as the parameter to enter the interrupt service routine corresponding to the interrupt number. The handle_irq function (arch/x86/kernel/irq_32.c) is analyzed below ).
1 bool handle_irq(unsigned irq, struct pt_regs *regs) 2 { 3 struct irq_desc *desc; 4 int overflow; 5 6 overflow = check_stack_overflow(); 7 8 desc = irq_to_desc(irq); 9 if (unlikely(!desc))10 return false;11 12 if (user_mode_vm(regs) || !execute_on_irq_stack(overflow, desc, irq)) {13 if (unlikely(overflow))14 print_stack_overflow();15 desc->handle_irq(irq, desc);16 }17 18 return true;19 }
Row 3 obtains the struct irq_desc struct pointer corresponding to the interrupt number irq. The kernel uses the struct irq_desc struct array to store all interrupt service routines. The interrupt number irq is used as the array element subscript, as shown below (kernel/irq/irqdesc. c ).
1 struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {2 [0 ... NR_IRQS-1] = {3 .handle_irq = handle_bad_irq,4 .depth = 1,5 .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),6 }7 };
Next, let's take a look at the struct irq_desc struct (include/linux/irqdesc. h ).
1 struct irq_desc { 2 struct irq_data irq_data; 3 unsigned int __percpu *kstat_irqs; 4 irq_flow_handler_t handle_irq; 5 #ifdef CONFIG_IRQ_PREFLOW_FASTEOI 6 irq_preflow_handler_t preflow_handler; 7 #endif 8 struct irqaction *action; /* IRQ action list */ 9 unsigned int status_use_accessors;10 unsigned int core_internal_state__do_not_mess_with_it;11 unsigned int depth; /* nested irq disables */12 unsigned int wake_depth; /* nested wake enables */13 unsigned int irq_count; /* For detecting broken IRQs */14 unsigned long last_unhandled; /* Aging timer for unhandled count */15 unsigned int irqs_unhandled;16 atomic_t threads_handled;17 int threads_handled_last;18 raw_spinlock_t lock;19 struct cpumask *percpu_enabled;20 #ifdef CONFIG_SMP21 const struct cpumask *affinity_hint;22 struct irq_affinity_notify *affinity_notify;23 #ifdef CONFIG_GENERIC_PENDING_IRQ24 cpumask_var_t pending_mask;25 #endif26 #endif27 unsigned long threads_oneshot;28 atomic_t threads_active;29 wait_queue_head_t wait_for_threads;30 #ifdef CONFIG_PROC_FS31 struct proc_dir_entry *dir;32 #endif33 int parent_irq;34 struct module *owner;35 const char *name;36 } ____cacheline_internodealigned_in_smp;
The real interrupt handler is not directly stored in the struct irq_desc struct, but stored in the struct irqaction struct pointed to by the action Member of the struct irq_desc struct, Row 1. Next, let's take a look at the struct irqaction struct type (include/linux/interrupt. h ).
1 struct irqaction { 2 irq_handler_t handler; 3 void *dev_id; 4 void __percpu *percpu_dev_id; 5 struct irqaction *next; 6 irq_handler_t thread_fn; 7 struct task_struct *thread; 8 unsigned int irq; 9 unsigned int flags;10 unsigned long thread_flags;11 unsigned long thread_mask;12 const char *name;13 struct proc_dir_entry *dir;14 } ____cacheline_internodealigned_in_smp;
The 1st-row handler stores interrupted service routines. The next struct pointer of this type is stored in Row 3 and next. Because an interrupt number can correspond to multiple interrupt service routines (shared with a broken line), the kernel organizes multiple interrupt service routines with the same interrupt number into a linked list, it is mounted to the irq_desc array element that uses the irq Number as the underlying standard.
Return to the handle_irq function, obtain the struct irq_desc struct pointer desc corresponding to the irq Number in row 8th, and then execute the desc-> handle_irq function in row 15th, execute all interrupt service routines of irq in this function.
Here, you must be clear about the differences between idt_table and irq_desc arrays in IDT tables. idt_table stores interrupt handlers, and the code at the beginning of these interrupt handlers is the same, you must jump to the common_interrupt function to find the interrupt service routine. The irq_desc array stores the interrupt service routine. The interrupt processing program will eventually find the corresponding interrupt service routine through this array and execute it. When writing a driver, we often need to write the interrupt service routine of the device. we store the interrupt service routine in the requested struct irqaction structure, the struct is mounted to the corresponding linked list of the irq_desc array. When the interrupt occurs, the system automatically uses IDT ---> GDT ---> Interrupt Processing Program ---> common_interrupt (belonging to the Interrupt Processing Program) ---> do_IRQ ---> handle_irq, and then execute all the interrupt service routines corresponding to the irq in the irq_desc [NR_IRQS] array.
After the interrupted service routine is executed, the system will return to the common_interrupt function at the beginning of the article, start to execute row 9th, and jump to the ret_from_intr function (arch/x86/kernel/entrt_32.S ).
1 ret_from_intr: 2 GET_THREAD_INFO(%ebp) 3 #ifdef CONFIG_VM86 4 movl PT_EFLAGS(%esp), %eax # mix EFLAGS and CS 5 movb PT_CS(%esp), %al 6 andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax 7 #else 8 /* 9 * We can be coming here from child spawned by kernel_thread().10 */11 movl PT_CS(%esp), %eax12 andl $SEGMENT_RPL_MASK, %eax13 #endif14 cmpl $USER_RPL, %eax15 jb resume_kernel # not returning to v8086 or userspace16 17 ENTRY(resume_userspace)18 LOCKDEP_SYS_EXIT19 DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt20 # setting need_resched or sigpending21 # between sampling and the iret22 TRACE_IRQS_OFF23 movl TI_flags(%ebp), %ecx24 andl $_TIF_WORK_MASK, %ecx # is there any work to be done on25 # int/exception return?26 jne work_pending27 jmp restore_all28 END(ret_from_exception)
Row 3 stores the cs register value in eax from the current stack, and row 3 extracts the DPL field from eax and saves it to eax through mask calculation. Row 3 compares eax (DPL) and the size of the user space permissions. If the DPL permission is too large, execute 15 lines and restore it to the kernel state. Otherwise, restore it to the user State and 17 lines.
What is the interrupted service program of the interrupt service in assembly language?
Well, I guess you're asking about PC, not MCU.
I. the interruption of assembly languages is divided into the following types:
1. BIOS interruption. This is fixed to the BIOS program. The BIOS is automatically loaded to the specified memory every time it is started.
DOS interrupt in 2.186. After the DOS system is loaded, the system will extend the BIOS interrupt vector and add some new vectors to it. These functions are the interrupt service program provided by the DOS system.
3.286 or more of the system interruptions, the PC will enter the protection mode, after the OS is loaded, the interruption is controlled by the IDT, this mechanism is similar to the interrupt vector table, but the interrupt vector is replaced with a selection sub. This interrupt mechanism slightly varies with different types of cpus. I will not go into detail here, and I will not understand it all myself.
II. Implementation of interruptions (normal interruptions under 8086)
Have you heard of the "priority encoder? -- If two signals are received at the same time, the priority of a signal is specified to be high. Execute the signal first. Interrupt is a similar solution.
When the CPU gets a signal with a high operation Priority (for example, the clock is triggered once at a fixed time; for example, the keyboard response, the user wants to exit any DOS program that is being executed through Ctrl + C), the CPU will hook up the program currently being executed, and then process the signal (similar to Call, but slightly different, ).
When handling an interruption, the system will interpret it as a label, such as int 9 h and int 21h. This label is a sequence number and stores a continuous table somewhere in the memory. This label is the "row number" in the table, except that each row contains two columns, includes the segment base address and offset of the interrupt handler. The interrupt vector table starts at and every 4 bytes is a table item. The interrupt number x4 is the address stored in the corresponding interrupt vector table. The high address is the base address, and the low address is the offset.
I don't know what you don't understand...
To sum up your problem, the interrupted service program is loaded into the memory. It may exist on the BIOS chip or hard disk before loading; in the interrupt vector table, only the entry address of the interrupt handler can be written. You must know that each table item is only 4 bytes. I do not believe that you will not talk about the specific interrupt service program in the compilation book, let me give you a rough idea: the INTR pin of the CPU gets the interrupt signal and the mark, for example, Number 5. The interrupt vector table item is 0000: 000A. Read this memory, obtain the endpoint address of the interrupted program, for example, AAAA: BBBB. Then, it will import the current CS/IP and Flags registers into the stack and then go to AAAA: go to BBBB and execute until the iret command returns the original task (maybe the task ended with the interruption and will not be returned ).
As for the interruption of the protection mode, I believe you have not encountered it yet. In the future, the 8259A chip will be manipulated to implement advanced interrupt. This is not a general requirement.
What is vector interrupt? Describes the relationship between the interrupt type number, the interrupt vector table, and the interrupt service program entry address?
Vector interrupt .. In fact, the processor is interrupted.
The corresponding interrupt number 1 2 3 is generated in a register.
If the processor supports enabling the vector interrupt function. This will jump directly to the interrupt want table for execution.
If not .. Then everyone enters an interruption. Difficult to determine in the program what causes the interruption, difficult to execute the corresponding interrupt service program
Interrupt type number... It is estimated that it is the same as the preceding number.
Interrupt vector table, used to store a table of Interrupt handlers.
The endpoint of the interrupted service program .. When an interruption occurs. The program automatically jumps to the address where the program runs.
In general .. There may be only a few interrupt entry addresses,
However, there are many interrupt types.
Therefore, many interrupt types have to be added to an interrupt entry address.
In this case, you need to determine the interrupt at the address of the interrupt entry and then process it.
This is because we need to determine what the interruption is. Cannot achieve the goal of fast interruption
All vector interrupt and interrupt vector meters
The purpose of vector interrupt is. The offset is automatically generated during interruption,
Process the address that automatically jumps to the interrupt vector table + offset, so that you do not need to determine what the interrupt is. Accelerated interrupted access