As mentioned in the previous blog, if the address does not belong to the address space of the process, the do_page_fault () function continues to execute the statements at the bad_area mark. If an error occurs in the user State, send a SIGSEGV signal to the current process and end the function:
/*
* Something tried to access memory that isn't in our memory map ..
* Fix it, but check if it's kernel or user first ..
*/
Bad_area:
Up_read (& mm-> mmap_sem );
Bad_area_nosemaphore:
/* User Mode accesses just cause a SIGSEGV */
If (error_code & 4 ){
/*
* Valid to do another page fault here because this one came
* From user space.
*/
If (is_prefetch (regs, address, error_code ))
Return;
Tsk-> thread. Cr2 = address;
/* Kernel addresses are always protection faults */
Tsk-> thread. error_code = error_code | (address> = task_size );
Tsk-> thread. trap_no = 14;
Force_sig_info_fault (SIGSEGV, si_code, address, tsk );
Return;
}
# Ifdef config_x86_f00f_bug
/*
* Pentium F0 0f C7 C8 bug workaround.
*/
If (boot_cpu_data.f00f_bug ){
Unsigned long NR;
Nr = (address-idt_descr.address)> 3;
If (Nr = 6 ){
Do_invalid_op (regs, 0 );
Return;
}
}
# Endif
The force_sig_info_fault () function ensures that the process does not ignore or block the SIGSEGV signal, and sends the signal to the user-state process while passing additional information through the si_code local variable. The si_code variable has been set to segv_maperr (if the exception is caused by a nonexistent page box, at the beginning of the previous blog ), or set it to segv_accerr (if the exception is caused by invalid access to the existing page box ).
If the exception occurs in the kernel state (error_code's 2nd bits are cleared by 0, that is, error_code & 4 is 0), there are still two optional situations:
(1) The exception is caused by passing a linear address as a system call parameter to the kernel.
(2) exceptions are caused by a real kernel defect.
The function distinguishes between the two options:
No_context:
/* Are we prepared to handle this Kernel Fault? */
If (fixup_exception (regs ))
Return;
/*
* Valid to do another page fault here, because if this fault
* Had been triggered by is_prefetch fixup_exception wocould have
* Handled it.
*/
If (is_prefetch (regs, address, error_code ))
Return;
In the first case, the Code jumps to a piece of "corrected Code". A typical operation of this Code is to send a SIGSEGV signal to the current process, or use an appropriate error code to terminate the system call handler:
Int fixup_exception (struct pt_regs * regs)
{
Const struct exception_table_entry * fixup;
# Ifdef config_pnpbios
If (unlikely (regs-> XCS &~ 15) = (gdt_entry_pnpbios_base <3 )))
{
Extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
Extern u32 pnp_bios_is_utter_crap;
Pnp_bios_is_utter_crap = 1;
Printk (kern_crit "pnpbios fault .. attempting recovery./N ");
_ ASM _ volatile (
"Movl % 0, % ESP/n/t"
"JMP * % 1/N/t"
: "G" (pnp_bios_fault_esp), "G" (pnp_bios_fault_eip ));
Panic ("do_trap: Can't hit this ");
}
# Endif
Fixup = search_exception_tables (regs-> EIP );
If (fixup ){
Regs-> EIP = fixup-> fixup;
Return 1;
}
Return 0;
}
Const struct exception_table_entry * search_exception_tables (unsigned long ADDR)
{
Const struct prediction_table_entry * E;
E = search_extable (_ start ___ ex_table, _ Stop ___ ex_table-1, ADDR );
If (! E)
E = search_module_extables (ADDR );
Return E;
}
In the second case, the function dumps all the CPU registers and kernel state stacks to the console, outputs them to a system message buffer, and then calls the do_exit () function to kill the current process. This is the so-called "kernel oops" error named after the displayed message. I will not talk about this piece of code. If you are a kernel expert, these output values can be used by the kernel programming experts to speculate the conditions that cause this error, and then discover and correct the error:
/*
* Oops. The kernel tried to access some bad page. We'll have
* Terminate things with extreme prejudice.
*/
Bust_spinlocks (1 );
If (oops_may_print ()){
# Ifdef config_x86_pae
If (error_code & 16 ){
Pte_t * PTE = lookup_address (Address );
If (PTE & pte_present (* PTE )&&! Pte_exec_kernel (* PTE ))
Printk (kern_crit "kernel tried to execute"
"NX-protected page-exploit attempt? "
"(UID: % d)/n", current-> UID );
}
# Endif
If (address <page_size)
Printk (kern_alert "bug: unable to handle kernel null"
"Pointer Dereference ");
Else
Printk (kern_alert "bug: unable to handle kernel paging"
"Request ");
Printk ("at virtual address % 08lx/N", address );
Printk (kern_alert "printing EIP:/N ");
Printk ("% 08lx/N", regs-> EIP );
}
Page = read_375 ();
Page = (unsigned long *) _ VA (page) [Address> 22];
If (oops_may_print ())
Printk (kern_alert "* PVDF = % 08lx/N", page );
/*
* We must not directly access the PTE in the highpte
* Case, the page table might be allocated in highmem.
* And lets rather not kmap-atomic the PTE, just in case
* It's allocated already.
*/
# Ifndef config_highpte
If (page & 1) & oops_may_print ()){
Page & = page_mask;
Address & = 0x003ff000;
Page = (unsigned long *) _ VA (page) [Address> page_shift];
Printk (kern_alert "* PTE = % 08lx/N", page );
}
# Endif
Tsk-> thread. Cr2 = address;
Tsk-> thread. trap_no = 14;
Tsk-> thread. error_code = error_code;
Die ("oops", regs, error_code );
Bust_spinlocks (0 );
Do_exit (sigkill );