/*
* linux/kernel/ptrace.c
*
* (C) Copyright 1999 Linus Torvalds
*
* Common interfaces for "ptrace()" which we do not want
* to continually duplicate across every architecture.
*/
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/smp_lock.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
/*
* Access another process' address space, one page at a time.
*/
static int access_one_page(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long addr, void *buf, int len, int write)
{
pgd_t * pgdir;
pmd_t * pgmiddle;
pte_t * pgtable;
char *maddr;
struct page *page;
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (pgd_none(*pgdir))
goto fault_in_page;
if (pgd_bad(*pgdir))
goto bad_pgd;
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle))
goto fault_in_page;
if (pmd_bad(*pgmiddle))
goto bad_pmd;
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable))
goto fault_in_page;
if (write && (!pte_write(*pgtable) || !pte_dirty(*pgtable)))
goto fault_in_page;
page = pte_page(*pgtable);
47
/* ZERO_PAGE is special: reads from it are ok even though it's marked reserved */
if (page != ZERO_PAGE(addr) || write) {
if ((!VALID_PAGE(page)) || PageReserved(page))
return 0;
}
53 flush_cache_page(vma, addr);
if (write) {
maddr = kmap(page);
memcpy(maddr + (addr & ~PAGE_MASK), buf, len);
flush_page_to_ram(page);
flush_icache_page(vma, page);
kunmap(page);
} else {
62 maddr = kmap(page);
memcpy(buf, maddr + (addr & ~PAGE_MASK), len);
flush_page_to_ram(page);
kunmap(page);
}
return len;
fault_in_page:
70 /* -1: out of memory. 0 - unmapped page */
if (handle_mm_fault(mm, vma, addr, write) > 0)
goto repeat;
73 return 0;
bad_pgd:
76 pgd_ERROR(*pgdir);
77 return 0;
78
79 bad_pmd:
pmd_ERROR(*pgmiddle);
81 return 0;
82 }
83
84 static int access_mm(struct mm_struct *mm, struct vm_area_struct * vma, unsigned long addr, void *buf, int len, int write)
{
86 int copied = 0;
87
88 for (;;) {
89 unsigned long offset = addr & ~PAGE_MASK;
90 int this_len = PAGE_SIZE - offset;
int retval;
92
93 if (this_len > len)
94 this_len = len;
95 retval = access_one_page(mm, vma, addr, buf, this_len, write);
96 copied += retval;
if (retval != this_len)
98 break;
99
len -= retval;
if (!len)
102 break;
104 addr += retval;
buf += retval;
107 if (addr < vma->vm_end)
continue;
if (!vma->vm_next)
110 break;
if (vma->vm_next->vm_start != vma->vm_end)
break;
vma = vma->vm_next;
115 }
116 return copied;
}
118
119 int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write)
{
121 int copied;
122 struct mm_struct *mm;
123 struct vm_area_struct * vma;
124
125 /* Worry about races with exit() */
task_lock(tsk);
mm = tsk->mm;
128 if (mm)
129 atomic_inc(&mm->mm_users);
task_unlock(tsk);
if (!mm)
return 0;
134 down(&mm->mmap_sem);
vma = find_extend_vma(mm, addr);
copied = 0;
137 if (vma)
copied = access_mm(mm, vma, addr, buf, len, write);
up(&mm->mmap_sem);
mmput(mm);
return copied;
143 }
145 int ptrace_readdata(struct task_struct *tsk, unsigned long src, char *dst, int len)
{
147 int copied = 0;
148
while (len > 0) {
char buf[128];
int this_len, retval;
152
this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
retval = access_process_vm(tsk, src, buf, this_len, 0);
if (!retval) {
if (copied)
157 break;
return -EIO;
159 }
160 if (copy_to_user(dst, buf, retval))
161 return -EFAULT;
copied += retval;
src += retval;
164 dst += retval;
165 len -= retval;
}
167 return copied;
168 }
169
int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long dst, int len)
{
int copied = 0;
while (len > 0) {
char buf[128];
int this_len, retval;
177
178 this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
if (copy_from_user(buf, src, this_len))
180 return -EFAULT;
181 retval = access_process_vm(tsk, dst, buf, this_len, 1);
if (!retval) {
if (copied)
184 break;
185 return -EIO;
186 }
copied += retval;
188 src += retval;
dst += retval;
190 len -= retval;
}
return copied;
}