@
0. Purpose:
1. Hardware interrupt response -- > interrupt in kernel driver
2. System call function response (sys call) - > system call
3. Custom interrupt -- > soft interrupt mode of software
4. Signal interrupt (kill -signalnum)
5. System exceptions and errors -- > system exception handling to understand the role of system exceptions
1. Interrupt mechanism of Linux
1.1 classification:
Hardware interrupt software interrupt
Hard interrupt: the interrupt sent by the 8259A similar hardware interrupt control chip of the computer host
Interrupt from AR interrupt controller
Soft interrupt: exception type I: interrupt reserved by CPU
System call exception
1.2 code structure:
asm.s -> trap.c
system_call.s -> fork.c signal.c exit.c sys.c
2. Interrupted workflow:
2.1: recall
- Do the conversion of CPU working mode
- Copy and stack registers
- Set interrupt exception vector table
- Save the return value of the normal function
- Jump to the corresponding interrupt service function to run
- Restore the mode and register
- Jump back to the working function address and continue running
2.2 interrupted workflow in Linux
-
Stack the values of all registers (switch CPU mode)
SS EFLAGS ESP CS EIP -
Put the exception code into the stack (interrupt number: which device generates the interrupt, and who can find the interrupt processing if an error occurs)
The exception code here is the interrupt number. The c code in the process of interrupt processing depends on this number to find the interrupt handling function -
Put the current function return value into the stack (in order to find out where the interrupt is after the interrupt is executed and restore it)
-
Call out the corresponding interrupt service function
-
Register recovery
As shown in the figure:
processing process before interrupt, reply process of interrupt execution process of interrupt
Hardware interrupt processing ASM s trap.c
Processing procedure of software and system call s fork.csignal .cexit.c sys.c
3. Code implementation process of interrupt
processing process before interrupt, reply process of interrupt execution process of interrupt
Hardware interrupt processing ASM s trap.c
The EIP register is used to store the address of the instruction to be read by the CPU. The CPU reads the instruction to be executed through the EIP register. Each time the CPU executes the corresponding assembly instruction, the value of EIP register will increase.
What the teacher said is not accurate. In fact, eax and (the memory pointed to by esp) are exchanged. esp stores the memory address, and adding parentheses is to take the memory value corresponding to this address, not the esp of the operation
CS(code segment) code segment address register, which stores the starting address of the code segment
DS(data segment) data segment address register, which stores the starting address of the data segment
SS(stack segment) stack segment address register, which stores the starting address of the stack segment (each process has its own stack space)
ES(extra segment) additional segment address register, which stores the starting address of the additional segment
/* * linux/kernel/asm.s * * (C) 1991 Linus Torvalds */ /* * asm.s contains the low-level code for most hardware faults. * page_exception is handled by the mm, so that isn't here. This * file also handles (hopefully) fpu-exceptions due to TS-bit, as * the fpu must be properly saved/resored. This hasn't been tested. */ .globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op .globl _double_fault,_coprocessor_segment_overrun .globl _invalid_TSS,_segment_not_present,_stack_segment .globl _general_protection,_coprocessor_error,_irq13,_reserved _divide_error: pushl $_do_divide_error//Put a c language function on the stack no_error_code://No error code interrupt xchgl %eax,(%esp) // The value of% esp in this case is$_ do_ divide_ Function address of error pushl %ebx pushl %ecx pushl %edx pushl %edi pushl %esi pushl %ebp push %ds push %es push %fs pushl $0 # "error code" lea 44(%esp),%edx pushl %edx movl $0x10,%edx mov %dx,%ds mov %dx,%es mov %dx,%fs call *%eax addl $8,%esp pop %fs pop %es pop %ds popl %ebp popl %esi popl %edi popl %edx popl %ecx popl %ebx popl %eax iret _debug://This is an interrupt pushl $_do_int3 # _do_debug jmp no_error_code//Jump _nmi: pushl $_do_nmi jmp no_error_code _int3: pushl $_do_int3 jmp no_error_code _overflow: pushl $_do_overflow jmp no_error_code _bounds: pushl $_do_bounds jmp no_error_code _invalid_op: pushl $_do_invalid_op jmp no_error_code _coprocessor_segment_overrun: pushl $_do_coprocessor_segment_overrun jmp no_error_code _reserved: pushl $_do_reserved jmp no_error_code _irq13: pushl %eax xorb %al,%al outb %al,$0xF0 movb $0x20,%al outb %al,$0x20 jmp 1f 1: jmp 1f 1: outb %al,$0xA0 popl %eax jmp _coprocessor_error _double_fault: pushl $_do_double_fault error_code://Interrupt with interrupt code xchgl %eax,4(%esp) # error code <-> %eax xchgl %ebx,(%esp) # &function <-> %ebx pushl %ecx pushl %edx pushl %edi pushl %esi pushl %ebp push %ds push %es push %fs pushl %eax # error code lea 44(%esp),%eax # offset pushl %eax movl $0x10,%eax mov %ax,%ds mov %ax,%es mov %ax,%fs call *%ebx addl $8,%esp pop %fs pop %es pop %ds popl %ebp popl %esi popl %edi popl %edx popl %ecx popl %ebx popl %eax iret _invalid_TSS: pushl $_do_invalid_TSS jmp error_code _segment_not_present: pushl $_do_segment_not_present jmp error_code _stack_segment: pushl $_do_stack_segment jmp error_code _general_protection: pushl $_do_general_protection jmp error_code
trap.c
/* * linux/kernel/traps.c * * (C) 1991 Linus Torvalds */ /* * 'Traps.c' handles hardware traps and faults after we have saved some * state in 'asm.s'. Currently mostly a debugging-aid, will be extended * to mainly kill the offending process (probably by giving it a signal, * but possibly by killing it outright if necessary). */ #include <string.h> #include <linux/head.h> #include <linux/sched.h> #include <linux/kernel.h> #include <asm/system.h> #include <asm/segment.h> #include <asm/io.h> #define get_seg_byte(seg,addr) ({ \ register char __res; \ __asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ :"=a" (__res):"0" (seg),"m" (*(addr))); \ __res;}) #define get_seg_long(seg,addr) ({ \ register unsigned long __res; \ __asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ :"=a" (__res):"0" (seg),"m" (*(addr))); \ __res;}) #define _fs() ({ \ register unsigned short __res; \ __asm__("mov %%fs,%%ax":"=a" (__res):); \ __res;}) int do_exit(long code); void page_exception(void); void divide_error(void); void debug(void); void nmi(void); void int3(void); void overflow(void); void bounds(void); void invalid_op(void); void device_not_available(void); void double_fault(void); void coprocessor_segment_overrun(void); void invalid_TSS(void); void segment_not_present(void); void stack_segment(void); void general_protection(void); void page_fault(void); void coprocessor_error(void); void reserved(void); void parallel_interrupt(void); void irq13(void); //esp_ptr segment pointer //nr wrong segment number //In general, the die function is used to print error messages static void die(char * str,long esp_ptr,long nr) { long * esp = (long *) esp_ptr; int i; //The following basic information is printed in the stack printk("%s: %04x\n\r",str,nr&0xffff); printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n", esp[1],esp[0],esp[2],esp[4],esp[3]); printk("fs: %04x\n",_fs()); printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17)); if (esp[4] == 0x17) { printk("Stack: "); for (i=0;i<4;i++) printk("%p ",get_seg_long(0x17,i+(long *)esp[3])); printk("\n"); } str(i); printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i); for(i=0;i<10;i++) printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0]))); printk("\n\r"); do_exit(11);//Exit interrupt /* play segment exception */ } void do_double_fault(long esp, long error_code) { die("double fault",esp,error_code); } void do_general_protection(long esp, long error_code) { die("general protection",esp,error_code); } //asm. The first function called by s is here void do_divide_error(long esp, long error_code) { die("divide error",esp,error_code); } void do_int3(long * esp, long error_code, long fs,long es,long ds, long ebp,long esi,long edi, long edx,long ecx,long ebx,long eax) { int tr; __asm__("str %%ax":"=a" (tr):"0" (0)); printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r", eax,ebx,ecx,edx); printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r", esi,edi,ebp,(long) esp); printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r", ds,es,fs,tr); printk("EIP: %8x CS: %4x EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]); } void do_nmi(long esp, long error_code) { die("nmi",esp,error_code); } void do_debug(long esp, long error_code) { die("debug",esp,error_code); } void do_overflow(long esp, long error_code) { die("overflow",esp,error_code); } void do_bounds(long esp, long error_code) { die("bounds",esp,error_code); } void do_invalid_op(long esp, long error_code) { die("invalid operand",esp,error_code); } void do_device_not_available(long esp, long error_code) { die("device not available",esp,error_code); } void do_coprocessor_segment_overrun(long esp, long error_code) { die("coprocessor segment overrun",esp,error_code); } void do_invalid_TSS(long esp,long error_code) { die("invalid TSS",esp,error_code); } void do_segment_not_present(long esp,long error_code) { die("segment not present",esp,error_code); } void do_stack_segment(long esp,long error_code) { die("stack segment",esp,error_code); } void do_coprocessor_error(long esp, long error_code) { if (last_task_used_math != current) return; die("coprocessor error",esp,error_code); } void do_reserved(long esp, long error_code) { die("reserved (15,17-47) error",esp,error_code); } //Interrupt initialization function //set_trap_gate has low priority and can only be called by user program //set_system_gate priority is very high, which is called by all programs of the system and users void trap_init(void) { int i; set_trap_gate(0,÷_error);//This interrupt occurs if the divisor is 0 set_trap_gate(1,&debug);//This interrupt is called during one-step debugging set_trap_gate(2,&nmi); set_system_gate(3,&int3); /* int3-5 can be called from all */ set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op); set_trap_gate(7,&device_not_available); set_trap_gate(8,&double_fault); set_trap_gate(9,&coprocessor_segment_overrun); set_trap_gate(10,&invalid_TSS); set_trap_gate(11,&segment_not_present); set_trap_gate(12,&stack_segment); set_trap_gate(13,&general_protection); set_trap_gate(14,&page_fault); set_trap_gate(15,&reserved); set_trap_gate(16,&coprocessor_error); for (i=17;i<48;i++) set_trap_gate(i,&reserved); set_trap_gate(45,&irq13); outb_p(inb_p(0x21)&0xfb,0x21); outb(inb_p(0xA1)&0xdf,0xA1); set_trap_gate(39,¶llel_interrupt); }