-
/*
*
linux/arch/i386/mm/fault.c
*
* Copyright (C) 1995 Linus Torvalds
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
extern void die(const char *,struct
pt_regs *,long);
/*
/* For unblank_screen() */
* Ugly, ugly, but the goto's result in better
assembly..
*/
int
__verify_write(const void * addr, unsigned long
size)
{
good_area:
for (;;) {
survive:
{
int fault =
handle_mm_fault(current->mm, vma, start, 1);
if (!fault)
goto
bad_area;
if (!(vma->vm_flags &
VM_WRITE))
size--;
size += start & ~PAGE_MASK;
size >>= PAGE_SHIFT;
start
&= PAGE_MASK;
goto bad_area;
vma = find_vma(current->mm, start);
if (!vma)
goto
bad_area;
if (!size)
return 1;
struct
vm_area_struct * vma;
unsigned long
start = (unsigned long) addr;
if
(vma->vm_start > start)
goto check_stack;
}
}
if (fault < 0)
goto out_of_memory;
if (!size)
size--;
start += PAGE_SIZE;
if (start < vma->vm_end)
continue;
break;
vma = vma->vm_next;
if (!vma
|| vma->vm_start != start)
goto bad_area;
if
(!(vma->vm_flags & VM_WRITE))
goto bad_area;;
return 1;
check_stack:
bad_area:
out_of_memory:
if (current->pid == 1) {
current->policy
|= SCHED_YIELD;
schedule();
return 0;
if
(!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
if
(expand_stack(vma, start) == 0)
goto good_area;
}
}
goto survive;
goto bad_area;
extern
spinlock_t timerlist_lock;
/*
* Unlock any spinlocks
which will prevent us from getting the
* message out (timerlist_lock is acquired through
the
* console unblank code)
*/
void bust_spinlocks(int
yes)
{
spin_lock_init(&timerlist_lock);
if (yes) {
oops_in_progress = 1;
#ifdef
CONFIG_SMP
#endif
} else {
int loglevel_save =
console_loglevel;
global_irq_lock = 0;
/* Many
serial drivers do __global_cli() */
#ifdef CONFIG_VT
#endif
oops_in_progress = 0;
/*
* OK, the message is on the console.
Now we call printk()
* without
oops_in_progress set so that printk will give
klogd
* a poke. Hold onto your
hats...
unblank_screen();
*/
/* NMI oopser may have shut
console_loglevel = 15;
the console up */
}
}
printk(
console_loglevel =
loglevel_save;
void do_BUG(const char
*file, int line)//
输出错误信息
{
}
asmlinkage void
do_invalid_op(struct pt_regs *, unsigned long);
extern unsigned long idt;
/*
* This routine handles
page faults. It determines the address,
* and the problem, and then passes it
off to one of the appropriate
*
routines.
*
* error_code:
*
*
*
*/
asmlinkage void
do_page_fault(struct pt_regs *regs, unsigned long
error_code)//regs
存放着异常发生时
cpu
寄存器的值
bit 0 == 0 means no page found, 1 means
protection fault
bit 1 == 0 means read,
1 means write
bit 2 == 0 means kernel,
1 means user-mode
bust_spinlocks(1);
p>
printk(
//error_code
是异常发生时,
cpu
的控制单元自动压栈给函数的,只有前三
位有意义,
第
0
位被清零,异常是由访
问一个不在内存中的页引起的,否则是由保护性失效引起
的。
//
保护性失效,第一位,为
0
,由读访问或执行访问引起的,否则异常是由写访问引起
的
//
第二位被清零,系统处于内核态,否则处于
用户态
{
/* get the
address */
__asm__(
把引起缺页的页地址
存放在
struct task_struct *tsk;
struct mm_struct *mm;
struct
vm_area_struct * vma;
unsigned long
address;
unsigned long page;
unsigned long fixup;
int
write;
siginfo_t info;
address
局部变量中
/*
* We fault-in kernel-space virtual memory on-
demand. The
tsk = current;
/* It's safe to allow irq's after cr2
has been saved */
//
恢复中断
,CR2
中的值已经得到了保存
if (regs->eflags & X86_EFLAGS_IF)
local_irq_enable();
* 'reference' page table is init_.
*
* NOTE! We MUST NOT take
any locks for this case. We may
* be
in an interrupt or a critical region, and should
* only copy the information from the
master page table,
* nothing more.
*
* This verifies that the
fault happens in kernel space
*
(error_code & 4) == 0, and that the fault was not
a
* protection error (error_code & 1)
== 0.
*/
if (address >=
TASK_SIZE && !(error_code &
5))//
若
错误发生在内核空间
且
内核空间的写
/
读地址错误
/*
* If we're in an interrupt or have no user
* context, we must not take the
fault..
*/
if
(in_interrupt() || !mm)//
判断异常发生时
cpu
是否正在处理中断或是执行内
mm =
tsk->mm;
_code =
SEGV_MAPERR;//
地址没有映射到对象
goto vmalloc_fault;//
转至内核非连续空间的异常处理
核态线程
down_read(&mm->
mmap_sem);//
获取读写信号量
goto no_context;
vma = find_vma(mm, address)
;//
检查当前进程的线性区,验证该地址是否在
进程的地址空
间中
if (!vma)//
若没有这样的
VMA
,说明异常地址是在进程地
址堆栈的上方
,
非法
goto bad_area;
if (vma->vm_start <= address)//
< br>如果地址在
vma
中
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))//V
M_GROWSDOWN:
向下增长
,
只
有栈才有这样的属性
.
不属于栈而
且又落在空洞中
,
非法
/*
* Ok, we have a good vm_area for this memory
access, so
* we can handle it..
*/
good_area:
_code =
SEGV_ACCERR;//
对映射的对象没有权限
write = 0;
goto
bad_area;
if (error_code & 4)//
< br>异常发生在用户态,检查
address
是否小于
regs->esp
{
}
if (expand_stack(vma,
address))//
扩大栈空间
goto bad_area;
/*
* accessing the stack below %esp is
always a bug.
* The
*
pusha) doing post-decrement on the stack and that
* doesn't show up until later..
*/
if (address + 32 <
regs->esp)
goto bad_area;