2021-10-11 18:37:13 -03:00

1294 lines
29 KiB
C

#ifndef lint
static char sccsid[] = "@(#)kvmgetcmd.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
#include "kvm.h"
#include "kvm_impl.h"
#include <sys/param.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <machine/vmparam.h>
#include <machine/pte.h>
#include <machine/mmu.h>
#include <vm/as.h>
#include <vm/seg_vn.h>
#include <vm/seg_u.h>
#include <vm/seg_map.h>
#include <vm/anon.h>
#include <vm/page.h>
#include <vm/swap.h>
#ifdef sun4m
#include <machine/devaddr.h>
#endif sun4m
extern char *malloc();
static long page_to_physaddr();
static int vp_to_fdoffset();
/*
* VERSION FOR MACHINES WITH STACKS GROWING DOWNWARD IN MEMORY
*
* On program entry, the top of the stack frame looks like this:
*
* hi: |-----------------------|
* | 0 |
* |-----------------------|+
* | : | \
* | arg and env strings | > no more than NCARGS bytes
* | : | /
* |-----------------------|+
* | (char *)0 |
* |-----------------------|
* | ptrs to env strings |
* | : |
* |-----------------------|
* | (char *)0 |
* |-----------------------|
* | ptrs to arg strings |
* | (argc = # of ptrs) |
* | : |
* |-----------------------|
* | argc | <- sp
* low: |-----------------------|
*/
/* define a structure for describing an argument list */
typedef struct {
int cnt; /* number of strings */
u_long sp; /* user virtual addr of first string */
u_long se; /* user virtual addr of end of strings */
} argv_t;
static char **argcopy();
static int stkcopy();
static int getstkpg();
static int getseg();
static int readseg();
static struct page *pagefind();
int _swapread();
int _anonoffset();
/*
* Convert an array offset to a user virtual address.
* This is done by adding the array offset to the virtual address
* of the start of the current page (kd->stkpg counts pages from the
* top of the stack).
*/
#define Uvaddr(x) \
(USRSTACK - ((kd->stkpg + 1) * PAGESIZE) + ((char*)(x) - kd->sbuf))
/*
* reconstruct an argv-like argument list from the target process
*/
int
kvm_getcmd(kd, proc, u, arg, env)
kvm_t *kd;
struct proc *proc;
struct user *u;
char ***arg;
char ***env;
{
argv_t argd;
argv_t envd;
register char *cp;
register int eqseen, i;
if (proc->p_ssize == 0) /* if no stack, give up now */
return (-1);
/*
* Read the last stack page into kd->sbuf (allocating, if necessary).
* Then, from top of stack, find the end of the environment strings.
*/
if (getstkpg(kd, proc, u, 0) == -1)
return (-1);
/*
* Point to the last byte of the environment strings.
*/
cp = &kd->sbuf[PAGESIZE - NBPW - 1];
/*
* Skip backward over any zero bytes used to pad the string data
* to a word boundary.
*/
for (i = 0; i != NBPW && *--cp == '\0'; i++)
;
/*
* Initialize descriptors
*/
envd.cnt = 0;
envd.sp = envd.se = Uvaddr(cp + 1); /* this must point at '\0' */
argd = envd;
/*
* Now, working backwards, count the environment strings and the
* argument strings and look for the (int)0 that delimits the
* environment pointers.
*/
while (*(int *)((u_long)cp & ~(NBPW - 1)) != 0) {
eqseen = 0;
while (*cp != '\0') {
if (*cp-- == '=')
eqseen = 1;
if (cp < kd->sbuf) {
if (kd->stkpg * PAGESIZE > NCARGS ||
getstkpg(kd, proc, u, ++kd->stkpg) == -1)
return (-1);
else
cp = &kd->sbuf[PAGESIZE - 1];
}
}
if (eqseen && argd.cnt == 0) {
envd.cnt++;
envd.sp = Uvaddr(cp + 1);
} else {
argd.cnt++;
argd.sp = Uvaddr(cp + 1);
}
cp--;
}
if (envd.cnt != 0) {
argd.se = envd.sp - 1;
if (argd.cnt == 0)
argd.sp = argd.se;
}
if (envd.se - argd.sp > NCARGS - 1) {
#ifdef _KVM_DEBUG
_kvm_error("kvm_getcmd: could not locate arg pointers");
#endif _KVM_DEBUG
return (-1);
}
/* Copy back the (adjusted) vectors and strings */
if (arg != NULL) {
if ((*arg = argcopy(kd, proc, u, &argd)) == NULL)
return (-1);
}
if (env != NULL) {
if ((*env = argcopy(kd, proc, u, &envd)) == NULL) {
if (arg != NULL)
(void) free((char *)*arg);
return (-1);
}
}
return (0);
}
static char **
argcopy(kd, proc, u, arg)
kvm_t *kd;
struct proc *proc;
struct user *u;
argv_t *arg;
{
int pcnt;
int scnt;
register char **ptr;
register char **p;
char *str;
/* Step 1: allocate a buffer to hold all pointers and strings */
pcnt = (arg->cnt + 1) * sizeof (char *); /* #bytes in ptrs */
scnt = arg->se - arg->sp + 1; /* #bytes in strings */
ptr = (char **)malloc((u_int)scnt + pcnt);
if (ptr == NULL) {
#ifdef _KVM_DEBUG
_kvm_perror("argcopy");
#endif _KVM_DEBUG
return (NULL);
}
str = (char *)ptr + pcnt;
/* Step 2: copy the strings from user space to buffer */
if (stkcopy(kd, proc, u, arg->sp, str, scnt) == -1) {
(void) free((char *)ptr);
return (NULL);
}
if (str[scnt-1] != '\0') {
#ifdef _KVM_DEBUG
_kvm_error("argcopy: added NULL at end of strings");
#endif _KVM_DEBUG
str[scnt-1] = '\0';
}
/* Step 3: calculate the pointers */
for (p = ptr, pcnt = arg->cnt; pcnt-- > 0;) {
*p++ = str;
while (*str++ != '\0')
;
}
*p++ = NULL; /* NULL pointer at end */
if ((str - (char *)p) != (arg->cnt ? scnt : 0)) {
#ifdef _KVM_DEBUG
_kvm_error("argcopy: string pointer botch");
#endif _KVM_DEBUG
}
return (ptr);
}
/* XXX this and getstkpg should use new kvm_as_read instead of readseg */
/*
* Copy user stack into specified buffer
*/
static int
stkcopy(kd, proc, u, va, buf, cnt)
kvm_t *kd;
struct proc *proc;
struct user *u;
u_long va;
char *buf;
int cnt;
{
register int i = 0;
register u_int off;
register int pg;
register int c;
if ((USRSTACK - va) < cnt) {
#ifdef _KVM_DEBUG
_kvm_error("stkcopy: bad stack copy length %d", cnt);
#endif _KVM_DEBUG
return (-1);
}
off = va & PAGEOFFSET;
pg = (int) ((USRSTACK - (va + 1)) / PAGESIZE);
while (cnt > 0) {
if ((kd->stkpg != pg) && (getstkpg(kd, proc, u, pg) == -1))
return (-1);
c = MIN((PAGESIZE - off), cnt);
bcopy(&kd->sbuf[off], &buf[i], c);
i += c;
cnt -= c;
off = 0;
pg--;
}
return (0);
}
#ifdef _KVM_DEBUG
#define getkvm(a, b, m) \
if (kvm_read(kd, (u_long)(a), (caddr_t)(b), sizeof (*b)) \
!= sizeof (*b)) { \
_kvm_error("error reading %s", m); \
return (-1); \
}
#else !_KVM_DEBUG
#define getkvm(a, b, m) \
if (kvm_read(kd, (u_long)(a), (caddr_t)(b), sizeof (*b)) \
!= sizeof (*b)) { \
return (-1); \
}
#endif _KVM_DEBUG
/*
* read a user stack page into a holding area
*/
/*ARGSUSED*/
static int
getstkpg(kd, proc, u, pg)
kvm_t *kd;
struct proc *proc;
struct user *u;
int pg;
{
addr_t vaddr;
/* If no address segment ptr, this is a system process (e.g. biod) */
if (proc->p_as == NULL)
return (-1);
/* First time through, allocate a user stack cache */
if (kd->sbuf == NULL) {
kd->sbuf = malloc(PAGESIZE);
if (kd->sbuf == NULL) {
#ifdef _KVM_DEBUG
_kvm_perror("can't allocate stack cache");
#endif _KVM_DEBUG
return (-1);
}
}
/* If no seg struct for this process, get one for the 1st stack page */
if (kd->useg.s_as != proc->p_as) {
struct as uas;
getkvm(proc->p_as, &uas, "user address space descriptor");
getkvm(uas.a_segs, &kd->useg, "1st user segment descriptor");
getkvm(kd->useg.s_prev, &kd->useg, "stack segment descriptor");
}
kd->stkpg = pg;
/* Make sure we've got the right seg structure for this address */
vaddr = (addr_t)(USRSTACK - ((pg + 1) * PAGESIZE));
if (getseg(kd, &kd->useg, vaddr, &kd->useg) == -1)
return (-1);
if (kd->useg.s_as != proc->p_as) {
#ifdef _KVM_DEBUG
_kvm_error("wrong segment for user stack");
#endif _KVM_DEBUG
return (-1);
}
if (kd->useg.s_ops != kd->segvn) {
#ifdef _KVM_DEBUG
_kvm_error("user stack segment not segvn type");
#endif _KVM_DEBUG
return (-1);
}
/* Now go find and read the page */
if (readseg(kd, &kd->useg, vaddr, kd->sbuf, PAGESIZE)
!= PAGESIZE) {
#ifdef _KVM_DEBUG
_kvm_error("error reading stack page");
#endif _KVM_DEBUG
return (-1);
}
return (0);
}
/*
* getseg - given a seg structure, find the appropriate seg for a given address
* (nseg may be identical to oseg)
*/
static int
getseg(kd, oseg, addr, nseg)
kvm_t *kd;
struct seg *oseg;
addr_t addr;
struct seg *nseg;
{
struct seg *xseg;
if (addr < oseg->s_base) {
xseg = oseg->s_prev; /* watch out for circularity */
do {
getkvm(oseg->s_prev, nseg, "prev segment descriptor");
if (nseg->s_prev == xseg)
goto noseg;
oseg = nseg;
} while (addr < nseg->s_base);
if (addr >= (nseg->s_base + nseg->s_size))
goto noseg;
} else if (addr >= (oseg->s_base + oseg->s_size)) {
xseg = oseg->s_next; /* watch out for circularity */
do {
getkvm(oseg->s_next, nseg, "next segment descriptor");
if (nseg->s_next == xseg)
goto noseg;
oseg = nseg;
} while (addr >= nseg->s_base + nseg->s_size);
if (addr < nseg->s_base)
goto noseg;
} else if (nseg != oseg) {
*nseg = *oseg; /* copy if necessary */
}
return (0);
noseg:
#ifdef _KVM_DEBUG
_kvm_error("can't find segment for user address %x", addr);
#endif _KVM_DEBUG
return (-1);
}
/*
* readseg - read data described by a virtual address and seg structure.
* The data block must be entirely contained within seg.
* Readseg() returns the number of bytes read, or -1.
*/
static int
readseg(kd, seg, addr, buf, size)
kvm_t *kd;
struct seg *seg;
addr_t addr;
char *buf;
u_int size;
{
u_int count;
if ((addr + size) > (seg->s_base + seg->s_size)) {
#ifdef _KVM_DEBUG
_kvm_error("readseg: segment too small");
#endif _KVM_DEBUG
return (-1);
}
count = 0;
if (seg->s_ops == kd->segvn) {
/* Segvn segment */
struct segvn_data sdata;
struct anon_map amap;
struct anon **anp;
struct anon *ap;
struct anon anon;
u_int apsz;
u_int aoff;
u_int rsize;
/* get private data for segment */
if (seg->s_data == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("NULL segvn_data ptr in segment");
#endif _KVM_DEBUG
return (-1);
}
getkvm(seg->s_data, &sdata, "segvn_data");
/* Null vnode indicates anonymous page */
if (sdata.vp != NULL) {
#ifdef _KVM_DEBUG
_kvm_error("non-NULL vp in segvn_data");
#endif _KVM_DEBUG
return (-1);
}
if (sdata.amp == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("NULL anon_map ptr in segvn_data");
#endif _KVM_DEBUG
return (-1);
}
/* get anon_map structure */
getkvm(sdata.amp, &amap, "anon_map");
if (amap.anon == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("anon_map has NULL ptr");
#endif _KVM_DEBUG
return (-1);
}
apsz = (amap.size >> PAGESHIFT) * sizeof (struct anon *);
if (apsz == 0) {
#ifdef _KVM_DEBUG
_kvm_error("anon_map has zero size");
#endif _KVM_DEBUG
return (-1);
}
if ((anp = (struct anon **)malloc(apsz)) == NULL) {
#ifdef _KVM_DEBUG
_kvm_perror("can't allocate anon pointer array");
#endif _KVM_DEBUG
return (-1);
}
/* read anon pointer array */
if (kvm_read(kd, (u_long)amap.anon, (char *)anp, apsz) != apsz){
#ifdef _KVM_DEBUG
_kvm_error("error reading anon ptr array");
#endif _KVM_DEBUG
free((char *)anp);
return (-1);
}
/* since data may cross page boundaries, break up request */
while (count < size) {
struct page *paddr;
struct page page;
int swapoff;
struct vnode *vp;
u_int vpoff;
long skaddr;
struct page *pages;
u_int pages_base;
aoff = (long)addr & PAGEOFFSET;
rsize = MIN((PAGESIZE - aoff), (size - count));
/* index into anon ptrs to find the right one */
ap = anp[sdata.anon_index +
((addr - seg->s_base)>>PAGESHIFT)];
if (ap == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("NULL anon ptr");
#endif _KVM_DEBUG
break;
}
if ((swapoff = _anonoffset(kd, ap, &vp, &vpoff)) == -1) {
break;
}
getkvm(ap, &anon, "anon structure");
/*
* If there is a page structure pointer,
* make sure it is valid. If not, try the
* hash table in case the page is free but
* reclaimable.
*/
paddr = anon.un.an_page;
if (paddr != NULL) {
getkvm(paddr, &page, "anon page structure");
if ((page.p_vnode == vp) &&
(vpoff == page.p_offset)) {
goto gotpage;
}
#ifdef _KVM_DEBUG
_kvm_error("anon page struct invalid");
#endif _KVM_DEBUG
}
/* try hash table in case page is still around */
paddr = pagefind(kd, &page, vp, vpoff);
if (paddr == NULL)
goto tryswap;
gotpage:
/* make sure the page structure is useful */
if (page.p_pagein)
goto tryswap;
if (page.p_gone) {
#ifdef _KVM_DEBUG
_kvm_error("anon page is gone");
#endif _KVM_DEBUG
break;
}
/*
* Page is in core (probably).
* XXX - the following is going to change when
* tuples are implemented
*/
if (paddr < kd->memseg.epages) {
pages = kd->memseg.pages;
pages_base = kd->memseg.pages_base;
} else {
struct memseg memseg;
struct memseg *next;
/* not in first memseg */
next = kd->memseg.next;
memseg.epages = kd->memseg.epages;
while (paddr > memseg.epages) {
getkvm(next, &memseg, "next memory segment");
next = memseg.next;
}
pages = memseg.pages;
pages_base = memseg.pages_base;
}
skaddr = aoff +
(((paddr - pages) + pages_base) << PAGESHIFT);
if (_uncondense(kd, kd->corefd, &skaddr)) {
#ifdef _KVM_DEBUG
_kvm_error("%s: anon page uncondense error",
kd->core);
#endif _KVM_DEBUG
break;
}
if (lseek(kd->corefd, (off_t)skaddr, L_SET) == -1) {
#ifdef _KVM_DEBUG
_kvm_perror("%s: anon page seek error",
kd->core);
#endif _KVM_DEBUG
break;
}
if (read(kd->corefd, buf, rsize) != rsize) {
#ifdef _KVM_DEBUG
_kvm_perror("%s: anon page read error",
kd->core);
#endif _KVM_DEBUG
break;
}
goto readok;
tryswap:
/*
* If no page structure, page is swapped out
*/
if (kd->swapfd == -1)
break;
if (_swapread(kd, (long)swapoff, aoff, buf, rsize) !=
rsize) {
#ifdef _KVM_DEBUG
_kvm_perror("%s: anon page read error",
kd->swap);
#endif _KVM_DEBUG
break;
}
readok:
count += rsize;
addr += rsize;
buf += rsize;
}
free((char *)anp); /* no longer need this */
} else if (seg->s_ops == kd->segmap) {
/* Segmap segment */
#ifdef _KVM_DEBUG
_kvm_error("cannot read segmap segments yet");
#endif _KVM_DEBUG
return (-1);
} else if (seg->s_ops == kd->segdev) {
/* Segdev segment */
#ifdef _KVM_DEBUG
_kvm_error("cannot read segdev segments yet");
#endif _KVM_DEBUG
return (-1);
} else {
/* Segkmem or unknown segment */
#ifdef _KVM_DEBUG
_kvm_error("unknown segment type");
#endif _KVM_DEBUG
return (-1);
}
if (count == 0)
return (-1);
else
return ((int) count);
}
/* this is like the getkvm() macro, but returns NULL on error instead of -1 */
#ifdef _KVM_DEBUG
#define getkvmnull(a, b, m) \
if (kvm_read(kd, (u_long)(a), (caddr_t)(b), sizeof (*b)) \
!= sizeof (*b)) { \
_kvm_error("error reading %s", m); \
return (NULL); \
}
#else !_KVM_DEBUG
#define getkvmnull(a, b, m) \
if (kvm_read(kd, (u_long)(a), (caddr_t)(b), sizeof (*b)) \
!= sizeof (*b)) { \
return (NULL); \
}
#endif _KVM_DEBUG
/*
* pagefind - hashed lookup to see if a page exists for a given vnode
* Returns address of page structure in 'page', or NULL if error.
*/
static struct page *
pagefind(kd, page, vp, off)
kvm_t *kd;
struct page *page;
struct vnode *vp;
u_int off;
{
#define PAGE_HASHSZ (kd->page_hashsz) /* used by PAGE_HASHFUNC() */
struct page *pp;
getkvmnull((kd->page_hash + PAGE_HASHFUNC(vp, off)), &pp,
"initial hashed page struct ptr");
while (pp != NULL) {
getkvmnull(pp, page, "hashed page structure");
if ((page->p_vnode == vp) && (page->p_offset == off)) {
return (pp);
}
pp = page->p_hash;
}
return (NULL);
}
/*
* _swapread - read data from the swap device, handling alignment properly,
* Swapread() returns the number of bytes read, or -1.
*/
int
_swapread(kd, addr, offset, buf, size)
kvm_t *kd;
long addr;
u_int offset;
char *buf;
u_int size;
{
if (_uncondense(kd, kd->swapfd, &addr)) {
/* Does this need to handle the -2 case? */
#ifdef _KVM_DEBUG
_kvm_error("%s: uncondense error", kd->swap);
#endif _KVM_DEBUG
return (-1);
}
if (lseek(kd->swapfd, addr, L_SET) == -1) {
#ifdef _KVM_DEBUG
_kvm_perror("%s: seek error", kd->swap);
#endif _KVM_DEBUG
return (-1);
}
if ((offset == 0) && ((size % DEV_BSIZE) == 0)) {
return (read(kd->swapfd, buf, size));
} else {
#ifdef _KVM_DEBUG
_kvm_error("%s: swap offsets not implemented", kd->swap);
#endif _KVM_DEBUG
return (-1);
}
}
/*
* _anonoffset
*
* Convert a pointer into an anon array into an offset (in bytes)
* into the unified swap file. Since each individual swap file has
* a separate swapinfo structure, we cache the linked list of swapinfo
* structures in order to do this calculation faster. Also, save the
* real vp and offset for the vnode that contains this page.
*/
int
_anonoffset(kd, ap, vp, vpoffset)
kvm_t *kd;
struct anon *ap;
struct vnode **vp;
u_int *vpoffset;
{
register struct swapinfo *sip;
sip = kd->sip;
/*
* First time through, read in all the swapinfo structures.
* Re-use the si_allocs field to store the swap offset (in pages).
*/
if (sip == NULL) {
register struct swapinfo **spp;
register struct swapinfo *sn;
register int soff;
sn = kd->swapinfo;
spp = &kd->sip;
soff = 0; /* keep track of cumulative offsets */
for (; sn != NULL; spp = &(*spp)->si_next, sn = *spp) {
*spp = (struct swapinfo *)malloc(sizeof (*sn));
if (*spp == NULL) {
#ifdef _KVM_DEBUG
_kvm_perror("no memory for swapinfo");
#endif _KVM_DEBUG
break;
}
if (kvm_read(kd, (u_long)sn, (char*)*spp, sizeof (*sn))
!= sizeof (*sn)) {
#ifdef _KVM_DEBUG
_kvm_error("error reading swapinfo");
#endif _KVM_DEBUG
free((char *)*spp);
break;
}
(*spp)->si_allocs = soff;
soff += ((*spp)->si_size >> PAGESHIFT);
}
*spp = NULL; /* clear final 'next' pointer */
sip = kd->sip; /* reset list pointer */
}
/*
* At this point, all the swapinfo structures are in memory.
* Now find the one that includes the current anon pointer.
*/
for (; sip != NULL; sip = sip->si_next) {
if ((ap >= sip->si_anon) && (ap <= sip->si_eanon)) {
*vp = sip->si_vp;
*vpoffset = (ap - sip->si_anon) << PAGESHIFT;
return ((sip->si_allocs << PAGESHIFT) + *vpoffset);
}
}
#ifdef _KVM_DEBUG
_kvm_error("can't find anon ptr in swapinfo list");
#endif _KVM_DEBUG
return (-1);
}
/*
* Find physical address correspoding to an address space/offset
* Returns -1 if address does not correspond to any physical memory
*/
int
kvm_physaddr(kd, as, vaddr)
kvm_t *kd;
struct as *as;
u_int vaddr;
{
int fd;
u_int off;
_kvm_physaddr(kd, as, vaddr, &fd, &off);
return (fd == kd->corefd) ? off : (-1);
}
/* internal interface that also finds swap offset if any */
/* fd is either kd->corefd if in core, kd->swapfd if in swap or -1 if nowhere */
_kvm_physaddr(kd, as, vaddr, fdp, offp)
kvm_t *kd;
struct as *as;
u_int vaddr;
int *fdp;
u_int *offp;
{
struct seg s, *seg, *fseg;
*fdp = -1;
/* get first seg structure */
seg = &s;
if (as->a_segs == kd->Kas.a_segs) {
if(kd->Bufheapseg.s_size && (vaddr >= (u_int)kd->Bufheapseg.s_base))
fseg = &kd->Bufheapseg;
else
fseg = (vaddr < (u_int)kd->Kseg.s_base) ?
&kd->Ktextseg : &kd->Kseg;
} else {
fseg = &s;
getkvm(as->a_segs, fseg, "1st user segment descriptor");
}
/* Make sure we've got the right seg structure for this address */
if (getseg(kd, fseg, (addr_t)vaddr, seg) == -1)
return (-1);
if (seg->s_ops == kd->segvn) {
/* Segvn segment */
struct segvn_data sdata;
u_int off;
struct vnode *vp;
u_int vpoff;
off = (long)vaddr & PAGEOFFSET;
/* get private data for segment */
if (seg->s_data == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("NULL segvn_data ptr in segment");
#endif _KVM_DEBUG
return (-1);
}
getkvm(seg->s_data, &sdata, "segvn_data");
/* Try anonymous pages first */
if (sdata.amp != NULL) {
struct anon_map amap;
struct anon **anp;
struct anon *ap;
u_int apsz;
/* get anon_map structure */
getkvm(sdata.amp, &amap, "anon_map");
if (amap.anon == NULL)
goto notanon;
/* get space for anon pointer array */
apsz = ((amap.size + PAGEOFFSET) >> PAGESHIFT) *
sizeof (struct anon *);
if (apsz == 0)
goto notanon;
if ((anp = (struct anon **)malloc(apsz)) == NULL) {
#ifdef _KVM_DEBUG
_kvm_perror("anon pointer array");
#endif _KVM_DEBUG
return (-1);
}
/* read anon pointer array */
if (kvm_read(kd, (u_long)amap.anon,
(char *)anp, apsz) != apsz) {
#ifdef _KVM_DEBUG
_kvm_error("error reading anon ptr array");
#endif _KVM_DEBUG
free((char *)anp);
return (-1);
}
/* index into anon ptrs to find the right one */
ap = anp[sdata.anon_index +
btop((addr_t)vaddr - seg->s_base)];
free((char *)anp); /* no longer need this */
if (ap == NULL)
goto notanon;
anon_to_fdoffset(kd, ap, fdp, offp, off);
return (0);
}
notanon:
/* If not in anonymous; try the vnode */
vp = sdata.vp;
vpoff = sdata.offset + ((addr_t)vaddr - seg->s_base);
/*
* If vp is null then the page doesn't exist.
*/
if (vp != NULL) {
vp_to_fdoffset(kd, vp, fdp, offp, vpoff);
} else {
#ifdef _KVM_DEBUG
_kvm_error("Can't translate virtual address");
#endif _KVM_DEBUG
return (-1);
}
} else if (seg->s_ops == kd->segkmem) {
/* Segkmem segment */
register int poff;
register u_long paddr;
register u_int p;
if (as->a_segs != kd->Kas.a_segs)
return (-1);
poff = vaddr & PAGEOFFSET; /* save offset into page */
vaddr &= PAGEMASK; /* round down to start of page */
if ((vaddr >= (u_int)kd->Kseg.s_base) && (vaddr < kd->Syslimit)) {
/* kseg */
p = klvtop(vaddr) >> PAGESHIFT;
if (!pte_valid(&kd->Sysmap[p])) {
#ifdef _KVM_DEBUG
_kvm_error(
"kvm_read: invalid kernel page %d", p);
#endif _KVM_DEBUG
return (-1);
}
paddr = poff + ptob(MAKE_PFNUM(&kd->Sysmap[p]));
} else if (vaddr >= (KERNELBASE + NBPG) &&
vaddr < kd->econtig) {
/* ktextseg */
paddr = kvtop(kd, vaddr) + poff;
} else if ( kd->Bufheapseg.s_size && kd->Heapptes &&
(vaddr >= (u_int)kd->Bufheapseg.s_base) &&
(vaddr < (u_int)kd->Bufheapseg.s_base +
(u_int)kd->Bufheapseg.s_size)) {
/* bufheapseg */
p = (vaddr - (u_int)kd->Bufheapseg.s_base) >>PAGESHIFT;
if (!pte_valid(&kd->Heapptes[p])) {
#ifdef _KVM_DEBUG
_kvm_error(
"kvm_read: invalid kernel page %d", p);
#endif _KVM_DEBUG
return (-1);
}
paddr = poff + ptob(MAKE_PFNUM(&kd->Heapptes[p]));
#ifdef sun4m
} else if (kd->is_mp) {
register int pageix, pix, pte;
register u_int region;
region = (vaddr >> MMU_STD_RGNSHIFT);
if ((region ==
((u_int)VA_PERCPU >> MMU_STD_RGNSHIFT)) ||
(region ==
((u_int) VA_PERCPUME >> MMU_STD_RGNSHIFT))) {
if (region ==
((u_int)VA_PERCPU >> MMU_STD_RGNSHIFT))
kd->cpuid = (vaddr >> 20) & 0xF;
pageix = (vaddr >> PAGESHIFT) & 0x3F;
pix = kd->cpuid * NL3PTEPERPT + pageix;
pte = (int) kd->percpu_l3tbl[pix].ptpe_int;
if ((pte & 3) != MMU_ET_PTE) {
#ifdef _KVM_DEBUG
_kvm_error(
"kvm_read: vaddr %x is not mapped",
vaddr);
#endif _KVM_DEBUG
return (-1);
}
if (pte & 0xF0000000) {
#ifdef _KVM_DEBUG
_kvm_error(
"kvm_read: vaddr %x is not memory",
vaddr);
#endif _KVM_DEBUG
return (-1);
}
paddr = ((pte & ~0xFF) << 4) + poff;
} else {
#ifdef _KVM_DEBUG
_kvm_error("kvm_read: can't translate virtual addr %x",
vaddr);
#endif _KVM_DEBUG
return (-1);
}
#endif sun4m
} else {
#ifdef _KVM_DEBUG
_kvm_error("kvm_read: can't translate virtual addr %x",
vaddr);
#endif _KVM_DEBUG
return (-1);
}
*fdp = kd->corefd;
*offp = paddr;
} else if (seg->s_ops == kd->segu) {
/* Segu segment */
int slot, pageno;
struct segu_segdata segudata;
struct segu_data segu_slot_data;
struct anon *ap;
u_int aoff;
slot = (vaddr - (u_int)seg->s_base) / ptob(SEGU_PAGES);
aoff = (long)vaddr & PAGEOFFSET;
pageno = btop(vaddr - (slot * ptob(SEGU_PAGES) + (u_int)seg->s_base));
getkvm(seg->s_data, &segudata, "segu segment data");
getkvm(segudata.usd_slots + slot,
&segu_slot_data, "segu per-slot data");
ap = segu_slot_data.su_swaddr[pageno];
anon_to_fdoffset(kd, ap, fdp, offp, aoff);
} else if (seg->s_ops == kd->segmap) {
/* Segmap segment */
struct segmap_data sdata;
struct smap *smp;
struct smap *smap;
u_int smpsz;
u_int off;
struct vnode *vp = NULL;
u_int vpoff;
off = (long)vaddr & MAXBOFFSET;
vaddr &= MAXBMASK;
/* get private data for segment */
if (seg->s_data == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("NULL segmap_data ptr in segment");
#endif _KVM_DEBUG
return (-1);
}
getkvm(seg->s_data, &sdata, "segmap_data");
/* get space for smap array */
smpsz = (seg->s_size >> MAXBSHIFT) * sizeof (struct smap);
if (smpsz == 0) {
#ifdef _KVM_DEBUG
_kvm_error("zero-length smap array");
#endif _KVM_DEBUG
return (-1);
}
if ((smap = (struct smap *)malloc(smpsz)) == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("no memory for smap array");
#endif _KVM_DEBUG
return (-1);
}
/* read smap array */
if (kvm_read(kd, (u_long)sdata.smd_sm,
(char *)smap, smpsz) != smpsz) {
#ifdef _KVM_DEBUG
_kvm_error("error reading smap array");
#endif _KVM_DEBUG
(void) free((char *)smap);
return (-1);
}
/* index into smap array to find the right one */
smp = &smap[((addr_t)vaddr - seg->s_base)>>MAXBSHIFT];
vp = smp->sm_vp;
if (vp == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("NULL vnode ptr in smap");
#endif _KVM_DEBUG
free((char *)smap); /* no longer need this */
return (-1);
}
vpoff = smp->sm_off + off;
free((char *)smap); /* no longer need this */
vp_to_fdoffset(kd, vp, fdp, offp, vpoff);
} else if (seg->s_ops == kd->segdev) {
#ifdef _KVM_DEBUG
_kvm_error("cannot read segdev segments yet");
#endif _KVM_DEBUG
return (-1);
} else {
#ifdef _KVM_DEBUG
_kvm_error("unknown segment type");
#endif _KVM_DEBUG
return (-1);
}
return (0);
}
/* convert anon pointer/offset to fd (swap, core or nothing) and offset */
anon_to_fdoffset(kd, ap, fdp, offp, aoff)
kvm_t *kd;
struct anon *ap;
int *fdp;
u_int *offp;
u_int aoff;
{
struct anon anon;
struct page *paddr;
struct page page;
int swapoff;
struct vnode *vp;
u_int vpoff;
long skaddr;
if (ap == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("anon_to_fdoffset: null anon ptr");
#endif _KVM_DEBUG
return (-1);
}
if ((swapoff = _anonoffset(kd, ap, &vp, &vpoff)) == -1) {
return (-1);
}
getkvm(ap, &anon, "anon structure");
/*
* If there is a page structure pointer,
* make sure it is valid. If not, try the
* hash table in case the page is free but
* reclaimable.
*/
paddr = anon.un.an_page;
if (paddr != NULL) {
getkvm(paddr, &page, "anon page structure");
if ((page.p_vnode == vp) &&
(vpoff == page.p_offset)) {
goto gotpage;
}
#ifdef _KVM_DEBUG
_kvm_error("anon page struct invalid");
#endif _KVM_DEBUG
return (-1);
}
/* try hash table in case page is still around */
paddr = pagefind(kd, &page, vp, vpoff);
if (paddr == NULL) {
*fdp = kd->swapfd;
*offp = swapoff + aoff;
return (0);
}
gotpage:
/* make sure the page structure is useful */
if (page.p_pagein) {
*fdp = kd->swapfd;
*offp = swapoff + aoff;
return (0);
}
if (page.p_gone) {
#ifdef _KVM_DEBUG
_kvm_error("anon page is gone");
#endif _KVM_DEBUG
return (-1);
}
/*
* Page is in core.
*/
skaddr = page_to_physaddr(kd, paddr);
if (skaddr == -1) {
#ifdef _KVM_DEBUG
_kvm_error("anon_to_fdoffset: can't find page 0x%x", paddr);
#endif _KVM_DEBUG
return (-1);
}
*fdp = kd->corefd;
*offp = skaddr + aoff;
return (0);
}
/* convert vnode pointer/offset to fd (core or nothing) and offset */
static int
vp_to_fdoffset(kd, vp, fdp, offp, vpoff)
kvm_t *kd;
struct vnode *vp;
int *fdp;
u_int *offp;
u_int vpoff;
{
struct page *paddr;
struct page page;
u_int off;
long skaddr;
off = vpoff & PAGEOFFSET;
vpoff &= PAGEMASK;
if (vp == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("vp_to_fdoffset: null vp ptr");
#endif _KVM_DEBUG
return (-1);
}
paddr = pagefind(kd, &page, vp, vpoff);
if (paddr == NULL) {
#ifdef _KVM_DEBUG
_kvm_error("vp_to_fdoffset: page not mapped in");
#endif _KVM_DEBUG
return (-1);
}
/* make sure the page structure is useful */
if (page.p_pagein) {
#ifdef _KVM_DEBUG
_kvm_error("vp_to_fdoffset: page in transit");
#endif _KVM_DEBUG
return (-1);
}
if (page.p_gone) {
#ifdef _KVM_DEBUG
_kvm_error("vp_to_fdoffset: page is gone");
#endif _KVM_DEBUG
return (-1);
}
/*
* Page is in core.
*/
skaddr = page_to_physaddr(kd, paddr);
if (skaddr == -1) {
#ifdef _KVM_DEBUG
_kvm_error("vp_to_fdoffset: can't find page 0x%x", paddr);
#endif _KVM_DEBUG
return (-1);
}
*fdp = kd->corefd;
*offp = skaddr + off;
return (0);
}
/* convert page pointer to physical address */
static long
page_to_physaddr(kd, paddr)
kvm_t *kd;
struct page *paddr;
{
struct page *pages;
u_int pages_base;
/*
* Find out which memory segment page is in.
*/
if (paddr < kd->memseg.epages) {
pages = kd->memseg.pages;
pages_base = kd->memseg.pages_base;
} else {
struct memseg memseg;
struct memseg *next;
/* not in first memseg */
next = kd->memseg.next;
memseg.epages = kd->memseg.epages;
while (paddr > memseg.epages) {
getkvm(next, &memseg, "next memory segment");
next = memseg.next;
/*
* if next is null, and we try to use it, the getkvm
* macro will have us return -1.
*/
}
pages = memseg.pages;
pages_base = memseg.pages_base;
}
return (((paddr - pages) + pages_base) << PAGESHIFT);
}