entry.s
来自「自己根据lkd和情境分析」· S 代码 · 共 665 行 · 第 1/2 页
S
665 行
/* * linux/arch/i386/entry.S * * Copyright (C) 1991, 1992 Linus Torvalds *//* * entry.S contains the system-call and fault low-level handling routines. * This also contains the timer-interrupt handler, as well as all interrupts * and faults that can result in a task-switch. * * NOTE: This code handles signal-recognition, which happens every time * after a timer-interrupt and after each system call. * * I changed all the .align's to 4 (16 byte alignment), as that's faster * on a 486. * * Stack layout in 'ret_from_system_call': * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be * updated in fork.c:copy_process, signal.c:do_signal, * ptrace.c and ptrace.h * * 0(%esp) - %ebx * 4(%esp) - %ecx * 8(%esp) - %edx * C(%esp) - %esi * 10(%esp) - %edi * 14(%esp) - %ebp * 18(%esp) - %eax * 1C(%esp) - %ds * 20(%esp) - %es * 24(%esp) - orig_eax * 28(%esp) - %eip * 2C(%esp) - %cs * 30(%esp) - %eflags * 34(%esp) - %oldesp * 38(%esp) - %oldss * * "current" is in register %ebx during any slow entries. */#include <linux/config.h>#include <linux/sys.h>#include <linux/linkage.h>#include <asm/segment.h>#include <asm/smp.h>EBX = 0x00ECX = 0x04EDX = 0x08ESI = 0x0CEDI = 0x10EBP = 0x14EAX = 0x18DS = 0x1CES = 0x20ORIG_EAX = 0x24EIP = 0x28CS = 0x2CEFLAGS = 0x30OLDESP = 0x34OLDSS = 0x38/*例:这里的EAX不是表示寄存器%%eax,而是表示该寄存器的内容在系统堆栈中的位置相对于此时的堆栈指针的位移*/CF_MASK = 0x00000001IF_MASK = 0x00000200NT_MASK = 0x00004000VM_MASK = 0x00020000/* * these are offsets into the task-struct. */state = 0flags = 4sigpending = 8addr_limit = 12exec_domain = 16need_resched = 20tsk_ptrace = 24processor = 52ENOSYS = 38#define SAVE_ALL \ cld; \ pushl %es; \ pushl %ds; \ pushl %eax; \ pushl %ebp; \ pushl %edi; \ pushl %esi; \ pushl %edx; \ pushl %ecx; \ pushl %ebx; \ movl $(__KERNEL_DS),%edx; \ movl %edx,%ds; \ movl %edx,%es;#define RESTORE_ALL \ popl %ebx; \ popl %ecx; \ popl %edx; \ popl %esi; \ popl %edi; \ popl %ebp; \ popl %eax; \1: popl %ds; \2: popl %es; \ addl $4,%esp; \ #加4跳过ORIG_EAX,那里存放的是中断初压入堆栈的中断请求号3: iret; \ #系统堆栈恢复到刚进入中断门时的状态,iret使CPU从中断返回,如果是从系统状态返回到用户状态会将当前堆栈切换到用户堆栈.section .fixup,"ax"; \4: movl $0,(%esp); \ #当装入一个段寄存器时,CPU都要根据新的段选择码以及GDTR或LDTR的内容在相应的段描述表中找到段描述项 jmp 1b; \ #检查如果描述项与选择码有效且相符则将描述项装入CPU段寄存器的不可见部分,如果一旦某个原因使选择码或5: movl $0,(%esp); \ #描述项无效或不符时,CPU产生一次全面保护异常(GP),所以要准备修复这种异常的手段,这里将%ds或%es清0来达到目的, jmp 2b; \ #以0作为段选择码装入段寄存器不会引起GP异常,在以后通过这个空选择码访问内存时才会引起异常,那是回到用户空间以后的事,到时候可以选择杀死这个进程6: pushl %ss; \ #从系统空间中断返回到用户空间时,要从系统堆栈恢复用户堆栈指针,包括堆栈段寄存器,并从系统堆栈中恢复在用户空间的返回地址,包括代码段寄存器内容 popl %ds; \ #所以也可能发生GP异常,使CPU回不到用户空间去,CS和SS不接受空选择码,因此调用do_exit杀死当前进程,然后内核会调度另一个进程为当前进程,所以 pushl %ss; \ #当要从系统空间返回到用户空间时,是返回到另一个进程的用户空间去,那时候要从系统堆栈中恢复寄存器副本也是另一个进程的副本了 popl %es; \ pushl $11; \ call do_exit; \.previous; \.section __ex_table,"a";\ .align 4; \ .long 1b,4b; \ .long 2b,5b; \ .long 3b,6b; \.previous#define GET_CURRENT(reg) \ movl $-8192, reg; \ andl %esp, regENTRY(lcall7) pushfl # We get a different stack layout with call gates, pushl %eax # which has to be cleaned up later.. SAVE_ALL movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. movl CS(%esp),%edx # this is eip.. movl EFLAGS(%esp),%ecx # and this is cs.. movl %eax,EFLAGS(%esp) # movl %edx,EIP(%esp) # Now we move them to their "normal" places movl %ecx,CS(%esp) # movl %esp,%ebx pushl %ebx andl $-8192,%ebx # GET_CURRENT movl exec_domain(%ebx),%edx # Get the execution domain movl 4(%edx),%edx # Get the lcall7 handler for the domain pushl $0x7 call *%edx addl $4, %esp popl %eax jmp ret_from_sys_callENTRY(lcall27) pushfl # We get a different stack layout with call gates, pushl %eax # which has to be cleaned up later.. SAVE_ALL movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. movl CS(%esp),%edx # this is eip.. movl EFLAGS(%esp),%ecx # and this is cs.. movl %eax,EFLAGS(%esp) # movl %edx,EIP(%esp) # Now we move them to their "normal" places movl %ecx,CS(%esp) # movl %esp,%ebx pushl %ebx andl $-8192,%ebx # GET_CURRENT movl exec_domain(%ebx),%edx # Get the execution domain movl 4(%edx),%edx # Get the lcall7 handler for the domain pushl $0x27 call *%edx addl $4, %esp popl %eax jmp ret_from_sys_callENTRY(ret_from_fork) pushl %ebx call SYMBOL_NAME(schedule_tail) addl $4, %esp GET_CURRENT(%ebx) testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS jne tracesys_exit jmp ret_from_sys_call/* * Return to user mode is not as complex as all this looks, * but we want the default path for a system call return to * go as quickly as possible which is why some of this is * less clear than it otherwise should be. */#SAVE_ALL压入堆栈的寄存器依次为:%es, %ds, %eax, %ebp, %edi, %esi, %edx, %ecx, %ebx#这里的%eax持有系统调用号(与orig_ax相同),%ebp是用作子程序调用过程中的帧指针的,#这2个寄存器不能用来传参,所以实际上可以用来传递参数的只有5个寄存器#所以系统调用传递的参数不能超过5个ENTRY(system_call) pushl %eax # save orig_eax 即保存系统调用号 SAVE_ALL GET_CURRENT(%ebx) testb $0x02,tsk_ptrace(%ebx) # 检查PT_TRACESYS 是否为1 ,跟踪子进程的系统调用 jne tracesys cmpl $(NR_syscalls),%eax #检查系统调用号是不是超出了范围 jae badsys call *SYMBOL_NAME(sys_call_table)(,%eax,4) #调用跳转表sys_call_table,偏移为%eax,单位为4字节 movl %eax,EAX(%esp) # save the return valueENTRY(ret_from_sys_call) cli # need_resched and signals atomic test cmpl $0,need_resched(%ebx) #测试是否需要调度need_resched在本文件中定义,表示偏移值 jne reschedule cmpl $0,sigpending(%ebx) #测试是否有信号sigpending同need_resched jne signal_returnrestore_all: RESTORE_ALL ALIGNsignal_return: sti # we can get here from an interrupt handler testl $(VM_MASK),EFLAGS(%esp) #VM_MASK vm86模式,模拟DOS movl %esp,%eax jne v86_signal_return xorl %edx,%edx call SYMBOL_NAME(do_signal) jmp restore_all ALIGNv86_signal_return: call SYMBOL_NAME(save_v86_state) movl %eax,%esp xorl %edx,%edx call SYMBOL_NAME(do_signal) jmp restore_all ALIGNtracesys: movl $-ENOSYS,EAX(%esp) call SYMBOL_NAME(syscall_trace) #系统调用syscall_trace()向父进程报告具体系统调用的进入和返回 movl ORIG_EAX(%esp),%eax cmpl $(NR_syscalls),%eax jae tracesys_exit call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return valuetracesys_exit: call SYMBOL_NAME(syscall_trace) jmp ret_from_sys_callbadsys: movl $-ENOSYS,EAX(%esp) jmp ret_from_sys_call ALIGNENTRY(ret_from_intr) GET_CURRENT(%ebx) #获取当前进程的task_struct结构指针放入寄存器EBX中ret_from_exception: movl EFLAGS(%esp),%eax # mix EFLAGS and CS movb CS(%esp),%al testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor? CS的最低2位代表发生中断时CPU的运行级别 jne ret_from_sys_call jmp restore_all #若发生在系统空间则跳转到restore_all ALIGNreschedule: call SYMBOL_NAME(schedule) # test 调用函数schedule jmp ret_from_sys_callENTRY(divide_error) pushl $0 # no error code pushl $ SYMBOL_NAME(do_divide_error) ALIGNerror_code: pushl %ds pushl %eax xorl %eax,%eax pushl %ebp pushl %edi pushl %esi pushl %edx decl %eax # eax = -1 pushl %ecx pushl %ebx cld # CLD是清方向标志,把标识寄存器的D位置呈零 movl %es,%ecx movl ORIG_EAX(%esp), %esi # get the error code 把出错代码放到%esi movl ES(%esp), %edi # get the function address 把函数指针放入%edi movl %eax, ORIG_EAX(%esp) #ORIG_EAX变为-1,RESTORE_ALL中会把ORIG_EAX跳过去 movl %ecx, ES(%esp) #用%ecx中的内容替换堆栈中ES处的函数指针 movl %esp,%edx pushl %esi # push the error code pushl %edx # push the pt_regs pointer movl $(__KERNEL_DS),%edx movl %edx,%ds movl %edx,%es GET_CURRENT(%ebx) call *%edi #调具体的函数 addl $8,%esp jmp ret_from_exception/*error_code 堆栈结构图,可与include/asm-i386/hw_irq.h中SAVE_ALL的堆栈结构图比较 | SS | 用户堆 | ESP | 栈指针 | EFLAGS | | CS | 返回 | EIP | 地址 | 出错代码 | (ORIG_EAX) | do_page_fault | 函数指针(ES) | DS | | EAX | | EBP | | EDI | | ESI | | EDX | | ECX | | EBX | | | <-堆栈指针ENTRY(coprocessor_error) /*因协处理器出错而导致的异常处理*/ pushl $0 /*将0压入堆栈中出错代码相应的地方即不产生出错代码*/ pushl $ SYMBOL_NAME(do_coprocessor_error) jmp error_codeENTRY(simd_coprocessor_error) pushl $0 pushl $ SYMBOL_NAME(do_simd_coprocessor_error) jmp error_codeENTRY(device_not_available) pushl $-1 # mark this as an int SAVE_ALL GET_CURRENT(%ebx) movl %cr0,%eax testl $0x4,%eax # EM (math emulation bit)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?