Example #1
0
File: trap.c Project: ivucica/linux
/*
 * We give a *copy* of the faultinfo in the regs to segv.
 * This must be done, since nesting SEGVs could overwrite
 * the info in the regs. A pointer to the info then would
 * give us bad data!
 */
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc)
{
	struct siginfo si;
	void *catcher;
	int err;
        int is_write = FAULT_WRITE(fi);
        unsigned long address = FAULT_ADDRESS(fi);

        if(!is_user && (address >= start_vm) && (address < end_vm)){
                flush_tlb_kernel_vm();
                return(0);
        }
	else if(current->mm == NULL)
		panic("Segfault with no mm");

	if (SEGV_IS_FIXABLE(&fi) || SEGV_MAYBE_FIXABLE(&fi))
		err = handle_page_fault(address, ip, is_write, is_user, &si.si_code);
	else {
		err = -EFAULT;
		/* A thread accessed NULL, we get a fault, but CR2 is invalid.
		 * This code is used in __do_copy_from_user() of TT mode. */
		address = 0;
	}

	catcher = current->thread.fault_catcher;
	if(!err)
		return(0);
	else if(catcher != NULL){
		current->thread.fault_addr = (void *) address;
		do_longjmp(catcher, 1);
	}
	else if(current->thread.fault_addr != NULL)
		panic("fault_addr set but no fault catcher");
        else if(!is_user && arch_fixup(ip, sc))
		return(0);

 	if(!is_user)
		panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
		      address, ip);

	if (err == -EACCES) {
		si.si_signo = SIGBUS;
		si.si_errno = 0;
		si.si_code = BUS_ADRERR;
		si.si_addr = (void __user *)address;
                current->thread.arch.faultinfo = fi;
		force_sig_info(SIGBUS, &si, current);
	} else if (err == -ENOMEM) {
		printk("VM: killing process %s\n", current->comm);
		do_exit(SIGKILL);
	} else {
		BUG_ON(err != -EFAULT);
		si.si_signo = SIGSEGV;
		si.si_addr = (void __user *) address;
                current->thread.arch.faultinfo = fi;
		force_sig_info(SIGSEGV, &si, current);
	}
	return(0);
}
Example #2
0
/*
 * We give a *copy* of the faultinfo in the regs to segv.
 * This must be done, since nesting SEGVs could overwrite
 * the info in the regs. A pointer to the info then would
 * give us bad data!
 */
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
                   struct uml_pt_regs *regs)
{
    struct siginfo si;
    jmp_buf *catcher;
    int err;
    int is_write = FAULT_WRITE(fi);
    unsigned long address = FAULT_ADDRESS(fi);

    if (!is_user && (address >= start_vm) && (address < end_vm)) {
        flush_tlb_kernel_vm();
        return 0;
    }
    else if (current->mm == NULL) {
        show_regs(container_of(regs, struct pt_regs, regs));
        panic("Segfault with no mm");
    }

    if (SEGV_IS_FIXABLE(&fi) || SEGV_MAYBE_FIXABLE(&fi))
        err = handle_page_fault(address, ip, is_write, is_user,
                                &si.si_code);
    else {
        err = -EFAULT;
        /*
         * A thread accessed NULL, we get a fault, but CR2 is invalid.
         * This code is used in __do_copy_from_user() of TT mode.
         * XXX tt mode is gone, so maybe this isn't needed any more
         */
        address = 0;
    }

    catcher = current->thread.fault_catcher;
    if (!err)
        return 0;
    else if (catcher != NULL) {
        current->thread.fault_addr = (void *) address;
        UML_LONGJMP(catcher, 1);
    }
    else if (current->thread.fault_addr != NULL)
        panic("fault_addr set but no fault catcher");
    else if (!is_user && arch_fixup(ip, regs))
        return 0;

    if (!is_user) {
        show_regs(container_of(regs, struct pt_regs, regs));
        panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
              address, ip);
    }

    if (err == -EACCES) {
        si.si_signo = SIGBUS;
        si.si_errno = 0;
        si.si_code = BUS_ADRERR;
        si.si_addr = (void __user *)address;
        current->thread.arch.faultinfo = fi;
        force_sig_info(SIGBUS, &si, current);
    } else if (err == -ENOMEM) {
        printk(KERN_INFO "VM: killing process %s\n", current->comm);
        do_exit(SIGKILL);
    } else {
        BUG_ON(err != -EFAULT);
        si.si_signo = SIGSEGV;
        si.si_addr = (void __user *) address;
        current->thread.arch.faultinfo = fi;
        force_sig_info(SIGSEGV, &si, current);
    }
    return 0;
}