Commit 8133aaa1 authored by Jeff Dike's avatar Jeff Dike

A number of small fixes for the uaccess merge.

parent 3817da13
......@@ -33,9 +33,8 @@ extern int do_signal(int error);
extern int is_stack_fault(unsigned long sp);
extern unsigned long segv(unsigned long address, unsigned long ip,
int is_write, int is_user, void *sc);
extern unsigned long handle_page_fault(unsigned long address, unsigned long ip,
int is_write, int is_user,
int *code_out);
extern int handle_page_fault(unsigned long address, unsigned long ip,
int is_write, int is_user, int *code_out);
extern void syscall_ready(void);
extern void set_tracing(void *t, int tracing);
extern int is_tracing(void *task);
......
......@@ -54,7 +54,7 @@ static inline int clear_user(void *mem, int len)
return(CHOOSE_MODE_PROC(clear_user_tt, clear_user_skas, mem, len));
}
static inline int strnlen_user(void *str, int len)
static inline int strnlen_user(const void *str, int len)
{
return(CHOOSE_MODE_PROC(strnlen_user_tt, strnlen_user_skas, str, len));
}
......
......@@ -10,7 +10,7 @@ obj-y = config.o exec_kern.o exitcode.o frame_kern.o frame.o helper.o \
process_kern.o ptrace.o reboot.o resource.o sigio_user.o \
sigio_kern.o signal_kern.o signal_user.o smp.o syscall_kern.o \
syscall_user.o sysrq.o sys_call_table.o tempfile.o time.o \
time_kern.o tlb.o trap_kern.o trap_user.o uaccess_user.o um_arch.o \
time_kern.o tlb.o trap_kern.o trap_user.o um_arch.o \
umid.o user_util.o
obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o
......
......@@ -222,21 +222,25 @@ int page_mask(void)
return(PAGE_MASK);
}
unsigned long um_virt_to_phys(void *t, unsigned long addr)
void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
pte_t *pte_out)
{
struct task_struct *task;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
task = t;
if(task->mm == NULL) return(0xffffffff);
if(task->mm == NULL)
return(ERR_PTR(-EINVAL));
pgd = pgd_offset(task->mm, addr);
pmd = pmd_offset(pgd, addr);
if(!pmd_present(*pmd)) return(0xffffffff);
if(!pmd_present(*pmd))
return(ERR_PTR(-EINVAL));
pte = pte_offset_kernel(pmd, addr);
if(!pte_present(*pte)) return(0xffffffff);
return((pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK));
if(!pte_present(*pte))
return(ERR_PTR(-EINVAL));
if(pte_out != NULL)
*pte_out = *pte;
return((void *) (pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK));
}
char *current_cmd(void)
......@@ -244,8 +248,8 @@ char *current_cmd(void)
#if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM)
return("(Unknown)");
#else
unsigned long addr = um_virt_to_phys(current, current->mm->arg_start);
return addr == 0xffffffff? "(Unknown)": __va(addr);
void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL);
return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr);
#endif
}
......
......@@ -26,10 +26,6 @@ static inline int verify_area_skas(int type, const void * addr,
return(access_ok_skas(type, addr, size) ? 0 : -EFAULT);
}
extern unsigned long handle_page_fault(unsigned long address,
unsigned long ip, int is_write,
int is_user, int *code_out);
extern void *um_virt_to_phys(struct task_struct *task, unsigned long virt,
pte_t *pte_out);
......@@ -212,7 +208,7 @@ static inline int strnlen_chunk(unsigned long str, int len, void *arg)
return(0);
}
static inline int strnlen_user_skas(void *str, int len)
static inline int strnlen_user_skas(const void *str, int len)
{
int count = 0, n;
......
......@@ -4,6 +4,7 @@
*/
#include "linux/kernel.h"
#include "asm/errno.h"
#include "linux/sched.h"
#include "linux/mm.h"
#include "linux/spinlock.h"
......@@ -19,41 +20,36 @@
#include "kern_util.h"
#include "kern.h"
#include "chan_kern.h"
#include "debug.h"
#include "mconsole_kern.h"
#include "2_5compat.h"
extern int nsyscalls;
unsigned long segv(unsigned long address, unsigned long ip, int is_write,
int is_user, void *sc)
int handle_page_fault(unsigned long address, unsigned long ip,
int is_write, int is_user, int *code_out)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
struct siginfo si;
void *catcher;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
unsigned long page;
int err = -EFAULT;
if((address >= start_vm) && (address < end_vm)){
flush_tlb_kernel_vm();
return(0);
}
if(mm == NULL) panic("Segfault with no mm");
catcher = current->thread.fault_catcher;
si.si_code = SEGV_MAPERR;
*code_out = SEGV_MAPERR;
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if(!vma) goto bad;
else if(vma->vm_start <= address) goto good_area;
else if(!(vma->vm_flags & VM_GROWSDOWN)) goto bad;
else if(expand_stack(vma, address)) goto bad;
if(!vma)
goto out;
else if(vma->vm_start <= address)
goto good_area;
else if(!(vma->vm_flags & VM_GROWSDOWN))
goto out;
else if(expand_stack(vma, address))
goto out;
good_area:
si.si_code = SEGV_ACCERR;
if(is_write && !(vma->vm_flags & VM_WRITE)) goto bad;
*code_out = SEGV_ACCERR;
if(is_write && !(vma->vm_flags & VM_WRITE))
goto out;
page = address & PAGE_MASK;
if(page == (unsigned long) current->thread_info + PAGE_SIZE)
panic("Kernel stack overflow");
......@@ -69,8 +65,10 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
current->maj_flt++;
break;
case VM_FAULT_SIGBUS:
goto do_sigbus;
err = -EACCES;
goto out;
case VM_FAULT_OOM:
err = -ENOMEM;
goto out_of_memory;
default:
BUG();
......@@ -80,43 +78,45 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
*pte = pte_mkyoung(*pte);
if(pte_write(*pte)) *pte = pte_mkdirty(*pte);
flush_tlb_page(vma, page);
err = 0;
out:
up_read(&mm->mmap_sem);
return(0);
do_sigbus:
up_read(&mm->mmap_sem);
return(err);
/*
* Send a sigbus, regardless of whether we were in kernel
* or user mode.
*/
si.si_signo = SIGBUS;
si.si_errno = 0;
si.si_code = BUS_ADRERR;
si.si_addr = (void *)address;
force_sig_info(SIGBUS, &si, current);
if(!is_user) goto bad;
return(0);
/*
* We ran out of memory, or some other thing happened to us that made
* us unable to handle the page fault gracefully.
*/
out_of_memory:
up_read(&mm->mmap_sem);
if (current->pid == 1) {
up_read(&mm->mmap_sem);
yield();
down_read(&mm->mmap_sem);
goto survive;
}
printk("VM: killing process %s\n", current->comm);
if(is_user)
do_exit(SIGKILL);
err = -ENOMEM;
goto out;
}
unsigned long segv(unsigned long address, unsigned long ip, int is_write,
int is_user, void *sc)
{
struct siginfo si;
void *catcher;
int err;
/* Fall through to bad */
if(!is_user && (address >= start_vm) && (address < end_vm)){
flush_tlb_kernel_vm();
return(0);
}
if(current->mm == NULL) panic("Segfault with no mm");
err = handle_page_fault(address, ip, is_write, is_user, &si.si_code);
bad:
if(catcher != NULL){
catcher = current->thread.fault_catcher;
if(!err)
return(0);
else if(catcher != NULL){
current->thread.fault_addr = (void *) address;
up_read(&mm->mmap_sem);
do_longjmp(catcher, 1);
}
else if(current->thread.fault_addr != NULL){
......@@ -125,15 +125,28 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
else if(arch_fixup(ip, sc))
return(0);
if(!is_user)
if(!is_user)
panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
address, ip);
si.si_signo = SIGSEGV;
si.si_addr = (void *) address;
current->thread.cr2 = address;
current->thread.err = is_write;
force_sig_info(SIGSEGV, &si, current);
up_read(&mm->mmap_sem);
if(err == -EACCES){
si.si_signo = SIGBUS;
si.si_errno = 0;
si.si_code = BUS_ADRERR;
si.si_addr = (void *)address;
force_sig_info(SIGBUS, &si, current);
}
else if(err == -ENOMEM){
printk("VM: killing process %s\n", current->comm);
do_exit(SIGKILL);
}
else {
si.si_signo = SIGSEGV;
si.si_addr = (void *) address;
current->thread.cr2 = address;
current->thread.err = is_write;
force_sig_info(SIGSEGV, &si, current);
}
return(0);
}
......
......@@ -98,7 +98,7 @@ static inline int clear_user_tt(void *mem, int len)
extern int __do_strnlen_user(const char *str, unsigned long n,
void **fault_addr, void **fault_catcher);
static inline int strnlen_user_tt(void *str, int len)
static inline int strnlen_user_tt(const void *str, int len)
{
return(__do_strnlen_user(str, len,
&current->thread.fault_addr,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment