📄 001_arch_i386_mm_fault_.html
字号:
goto bad_area; //没有这样的vma<br>
if (vma->vm_start <= address)<br>
goto good_area; //异常地址在一个vma 中<br>
<br>
//运行到这里表示, 这个地址落到了一个vma 空洞中<br>
if (!(vma->vm_flags & VM_GROWSDOWN))<br>
goto bad_area; //如果这个空洞上的vma
不是堆栈, 肯定是越界<br>
<br>
if (error_code & 4) {<br>
/*<br>
* accessing the stack below %esp
is always a bug.<br>
* The "+ 32" is there due to some
instructions (like<br>
* pusha) doing post-decrement on
the stack and that<br>
* doesn't show up until later..<br>
*/<br>
if (address + 32 <
regs->esp)<br>
goto
bad_area; //异常点不在esp 附近时也是一种越界<br>
//而不是正常的堆栈操作<br>
}<br>
//对于正常的堆栈操作越界,我们应该为用户扩展堆栈<br>
if (expand_stack(vma, address))<br>
goto bad_area; //扩展失败, 转到bad_area<br>
<br>
/*<br>
* 堆栈扩展以后, vma 就正常了,因此流程自然进入good_area<br>
*/<br>
good_area:<br>
//异常点在正确的vma 中<br>
//还要根据vma(代表OS) 容许的权限, 对异常分类<br>
info.si_code = SEGV_ACCERR;<br>
write = 0;<br>
switch (error_code & 3) {<br>
default: /* 3:
write, present */<br>
#ifdef TEST_VERIFY_AREA<br>
if (regs->cs
== KERNEL_CS)<br>
printk("WP fault at %08lx\n", regs->eip);<br>
#endif<br>
/* fall through
*/<br>
case 2:
/* write, not present */<br>
if
(!(vma->vm_flags & VM_WRITE))<br>
goto bad_area; //vma 不容许写, 是一种非法操作<br>
write++;<br>
break;<br>
case 1:
/* read, present */<br>
goto bad_area;
// 权限错误, 非法<br>
case 0:
/* read, not present */<br>
if
(!(vma->vm_flags & (VM_READ | VM_EXEC)))<br>
goto bad_area; //vma 不容许读的情况下非法<br>
}<br>
<br>
/*<br>
* 如果处理陷入时到了这里,<br>
* 说明异常点在一个完好的vma 中,并且符合OS 赋予<br>
* 用户的权限.<br>
*/<br>
switch (handle_mm_fault(mm, vma, address, write)) {<br>
case
1:
<br>
tsk->min_flt++;<br>
break;<br>
case 2:<br>
tsk->maj_flt++;<br>
break;<br>
case 0:<br>
goto do_sigbus;<br>
default:<br>
goto out_of_memory;<br>
}<br>
<br>
/*<br>
* Did it hit the DOS screen memory VA from vm86
mode?<br>
*/<br>
if (regs->eflags & VM_MASK) {<br>
unsigned long bit = (address -
0xA0000) >> PAGE_SHIFT;<br>
if (bit < 32)<br>
tsk->thread.screen_bitmap |= 1 << bit;<br>
}<br>
up(&mm->mmap_sem);<br>
return;<br>
<br>
/*<br>
* Something tried to access memory that isn't in our memory map..<br>
* Fix it, but check if it's kernel or user first..<br>
*/<br>
bad_area:<br>
up(&mm->mmap_sem);<br>
<br>
bad_area_nosemaphore:<br>
/* User mode accesses just cause a SIGSEGV */<br>
if (error_code & 4) {<br>
tsk->thread.cr2 = address;<br>
tsk->thread.error_code =
error_code;<br>
tsk->thread.trap_no = 14;<br>
info.si_signo = SIGSEGV;<br>
info.si_errno = 0;<br>
/* info.si_code has been set above
*/<br>
info.si_addr = (void *)address;<br>
force_sig_info(SIGSEGV, &info,
tsk);<br>
return;<br>
}<br>
<br>
/*<br>
* Pentium F0 0F C7 C8 bug workaround.<br>
*/<br>
if (boot_cpu_data.f00f_bug) {<br>
unsigned long nr;<br>
<br>
nr = (address - idt) >> 3;<br>
<br>
if (nr == 6) {<br>
do_invalid_op(regs, 0);<br>
return;<br>
}<br>
}<br>
<br>
no_context:<br>
/* Are we prepared to handle this kernel fault?
*/<br>
if ((fixup = search_exception_table(regs->eip)) !=
0) {<br>
regs->eip = fixup;<br>
return;<br>
}<br>
<br>
/*<br>
* Oops. The kernel tried to access some bad page. We'll have to<br>
* terminate things with extreme prejudice.<br>
*/<br>
<br>
bust_spinlocks();<br>
<br>
if (address < PAGE_SIZE)<br>
printk(KERN_ALERT "Unable to
handle kernel NULL pointer dereference");<br>
else<br>
printk(KERN_ALERT "Unable to
handle kernel paging request");<br>
printk(" at virtual address %08lx\n",address);<br>
printk(" printing eip:\n");<br>
printk("%08lx\n", regs->eip);<br>
asm("movl %%cr3,%0":"=r" (page));<br>
page = ((unsigned long *) __va(page))[address
>> 22];<br>
printk(KERN_ALERT "*pde = %08lx\n", page);<br>
if (page & 1) {<br>
page &= PAGE_MASK;<br>
address &= 0x003ff000;<br>
page = ((unsigned long *)
__va(page))[address >> PAGE_SHIFT];<br>
printk(KERN_ALERT "*pte =
%08lx\n", page);<br>
}<br>
die("Oops", regs, error_code);<br>
do_exit(SIGKILL);<br>
<br>
/*<br>
* We ran out of memory, or some other thing happened to us that
made<br>
* us unable to handle the page fault gracefully.<br>
*/<br>
out_of_memory:<br>
up(&mm->mmap_sem);<br>
printk("VM: killing process %s\n", tsk->comm);<br>
if (error_code & 4)<br>
do_exit(SIGKILL);<br>
goto no_context;<br>
<br>
do_sigbus:<br>
up(&mm->mmap_sem);<br>
<br>
/*<br>
* Send a sigbus, regardless of whether we were in
kernel<br>
* or user mode.<br>
*/<br>
tsk->thread.cr2 = address;<br>
tsk->thread.error_code = error_code;<br>
tsk->thread.trap_no = 14;<br>
info.si_code = SIGBUS;<br>
info.si_errno = 0;<br>
info.si_code = BUS_ADRERR;<br>
info.si_addr = (void *)address;<br>
force_sig_info(SIGBUS, &info, tsk);<br>
<br>
/* Kernel mode? Handle exceptions or die */<br>
if (!(error_code & 4))<br>
goto no_context;<br>
return;<br>
<br>
vmalloc_fault: /* 内核的vmalloc可能分配了一个页面但是task自己的页表之内核<br>
部分还没有设置。注:每个task都有自己的“kernel 空间”,这里<br>
进行同步,从内核自己的页表将kernel space 的映射同步到<br>
task page table*/<br>
{<br>
/*<br>
* Synchronize this task's top
level page-table<br>
* with the 'reference' page
table.<br>
*/<br>
int offset =
__pgd_offset(address);<br>
pgd_t *pgd, *pgd_k;<br>
pmd_t *pmd, *pmd_k;<br>
<br>
pgd = tsk->active_mm->pgd +
offset;<br>
pgd_k = init_mm.pgd + offset;<br>
<br>
if (!pgd_present(*pgd)) {<br>
if
(!pgd_present(*pgd_k))<br>
goto bad_area_nosemaphore;<br>
set_pgd(pgd,
*pgd_k);<br>
return;<br>
}<br>
<br>
pmd = pmd_offset(pgd, address);<br>
pmd_k = pmd_offset(pgd_k,
address);<br>
<br>
if (pmd_present(*pmd) ||
!pmd_present(*pmd_k))<br>
goto
bad_area_nosemaphore;<br>
set_pmd(pmd, *pmd_k);<br>
return;<br>
}<br>
}<br>
<br>
<br>
英文说的比较明白的就不敢添足了。<br>
<br>
幸亏上个函数对handle_mm_fault评论了一番,do_page_fault到这里也该收手了.<br>
<br>
<br>
<br>
<br>
<br>
</td>
</tr>
</tbody>
</table>
</div>
<br>
<br></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -