xref: /original-bsd/old/adb/common_source/access.c (revision a76afa45)
1 #ifndef lint
2 static char sccsid[] = "@(#)access.c	5.2 (Berkeley) 02/04/89";
3 #endif
4 
5 /*
6  * Adb: access data in file/process address space.
7  */
8 
9 #include "defs.h"
10 #include <sys/file.h>
11 #include <sys/ptrace.h>
12 
13 off_t	lseek();
14 
15 /*
16  * Read or write from or to the given address, accessing or altering
17  * only the given byte(s).  Return the number of bytes transferred.
18  * Remote (debuggee) addresses are specified as a <space,address> pair.
19  * Neither the remote nor the local address need be aligned.
20  *
21  * If there is a current process, ask the system to do this (via ptrace
22  * [ick]).  If debugging the kernel, use vtophys() to map virtual to
23  * physical locations (in a system-dependent manner).  Otherwise we
24  * can just read or write the files being debugged directly.
25  */
26 int
27 adbio(rw, space, rmtaddr, localaddr, cnt)
28 	enum rwmode rw;
29 	int space;
30 	addr_t rmtaddr;
31 	caddr_t localaddr;
32 	int cnt;
33 {
34 	register int ret;
35 	register struct map *mp;
36 	struct m1 *mm;
37 
38 	static char derr[] = "data address not found";
39 	static char terr[] = "text address not found";
40 #define rwerr() errflag = space & SP_DATA ? derr : terr
41 #define	within(which) (rmtaddr >= which.b && rmtaddr < which.e)
42 
43 	if (space == SP_NONE) {
44 		/* The no-space is all zero. */
45 		bzero(localaddr, cnt);
46 		return (cnt);
47 	}
48 	if (pid) {
49 		ret = io_ptrace(rw, space, rmtaddr, localaddr, cnt);
50 		if (ret != cnt)
51 			rwerr();
52 		return (ret);
53 	}
54 	if (rw == RWMODE_WRITE && !wtflag)
55 		error("not in write mode");
56 	mp = space & SP_DATA ? &datmap : &txtmap;
57 	if ((space & SP_STAR) == 0 && within(mp->m1))
58 		mm = &mp->m1;
59 	else if (within(mp->m2))
60 		mm = &mp->m2;
61 	else {
62 		rwerr();
63 		return (0);
64 	}
65 	rmtaddr += mm->f - mm->b;
66 	if (kernel && space == SP_DATA) {
67 		char *err = NULL;
68 
69 		rmtaddr = vtophys(rmtaddr, &err);
70 		if (err) {
71 			errflag = err;
72 			return (0);
73 		}
74 	}
75 	if (lseek(mp->ufd, (off_t)rmtaddr, 0) == -1) {
76 		rwerr();
77 		return (0);
78 	}
79 	if (rw == RWMODE_READ) {
80 		ret = read(mp->ufd, localaddr, cnt);
81 		/* gratuitously supply extra zeroes at end of file */
82 		if (ret > 0 && ret < cnt) {
83 			bzero(localaddr + ret, cnt - ret);
84 			ret = cnt;
85 		}
86 	} else
87 		ret = write(mp->ufd, localaddr, cnt);
88 	if (ret != cnt)
89 		rwerr();
90 	return (ret);
91 #undef rwerr
92 #undef within
93 }
94 
95 /*
96  * Read a single object of length `len' from the core file at the
97  * given offset.  Return the length read.  (This routine allows vtophys
98  * and kernel crash startup code to read ptes, etc.)
99  */
100 int
101 readcore(off, addr, len)
102 	off_t off;
103 	caddr_t addr;
104 	int len;
105 {
106 
107 	if (lseek(corefile.fd, off, L_SET) == -1)
108 		return (-1);
109 	return (read(corefile.fd, addr, len));
110 }
111 
112 /*
113  * THE FOLLOWING IS GROSS.  WE SHOULD REPLACE PTRACE WITH SPECIAL
114  * FILES A LA /proc.
115  *
116  * Read or write using ptrace.  io_ptrace arranges that the
117  * addresses passed to ptrace are an even multiple of sizeof(int),
118  * and is able to read or write single bytes.
119  *
120  * Since ptrace is so horribly slow, and some commands do repeated
121  * reading of units smaller than an `int', io_ptrace calls cptrace
122  * (cached ptrace) to allow some cacheing.  cptrace also converts a
123  * read/write op and a space into a ptrace op, and returns 0 on success
124  * and hence takes a pointer to the value cell rather than the value.
125  */
126 struct cache {
127 	short	rop, wop;		/* ptrace ops for read and write */
128 	int	valid;			/* true iff cache entry valid */
129 	int	*addr;			/* address of cached value */
130 	int	val;			/* and the value */
131 };
132 static struct cache icache = { PT_READ_I, PT_WRITE_I };
133 static struct cache dcache = { PT_READ_D, PT_WRITE_D };
134 
135 /*
136  * Invalidate one or both caches.
137  * This is the only function that accepts two spaces simultaneously.
138  */
139 cacheinval(space)
140 	int space;
141 {
142 
143 	if (space & SP_INSTR)
144 		icache.valid = 0;
145 	if (space & SP_DATA)
146 		dcache.valid = 0;
147 }
148 
149 int	cachehit, cachemiss;		/* statistics */
150 
151 static int
152 cptrace(rw, space, p, addr, val)
153 	enum rwmode rw;
154 	int space, p, *addr, *val;
155 {
156 	register struct cache *c = space & SP_DATA ? &dcache : &icache;
157 	int v;
158 
159 	if (rw == RWMODE_READ) {
160 		if (c->valid && c->addr == addr) {
161 			cachehit++;
162 			*val = c->val;
163 			return (0);
164 		}
165 		cachemiss++;
166 		errno = 0;
167 		if ((v = ptrace(c->rop, p, addr, 0)) == -1 && errno)
168 			return (-1);
169 		*val = v;
170 	} else {
171 		c->valid = 0;		/* paranoia */
172 		errno = 0;
173 		if (ptrace(c->wop, p, addr, v = *val) == -1 && errno)
174 			return (-1);
175 	}
176 	c->valid = 1;
177 	c->addr = addr;
178 	c->val = v;
179 	return (0);
180 }
181 
182 int
183 io_ptrace(rw, space, rmtaddr, localaddr, cnt)
184 	register enum rwmode rw;
185 	register int space;
186 	addr_t rmtaddr;
187 	register caddr_t localaddr;
188 	register int cnt;
189 {
190 	register addr_t addr;
191 	register int nbytes, ret = 0, off;
192 	int tmp;
193 
194 	/*
195 	 * Start by aligning rmtaddr; set nbytes to the number of bytes of
196 	 * useful data we shall obtain.
197 	 */
198 	off = rmtaddr % sizeof(int);	/* addr_t is unsigned */
199 	addr = rmtaddr - off;
200 	nbytes = sizeof(int) - off;
201 	while (cnt != 0) {
202 		if (cnt < nbytes)
203 			nbytes = cnt;
204 		if (rw == RWMODE_READ) {
205 			if (cptrace(rw, space, pid, (int *)addr, &tmp))
206 				return (ret);
207 			bcopy((caddr_t)&tmp + off, localaddr, nbytes);
208 		} else {
209 			if (nbytes < sizeof(int) &&
210 			    cptrace(RWMODE_READ, space, pid, (int *)addr, &tmp))
211 				return (ret);
212 			bcopy(localaddr, (caddr_t)&tmp + off, nbytes);
213 			if (cptrace(rw, space, pid, (int *)addr, &tmp))
214 				return (ret);
215 		}
216 		addr += sizeof(int);
217 		localaddr += nbytes;
218 		ret += nbytes;
219 		cnt -= nbytes;
220 		/*
221 		 * For the rest of the loop, the offset is 0 and we can
222 		 * use all the bytes obtained.
223 		 */
224 		off = 0;
225 		nbytes = sizeof(int);
226 	}
227 	return (ret);
228 }
229