do_page_fault 函数分析

代码路径:arch/arm64/mm/fault.c

/**

 * @brief 

 * 通过汇编函数  el1_da 跳转到这个函数 

 * @param addr 失效地址 x0 FSR 提供 

 * @param esr el1_da 汇编函数中读取到的el1_da寄存器 x3  ESR提供  

 * @param regs 发生异常时寄存器pt_regs指针  x2

 * @return asmlinkage 
   */
    //代码在.kprobes.text
    //# define __kprobes	__attribute__((__section__(".kprobes.text")))
   static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
   			   struct pt_regs *regs)
   {
   const struct fault_info *inf;
   struct task_struct *tsk;
   struct mm_struct *mm;
   vm_fault_t fault, major = 0;
   unsigned long vm_flags = VM_READ | VM_WRITE;
   unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;

   if (notify_page_fault(regs, esr))//通知页面故障
   	return 0;

   tsk = current;
   mm  = tsk->mm;

   /*

    * If we're in an interrupt or have no user context, we must not take
    * the fault.
      */
       //判断 内核是否在执行一些关键路经的代码 tsk->mm == NULL
      // pagefault_disabled()---> 页错误被disable || in_atomic() ----> 是否处于原子状态
      if (faulthandler_disabled() || !mm)
      goto no_context;

   if (user_mode(regs))
   	mm_flags |= FAULT_FLAG_USER;
   //判断当前异常类型  根据 esr 设置相关的标志位
   if (is_el0_instruction_abort(esr)) {
   	vm_flags = VM_EXEC;
   } else if ((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) {
   	vm_flags = VM_WRITE;
   	mm_flags |= FAULT_FLAG_WRITE;
   }

//根据FAR 寄存器 判断异常是否发生在用户空间 
	if (is_ttbr0_addr(addr) && is_el1_permission_fault(addr, esr, regs)) {
		/* regs->orig_addr_limit may be 0 if we entered from EL0 */
		if (regs->orig_addr_limit == KERNEL_DS)
			die_kernel_fault("access to user memory with fs=KERNEL_DS",
					 addr, esr, regs);
if (is_el1_instruction_abort(esr))
		die_kernel_fault("execution of user memory",
				 addr, esr, regs);

	if (!search_exception_tables(regs->pc))
		die_kernel_fault("access to user memory outside uaccess routines",
				 addr, esr, regs);
}

perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);

/*
 * As per x86, we may deadlock here. However, since the kernel only
 * validly references user space from well defined areas of the code,
 * we can bug out early if this is from code which shouldn't.
 */
 //判断当前进程的 mm->mmap_sem 是否可以获取
 //返回1 可以获取读写信号 0 不能获取读写信号 
 //如果brk在申请堆空间  或者mmap 正在进行内存映射 就获取不到读写信号
if (!down_read_trylock(&mm->mmap_sem)) {
	if (!user_mode(regs) && !search_exception_tables(regs->pc))
		goto no_context;//发生在内核空间 使用内核的__do_kernel_fault 函数
retry:
		//发生在用户空间 使用down_read 等待持有者释放锁
		down_read(&mm->mmap_sem);
	} else {
		/*
		 * The above down_read_trylock() might have succeeded in which
		 * case, we'll have missed the might_sleep() from down_read().
		 */
		might_sleep();
#ifdef CONFIG_DEBUG_VM
		if (!user_mode(regs) && !search_exception_tables(regs->pc))
			goto no_context;
#endif
	}
// 用户空间 使用__do_page_fault
	fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
	major |= fault & VM_FAULT_MAJOR;
...
//释放锁
up_read(&mm->mmap_sem);
...

return 0;
no_context:
	__do_kernel_fault(addr, esr, regs);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DipsyHu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值