Blog post: The Linux kernel initialization phase through the Early_trap_init () function completes the copy of the anomaly vector to the beginning of the 0xffff0000, these anomaly vectors are mostly pointed to through the Vector_stub macro definition of the code, The main task of this code is to calculate the return address of the exception, save the scene, switch to the SVC mode, jump to execute the assembly exception handler function, the assembly exception handler function in SVC mode, first take over the previous exception mode save the scene, and then call C processing function, The C function returns after executing a piece of assembly code to complete the exception return work. This series of work is the architecture of kernel exception handling based on the ARM9 processor.
The Linux kernel IRQ interrupt is also a class of exception events, but the IRQ interrupt occurs more than one source, arm920t only one IRQ interrupt signal receive PIN, the judgment of the specific interrupt source also needs to rely on the off-chip interrupt controller, moreover, the IRQ interrupt pin is connected with the interrupt pin on the peripheral, Peripheral type thousand odd state, specific outside with interrupt event requires the processor to perform special processing work, I think based on this background, a dedicated set of interrupt processing architecture to manage the interrupt event to enable the processor to work efficiently.
the core interrupt processing architecture is built on the INIT_IRQ () function , which is called directly by the Srart_kernel () function, defined in ARCH/ARM/KERNEL/IRQ.C.
void __init Init_irq (void) {int IRQ;//interrupt number for (IRQ = 0; IRQ < Nr_irqs; irq++) irq_desc[irq].status |= irq_norequest | Irq_noprobe; By setting each of the corresponding structure member "status" state field to an unsolicited and undetected state init_arch_irq (); INIT_ARCH_IRQ is actually a function pointer that is initially set by the Setup_arch () function}
function Analysis:
The ① kernel has a uniform number of interrupts, is used to call the "interrupt number", and defines the IRQ_DESC[NR_IRQS] structure data to describe each break, the interrupt number is used to locate the corresponding interrupt description structure in this array. IRQ_DESC structure behind the analysis, and now put aside.
② Explore the INIT_ARCH_IRQ () function: I found him. Initialized in the Start_kernel-->setup_arch function
void __init Setup_arch (char **cmdline_p) {... init_arch_irq = mdesc->init_irq;//void pointer Init_arch_irq force point to struct INIT_IRQ member of MACHINE_DESC structure ...}
Okay, here's the question: When did you fill out the initial "MDESC->INIT_IRQ" member beforehand?
struct Machine_desc {... void (*INIT_IRQ) (void); is a function pointer, in the sourceinside inside search a search. ......};
the INIT_IRQ member is assigned the S3C24XX_INIT_IRQ function in the file arch/arm/mach-s3c2440/mach-tq2440.c
Machine_start (s3c2440, "TQ2440"). Phys_io= s3c2410_pa_uart,.io_pg_offst= ((u32) S3c24xx_va_uart >>) & 0xfffc,.boot_params= S3c2410_sdram_pa + 0x100,.init_irq= S3C24XX_INIT_IRQ,//finally found!! The INIT_IRQ () function INIT_ARCH_IRQ the real content of the call through the function pointer!!! Analyze this function and see what really happened: map_io= tq2440_map_io,.init_machine= tq2440_machine_init,. timer= &s3c24xx_timer,machine_end
analysis here, the interrupt processing framework does not start to build, because there is not set up a variety of interrupt triggering mode and processing functions, and so on, set the content is the IRQ_DESC structure of the main member of the array initialization, let's see if this is the case!
③ Explore the S3C24XX_INIT_IRQ () function: Also defined in the Arch/arm/kernel/irq.c file, which is a huge function call relationship
Function: Completes initialization of the interrupt controller and sets the value of the corresponding function pointer of the interrupt descriptor to call these functions to complete the chip-level processing when the interrupt occurs. That is to establish a source of the interrupt corresponding to a processing function mapping relationship!
void __init S3c24xx_init_irq (void) {unsigned long pend;unsigned long last;int irqno;int i;irqdbf ("S3C2410_INIT_IRQ: Clearing interrupt status flags\n ");/* First, clear all interrupts pending ... *///(1) Before starting Setup, clear all interrupt sources waiting for last = 0;for (i = 0; I < 4; i++) {pend = __raw_readl (S3c24xx_eintpend), if (Pend = = 0 | | pend = last) Break;__raw_writel (Pend, s3c24xx_eintpend);p rint K ("Irq:clearing Pending ext Status%08x\n", (int) pend); last = Pend;} last = 0;for (i = 0; i < 4; i++) {pend = __raw_readl (S3C2410_INTPND), if (Pend = = 0 | | pend = = last) Break;__raw_writel (p End, S3C2410_SRCPND); __raw_writel (Pend, S3C2410_INTPND);p RINTK ("Irq:clearing Pending Status%08x\n", (int) pend); last = Pend;} last = 0;for (i = 0; i < 4; i++) {pend = __raw_readl (S3C2410_SUBSRCPND), if (Pend = = 0 | | pend = = last) break;printk ("IRQ : Clearing subpending status%08x\n ", (int) pend); __raw_writel (Pend, S3C2410_SUBSRCPND); last = Pend;} /* Register the main interrupts *///(2) to register the primary interrupt irqdbf ("S3c2410_init_irq:registering s3c2410 Interrupt handlers\n "); for (irqno = irq_eint4t7; irqno <= irq_adcparent; irqno++) {/* Set all the s3c2410 internal IRQ S */switch (irqno) {/* deal with the special IRQs (cascaded) */case irq_eint4t7:case irq_eint8t23:case irq_uart0:case IRQ_ Uart1:case irq_uart2:case irq_adcparent:set_irq_chip (irqno, &s3c_irq_level_chip); Set_irq_handler (irqno, handle _LEVEL_IRQ); Break;case irq_reserved6:case irq_reserved24:/* no IRQ here */break;default://irqdbf ("Registering IRQ%d ( s3c IRQ) \ n ", irqno); Set_irq_chip (irqno, &s3c_irq_chip); Set_irq_handler (irqno, HANDLE_EDGE_IRQ); Set_irq_flags ( Irqno, Irqf_valid);}} /* Setup The Cascade IRQ handlers *///(3) to set cascading interrupt handling Set_irq_chained_handler (IRQ_EINT4T7, s3c_irq_demux_extint4t7); set_ Irq_chained_handler (irq_eint8t23, s3c_irq_demux_extint8); Set_irq_chained_handler (IRQ_UART0, s3c_irq_demux_uart0) ; Set_irq_chained_handler (Irq_uart1, S3c_irq_demux_uart1); Set_irq_chained_handler (Irq_uart2, S3c_irq_demux_uart2) ; Set_irq_chained_handler (Irq_adcparent, S3c_irq_deMUX_ADC);/* Register external interrupts *///(4) Register external interrupt for (irqno = irq_eint0; irqno <= irq_eint3; irqno++) {irqdbf ("re Gistering IRQ%d (ext int) \ n ", irqno); Set_irq_chip (irqno, &S3C_IRQ_EINT0T4); Set_irq_handler (Irqno, Handle_edge_ IRQ); Set_irq_flags (irqno, irqf_valid);} for (irqno = irq_eint4; irqno <= irq_eint23; irqno++) {irqdbf ("Registering IRQ%d (extended s3c IRQ) \ n", irqno); SET_IRQ _chip (Irqno, &s3c_irqext_chip); Set_irq_handler (irqno, HANDLE_EDGE_IRQ); Set_irq_flags (irqno, IRQF_VALID);} /* Register the UART interrupts *///(5) Register the UART interrupt irqdbf ("s3c2410:registering external interrupts\n"); for (irqno = Irq_s3cu art_rx0; Irqno <= irq_s3cuart_err0; irqno++) {irqdbf ("Registering IRQ%d (s3c uart0 IRQ) \ n", irqno); Set_irq_chip (irqno, &s3c_irq_uart0); set_irq_ Handler (irqno, HANDLE_LEVEL_IRQ); Set_irq_flags (irqno, irqf_valid);} for (irqno = irq_s3cuart_rx1; irqno <= irq_s3cuart_err1; irqno++) {irqdbf ("Registering IRQ%d (s3c uart1 IRQ) \ n", irqno ); Set_irq_chip (Irqno, &s3c_IRQ_UART1); Set_irq_handler (irqno, HANDLE_LEVEL_IRQ);//Set handler function Set_irq_flags (irqno, irqf_valid);} for (irqno = irq_s3cuart_rx2; irqno <= irq_s3cuart_err2; irqno++) {irqdbf ("Registering IRQ%d (s3c uart2 IRQ) \ n", irqno ); Set_irq_chip (irqno, &s3c_irq_uart2); Set_irq_handler (irqno, HANDLE_LEVEL_IRQ);//Set handler function Set_irq_flags (irqno, Irqf_valid);} for (irqno = IRQ_TC; irqno <= irq_adc; irqno++) {irqdbf ("Registering IRQ%d (s3c ADC IRQ) \ n", irqno); Set_irq_chip (irqno , &S3C_IRQ_ADC); Set_irq_handler (irqno, HANDLE_EDGE_IRQ);//Set handler function Set_irq_flags (irqno, irqf_valid);} IRQDBF ("s3c2410:registered interrupt handlers\n");}
Function Analysis:
The main
use of the Set_irq_chip (), Set_irq_handler (), Set_irq_flags () These three functions to initialize the IRQ_DESC structure of chip, HANDLE_IRQ, flags, Initialization means pointing to something (struct, function, etc.)
Take a paragraph to analyze:
for (irqno = irq_eint4; irqno <= irq_eint23; irqno++) {irqdbf ("Registering IRQ%d (extended s3c IRQ) \ n", irqno); SET_IRQ _chip (Irqno, &s3c_irqext_chip); Set_irq_handler (irqno, HANDLE_EDGE_IRQ); Set_irq_flags (irqno, IRQF_VALID);}
Set_irq_chip (): The effect of execution is irq_desc[irqno]=&s3c_irqext_chip, the struct irq_chip of the interrupt descriptor * The chip structure pointer points to a well-set struct IRQ_CHIP s3c_irqext_chip structure for the S3CXXX chip, in which the members are mainly used to set the function pointers such as the shielding of the external interrupt or the triggering method.
static struct Irq_chip s3c_irqext_chip = {. Name= "S3c-ext",. mask= s3c_irqext_mask, //shielded interrupt source. unmask= S3c_irqext_ unmask,//Turn on the receive interrupt source. ack= S3c_irqext_ack, //Response interrupt: Usually clears the current interrupt so that the next interrupt can be received. set_type= S3c_irqext_type, //Trigger mode. set_ wake= s3c_irqext_wake //Wake related};
The analysis of the interrupt processing framework here is built, as shown in the figure, the user registration of the interrupt handler function is mainly refers to the driver development when the programmer added.
The following is an analysis of the processing of the HANDLE_EDGE_IRQ interrupt handler function.
④ Interrupt processing function , just as we play bare metal when we write the same meaning: the break and the interruption of processing, it is here to consider more things, so look complex, the fact that it is more complex.
void Handle_edge_irq (unsigned int IRQ, struct Irq_desc *desc) {spin_lock (&desc->lock);d esc->status &= ~ ( Irq_replay | irq_waiting);/* * If we ' re currently running this IRQ, or its disabled, * we shouldn ' t process the IRQ. Mark it pending, handle * the necessary masking and go out * abnormal handling */if (unlikely (Desc->status & (Irq_inprogress | irq_disabled)) | | !desc->action) {desc->status |= (irq_pending | irq_masked); MASK_ACK_IRQ (DESC, IRQ);d ESC = IRQ_REMAP_TO_DESC (IRQ, desc); goto Out_unlock;} KSTAT_INCR_IRQS_THIS_CPU (IRQ, DESC);/* Start handling the IRQ starts processing interrupt */if (Desc->chip->ack) desc->chip->ack (IRQ); # #清中断处理desc = Irq_remap_to_desc (IRQ, DESC);/* Mark the IRQ currently in Progress.*/desc->status |= Irq_inprogress;do {struct Irqaction *action = desc->action;//# #取出irq_desc结构里边的action成员, the action list is a user-registered handler irqreturn_t Action_ret ; if (unlikely (!action)) {desc->chip->mask (IRQ); goto Out_unlock;} /* * When another IRQ arrived while we were HAndling * One, we could have masked the IRQ. * Renable it, if it is not disabled in meantime. * Abnormal handling: If another interrupt arrives, block it */if (unlikely (Desc->status & (Irq_pending | irq_masked | irq_disabled) = = (Irq_pending | irq_masked)) {desc->chip->unmask (IRQ);d esc->status &= ~irq_masked;} Desc->status &= ~irq_pending;spin_unlock (&desc->lock); Action_ret = Handle_irq_event (IRQ, action); # #进入handle_IRQ_event函数中发现是: Take out the members of the action link side, perform action->handleif (!noirqdebug) note_interrupt (IRQ, DESC, Action_ RET); Spin_lock (&desc->lock);} while (Desc->status & (Irq_pending | irq_disabled) = = irq_pending);d esc->status &= ~irq_inprogress;out_unlock:spin_unlock (&desc->lock);}
Take a look at the Handle_irq_event function: Traverse the action list in the middle descriptor corresponding to the interrupt number, and execute the handler member of the action struct, which is the processing function
irqreturn_t handle_irq_event (unsigned int IRQ, struct irqaction *action) {irqreturn_t ret, retval = irq_none;unsigned int status = 0;if (! (Action->flags & irqf_disabled)) LOCAL_IRQ_ENABLE_IN_HARDIRQ ();d o {trace_irq_handler_entry (IRQ, action); ret = Action->handler (IRQ, Action->dev _ID); Perform Action->handler function trace_irq_handler_exit (IRQ, Action, ret); switch (ret) {Case Irq_wake_thread:ret = irq_handled if (unlikely (!ACTION->THREAD_FN)) {Warn_no_thread (IRQ, action); if (Likely (!test_bit (irqtf_died, &action->thread_flags))) {Set_bit (Irqtf_runthread, &action->thread_ Flags); wake_up_process (Action->thread);} Case Irq_handled:status |= Action->flags;break;default:break;} retval |= ret;action = Action->next;} while (action); Take out the list of members if (Status & Irqf_sample_random) add_interrupt_randomness (IRQ); local_irq_disable (); return retval;}
⑤ Scenario Analysis: When a key break occurs, the function processing flow:
First enter the exception mode to perform B vector_irq+offset--and then vecrot_stubs do some protection field work and switch to SVC mode--Jump to the jump table __usr_irq execute and then take over the "live" data, then call Asm_do_ IRQ, he found IRQ_DESC[IRQ].HANDLE_IRQ based on the interrupt number.
-- assuming IRQ_DESC[IRQ].HANDLE_IRQ points to HANDLE_EDGE_IRQ, this function will work on the ④ point above.
Analysis of Linux kernel interrupt processing system