#define __ASM__ #include #include #include #define __ASM_INTR_DIAGNOSIS #ifdef __ASM_INTR_DIAGNOSIS .section .bss .global debug_resv debug_resv: .skip 16 tmp_store: .skip 4 #endif .section .bss .align 16 lo_tmp_stack: .skip 128 tmp_stack: .section .text .global interrupt_wrapper interrupt_wrapper: /* Stack layout (layout of struct isr_param) msa: [ss] > 76 [esp] > 72 eflags > 68 cs > 64 eip > 60 err_code > 56 vector > offset = 52 [saved_prev_ctx] > offset = 0 --- esp gs fs es ds > offset = 7 * 4 = 28 + 4 esi ebp edi edx ecx ebx eax lsa: depth > offset = 0 las: Least Significant Address msa: Most Significant Address */ cld subl $52, %esp pushl %esp subl $16, %esp movw %gs, 12(%esp) movw %fs, 8(%esp) movw %es, 4(%esp) movw %ds, (%esp) pushl %esi pushl %ebp pushl %edi pushl %edx pushl %ecx pushl %ebx pushl %eax movl __current, %eax movl 8(%eax), %eax incl %eax pushl %eax # nested intr: current depth movl 116(%esp), %eax /* 取出 %cs */ andl $0x3, %eax /* 判断 RPL */ jz 1f movw $KDATA_SEG, %ax /* 如果从用户模式转来,则切换至内核数据段 */ movw %ax, %gs movw %ax, %fs movw %ax, %ds movw %ax, %es # 保存用户栈顶指针。这是因为我们允许系统调用内进行上下文切换,而这样一来,我们就失去了用户栈的信息, # 这样一来,就无法设置信号上下文。这主要是为了实现了pause()而做的准备 movl __current, %eax # 保存x87FPU的状态 movl 68(%eax), %ebx fxsave (%ebx) movl 124(%esp), %ebx # 取出esp movl %ebx, 60(%eax) # 存入__current->ustack_top 1: movl %esp, %eax andl $0xfffffff0, %esp subl $16, %esp movl %eax, (%esp) call intr_handler movl (%esp), %eax .global soft_iret soft_iret: movl %eax, %esp #ifdef __ASM_INTR_DIAGNOSIS movl %eax, (debug_resv + 8) movl 48(%esp), %eax movl 60(%eax), %eax movl %eax, (debug_resv + 4) # eip #endif movl __current, %eax movl 68(%eax), %eax test %eax, %eax # do we have stored x87 context? jz 1f fxrstor (%eax) 1: popl %eax # discard isr_param::depth popl %eax popl %ebx popl %ecx popl %edx popl %edi popl %ebp popl %esi movw (%esp), %ds movw 4(%esp), %es movw 8(%esp), %fs movw 12(%esp), %gs movl 16(%esp), %esp movl %eax, tmp_store movl __current, %eax # nested intr: restore saved context popl 8(%eax) # depth popl 12(%eax) # eax popl 16(%eax) # ebx popl 20(%eax) # ecx popl 24(%eax) # edx popl 28(%eax) # edi popl 32(%eax) # ebp popl 36(%eax) # esi popl 40(%eax) # ds popl 44(%eax) # es popl 48(%eax) # fs popl 52(%eax) # gs popl 56(%eax) # esp addl $8, %esp #ifdef __ASM_INTR_DIAGNOSIS movl (%esp), %eax movl %eax, debug_resv #endif # 处理TSS.ESP的一些边界条件。如果是正常iret(即从内核模式*优雅地*退出) # 那么TSS.ESP0应该为iret进行弹栈后,%esp的值。 # 所以这里的边界条件是:如返回用户模式,iret会额外弹出8个字节(ss,esp) movl 4(%esp), %eax andl $3, %eax setnz %al shll $3, %eax addl $12, %eax addl %esp, %eax movl %eax, (_tss + 4) movl tmp_store, %eax iret .global switch_to switch_to: # 约定 # arg1: 目标进程PCB地址 (next popl %ebx # next movl __current, %eax movl 64(%eax), %ecx # __current->pagetable movl 64(%ebx), %eax # next->pagetable cmpl %ecx, %eax # if(next->pagtable != __current->pagetable) { jz 1f movl %eax, %cr3 # cpu_lcr3(next->pagetable) # } 1: movl %ebx, __current # __current = next # 我们已经处在了新的地址空间,为了避免影响其先前的栈布局 # 需要使用一个临时的栈空间 movl $tmp_stack, %esp call signal_dispatch # kernel/signal.c test %eax, %eax # do we have signal to handle? jz 1f jmp handle_signal 1: leal 8(%ebx), %eax jmp soft_iret .global handle_signal handle_signal: # 注意1:任何对proc_sig的布局改动,都须及时的保证这里的一致性! # 注意2:handle_signal在调用之前,须确保proc_sig已经写入用户栈! leal 8(%eax), %ebx # arg1 in %eax: addr of proc_sig structure in user stack pushl $UDATA_SEG # proc_sig->prev_context.proc_regs.ss pushl %eax # esp movl 48(%ebx), %ebx pushl 68(%ebx) # proc_sig->prev_context.proc_regs.execp->eflags pushl $UCODE_SEG # cs pushl $sig_wrapper # eip for sig wrapper movw $UDATA_SEG, %cx # switch data seg to user mode movw %cx, %es movw %cx, %ds movw %cx, %fs movw %cx, %gs iret .section .usrtext sig_wrapper: # in user mode movl %esp, %eax and $0xfffffff0, %esp subl $8, %esp pushl %eax # Addr to proc_sig structure pushl 4(%eax) # proc_sig->sig_num ---- 16 bytes aligned call *(%eax) # invoke signal handler # invoke the sigreturn syscall to exit the signal wrapper movl $__SYSCALL_sigreturn, %eax movl 4(%esp), %ebx int $LUNAIX_SYS_CALL ud2 # never reach!