- pre1: (for ISDN synchronization _ONLY_! Not complete!)
[davej-history.git] / kernel / ptrace.c
blob410f9de937bcce2d92ca747eb9a0590bc4192f07
1 /*
2 * linux/kernel/ptrace.c
4 * (C) Copyright 1999 Linus Torvalds
6 * Common interfaces for "ptrace()" which we do not want
7 * to continually duplicate across every architecture.
8 */
10 #include <linux/sched.h>
11 #include <linux/errno.h>
12 #include <linux/mm.h>
13 #include <linux/highmem.h>
14 #include <linux/smp_lock.h>
16 #include <asm/pgtable.h>
17 #include <asm/uaccess.h>
20 * Access another process' address space, one page at a time.
22 static int access_one_page(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long addr, void *buf, int len, int write)
24 pgd_t * pgdir;
25 pmd_t * pgmiddle;
26 pte_t * pgtable;
27 char *maddr;
28 struct page *page;
30 repeat:
31 pgdir = pgd_offset(vma->vm_mm, addr);
32 if (pgd_none(*pgdir))
33 goto fault_in_page;
34 if (pgd_bad(*pgdir))
35 goto bad_pgd;
36 pgmiddle = pmd_offset(pgdir, addr);
37 if (pmd_none(*pgmiddle))
38 goto fault_in_page;
39 if (pmd_bad(*pgmiddle))
40 goto bad_pmd;
41 pgtable = pte_offset(pgmiddle, addr);
42 if (!pte_present(*pgtable))
43 goto fault_in_page;
44 if (write && (!pte_write(*pgtable) || !pte_dirty(*pgtable)))
45 goto fault_in_page;
46 page = pte_page(*pgtable);
48 /* ZERO_PAGE is special: reads from it are ok even though it's marked reserved */
49 if (page != ZERO_PAGE(addr) || write) {
50 if ((!VALID_PAGE(page)) || PageReserved(page))
51 return 0;
53 flush_cache_page(vma, addr);
55 if (write) {
56 maddr = kmap(page);
57 memcpy(maddr + (addr & ~PAGE_MASK), buf, len);
58 flush_page_to_ram(page);
59 flush_icache_page(vma, page);
60 kunmap(page);
61 } else {
62 maddr = kmap(page);
63 memcpy(buf, maddr + (addr & ~PAGE_MASK), len);
64 flush_page_to_ram(page);
65 kunmap(page);
67 return len;
69 fault_in_page:
70 /* -1: out of memory. 0 - unmapped page */
71 if (handle_mm_fault(mm, vma, addr, write) > 0)
72 goto repeat;
73 return 0;
75 bad_pgd:
76 pgd_ERROR(*pgdir);
77 return 0;
79 bad_pmd:
80 pmd_ERROR(*pgmiddle);
81 return 0;
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;
88 for (;;) {
89 unsigned long offset = addr & ~PAGE_MASK;
90 int this_len = PAGE_SIZE - offset;
91 int retval;
93 if (this_len > len)
94 this_len = len;
95 retval = access_one_page(mm, vma, addr, buf, this_len, write);
96 copied += retval;
97 if (retval != this_len)
98 break;
100 len -= retval;
101 if (!len)
102 break;
104 addr += retval;
105 buf += retval;
107 if (addr < vma->vm_end)
108 continue;
109 if (!vma->vm_next)
110 break;
111 if (vma->vm_next->vm_start != vma->vm_end)
112 break;
114 vma = vma->vm_next;
116 return copied;
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;
125 /* Worry about races with exit() */
126 task_lock(tsk);
127 mm = tsk->mm;
128 if (mm)
129 atomic_inc(&mm->mm_users);
130 task_unlock(tsk);
131 if (!mm)
132 return 0;
134 down(&mm->mmap_sem);
135 vma = find_extend_vma(mm, addr);
136 copied = 0;
137 if (vma)
138 copied = access_mm(mm, vma, addr, buf, len, write);
140 up(&mm->mmap_sem);
141 mmput(mm);
142 return copied;
145 int ptrace_readdata(struct task_struct *tsk, unsigned long src, char *dst, int len)
147 int copied = 0;
149 while (len > 0) {
150 char buf[128];
151 int this_len, retval;
153 this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
154 retval = access_process_vm(tsk, src, buf, this_len, 0);
155 if (!retval) {
156 if (copied)
157 break;
158 return -EIO;
160 if (copy_to_user(dst, buf, retval))
161 return -EFAULT;
162 copied += retval;
163 src += retval;
164 dst += retval;
165 len -= retval;
167 return copied;
170 int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long dst, int len)
172 int copied = 0;
174 while (len > 0) {
175 char buf[128];
176 int this_len, retval;
178 this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
179 if (copy_from_user(buf, src, this_len))
180 return -EFAULT;
181 retval = access_process_vm(tsk, dst, buf, this_len, 1);
182 if (!retval) {
183 if (copied)
184 break;
185 return -EIO;
187 copied += retval;
188 src += retval;
189 dst += retval;
190 len -= retval;
192 return copied;