BSD 4_4 release
[unix-history] / usr / src / old / adb / common_source / access.c
/*-
* Copyright (c) 1991 The Regents of the University of California.
* All rights reserved.
*
* This module is believed to contain source code proprietary to AT&T.
* Use and redistribution is subject to the Berkeley Software License
* Agreement and your Software Agreement with AT&T (Western Electric).
*/
#ifndef lint
static char sccsid[] = "@(#)access.c 5.4 (Berkeley) 4/4/91";
#endif /* not lint */
/*
* Adb: access data in file/process address space.
*/
#include "defs.h"
#include <sys/file.h>
#include <sys/ptrace.h>
off_t lseek();
/*
* Read or write from or to the given address, accessing or altering
* only the given byte(s). Return the number of bytes transferred.
* Remote (debuggee) addresses are specified as a <space,address> pair.
* Neither the remote nor the local address need be aligned.
*
* If there is a current process, ask the system to do this (via ptrace
* [ick]). If debugging the kernel, use vtophys() to map virtual to
* physical locations (in a system-dependent manner). Otherwise we
* can just read or write the files being debugged directly.
*/
int
adbio(rw, space, rmtaddr, localaddr, cnt)
enum rwmode rw;
int space;
addr_t rmtaddr;
caddr_t localaddr;
int cnt;
{
register int ret;
register struct map *mp;
struct m1 *mm;
static char *derr = "data address not found";
static char *terr = "text address not found";
#define rwerr() errflag = space & SP_DATA ? derr : terr
#define within(which) (rmtaddr >= which.b && rmtaddr < which.e)
if (space == SP_NONE) {
/* The no-space is all zero. */
bzero(localaddr, cnt);
return (cnt);
}
if (pid) {
ret = io_ptrace(rw, space, rmtaddr, localaddr, cnt);
if (ret != cnt)
rwerr();
return (ret);
}
if (rw == RWMODE_WRITE && !wtflag)
error("not in write mode");
mp = space & SP_DATA ? &datmap : &txtmap;
if ((space & SP_STAR) == 0 && within(mp->m1))
mm = &mp->m1;
else if (within(mp->m2))
mm = &mp->m2;
else {
rwerr();
return (0);
}
rmtaddr += mm->f - mm->b;
if (kernel && space == SP_DATA) {
char *err = NULL;
rmtaddr = vtophys(rmtaddr, &err);
if (err) {
errflag = err;
return (0);
}
}
if (lseek(mp->ufd, (off_t)rmtaddr, 0) == -1) {
rwerr();
return (0);
}
if (rw == RWMODE_READ) {
ret = read(mp->ufd, localaddr, cnt);
/* gratuitously supply extra zeroes at end of file */
if (ret > 0 && ret < cnt) {
bzero(localaddr + ret, cnt - ret);
ret = cnt;
}
} else
ret = write(mp->ufd, localaddr, cnt);
if (ret != cnt)
rwerr();
return (ret);
#undef rwerr
#undef within
}
/*
* Read a single object of length `len' from the core file at the
* given offset. Return the length read. (This routine allows vtophys
* and kernel crash startup code to read ptes, etc.)
*/
int
readcore(off, addr, len)
off_t off;
caddr_t addr;
int len;
{
if (lseek(corefile.fd, off, L_SET) == -1)
return (-1);
return (read(corefile.fd, addr, len));
}
/*
* THE FOLLOWING IS GROSS. WE SHOULD REPLACE PTRACE WITH SPECIAL
* FILES A LA /proc.
*
* Read or write using ptrace. io_ptrace arranges that the
* addresses passed to ptrace are an even multiple of sizeof(int),
* and is able to read or write single bytes.
*
* Since ptrace is so horribly slow, and some commands do repeated
* reading of units smaller than an `int', io_ptrace calls cptrace
* (cached ptrace) to allow some cacheing. cptrace also converts a
* read/write op and a space into a ptrace op, and returns 0 on success
* and hence takes a pointer to the value cell rather than the value.
*/
struct cache {
short rop, wop; /* ptrace ops for read and write */
int valid; /* true iff cache entry valid */
int *addr; /* address of cached value */
int val; /* and the value */
};
static struct cache icache = { PT_READ_I, PT_WRITE_I };
static struct cache dcache = { PT_READ_D, PT_WRITE_D };
/*
* Invalidate one or both caches.
* This is the only function that accepts two spaces simultaneously.
*/
cacheinval(space)
int space;
{
if (space & SP_INSTR)
icache.valid = 0;
if (space & SP_DATA)
dcache.valid = 0;
}
int cachehit, cachemiss; /* statistics */
static int
cptrace(rw, space, p, addr, val)
enum rwmode rw;
int space, p, *addr, *val;
{
register struct cache *c = space & SP_DATA ? &dcache : &icache;
int v;
if (rw == RWMODE_READ) {
if (c->valid && c->addr == addr) {
cachehit++;
*val = c->val;
return (0);
}
cachemiss++;
errno = 0;
if ((v = ptrace(c->rop, p, addr, 0)) == -1 && errno)
return (-1);
*val = v;
} else {
c->valid = 0; /* paranoia */
errno = 0;
if (ptrace(c->wop, p, addr, v = *val) == -1 && errno)
return (-1);
}
c->valid = 1;
c->addr = addr;
c->val = v;
return (0);
}
int
io_ptrace(rw, space, rmtaddr, localaddr, cnt)
register enum rwmode rw;
register int space;
addr_t rmtaddr;
register caddr_t localaddr;
register int cnt;
{
register addr_t addr;
register int nbytes, ret = 0, off;
int tmp;
/*
* Start by aligning rmtaddr; set nbytes to the number of bytes of
* useful data we shall obtain.
*/
off = rmtaddr % sizeof(int); /* addr_t is unsigned */
addr = rmtaddr - off;
nbytes = sizeof(int) - off;
while (cnt != 0) {
if (cnt < nbytes)
nbytes = cnt;
if (rw == RWMODE_READ) {
if (cptrace(rw, space, pid, (int *)addr, &tmp))
return (ret);
bcopy((caddr_t)&tmp + off, localaddr, nbytes);
} else {
if (nbytes < sizeof(int) &&
cptrace(RWMODE_READ, space, pid, (int *)addr, &tmp))
return (ret);
bcopy(localaddr, (caddr_t)&tmp + off, nbytes);
if (cptrace(rw, space, pid, (int *)addr, &tmp))
return (ret);
}
addr += sizeof(int);
localaddr += nbytes;
ret += nbytes;
cnt -= nbytes;
/*
* For the rest of the loop, the offset is 0 and we can
* use all the bytes obtained.
*/
off = 0;
nbytes = sizeof(int);
}
return (ret);
}