#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 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef sun4m #include #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); }