/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include /* * sfmmu mdb support */ #define SFMMU_VTOP_DBG_SYMBOL 1 #define SFMMU_VTOP_DBG_VERBOSE 2 #define SFMMU_VTOP_DBG_DEBUG 4 #define SFMMU_VTOP_DBG_ALL (SFMMU_VTOP_DBG_SYMBOL|SFMMU_VTOP_DBG_VERBOSE|\ SFMMU_VTOP_DBG_DEBUG) #define SFMMU_VTOP_DBG_SYM if (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_SYMBOL) \ mdb_printf #define SFMMU_VTOP_DBG_VRB if (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_VERBOSE) \ mdb_printf #define SFMMU_VTOP_DBG_DBG if (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_DEBUG) \ mdb_printf #define SFMMU_VTOP_READSYM(dest, synm, where) \ if (mdb_readsym(&(dest), sizeof (dest), (synm)) == -1) \ mdb_warn("%s: couldn't find or read '%s'\n", (where), (synm)); struct hme_blks_max { struct hme_blk hmx_hmeblk; struct sf_hment hmx_hmes[NHMENTS - 1]; }; int sfmmu_vtop(uintptr_t, uint_t, int, const mdb_arg_t *); static int sfmmu_vtop_common(struct as *, uintptr_t, physaddr_t *); static int sfmmu_vtop_impl(uintptr_t, sfmmu_t *, sfmmu_t *, physaddr_t *); static void sfmmu_vtop_print_hmeblk(struct hme_blk *); static struct sf_hment *mdb_sfmmu_hblktohme(struct hme_blk *, caddr_t, int *); int sfmmu_vtop_dbg_wanted = 0; /* set this as desired */ int sfmmu_vtop_dbg = 0; /* * ::sfmmu_vtop [[-v] -a as] * Extended version of the vtop builtin. The optional argument is * used as base address space for translating a virtual address into a * physical address. The verbose option ("-v") shows intermediate * translation steps. If or kas is ommitted, the builtin ::vtop * dcmd is called. */ int sfmmu_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int ret; struct as *asp = NULL; char *asnmp = NULL; int verbose = 0; physaddr_t paddr; sfmmu_vtop_dbg = sfmmu_vtop_dbg_wanted; if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose, 'a', MDB_OPT_STR, &asnmp, NULL) != argc) return (DCMD_USAGE); if (verbose != 0 && asnmp == NULL) { mdb_warn("-v requires -a option\n"); return (DCMD_USAGE); } if (verbose != 0 && (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_VERBOSE) == 0) { sfmmu_vtop_dbg |= SFMMU_VTOP_DBG_VERBOSE; } if (asnmp != NULL) { GElf_Sym sym; SFMMU_VTOP_DBG_DBG("asnmp=%p asnm=%s\n", asnmp, asnmp); if (strcmp(asnmp, "kas") == 0) { if (mdb_lookup_by_name("kas", &sym) == -1) { mdb_warn("couldn't find 'kas'\n"); return (DCMD_ERR); } else { asp = (struct as *)sym.st_value; SFMMU_VTOP_DBG_SYM("kas &sym=%p\n", &sym); } } else { asp = (struct as *)mdb_strtoull(asnmp); } SFMMU_VTOP_DBG_DBG("asp=0x%p\n", asp); } if (asp == 0) { SFMMU_VTOP_DBG_DBG("sfmmu_vtop: call standard vtop\n"); return (mdb_call_dcmd("vtop", addr, flags, argc, argv)); } if ((ret = sfmmu_vtop_common(asp, addr, &paddr)) == -1L) { mdb_printf("no mapping found for addr=%p\n", addr); return (DCMD_ERR); } if (ret == 0) { mdb_printf("address space %p: virtual %lr mapped to physical " "%llr", asp, addr, paddr); } else { return (DCMD_ERR); } return (DCMD_OK); } static int sfmmu_vtop_common(struct as *asp, uintptr_t addr, physaddr_t *pap) { struct as mas; struct as *masp = &mas; sfmmu_t *hatp; sfmmu_t mhat; sfmmu_t *mhatp = &mhat; int ret; if (mdb_vread(masp, sizeof (mas), (uintptr_t)asp) == -1) { mdb_warn("couldn't read as at %p\n", asp); return (DCMD_ERR); } hatp = masp->a_hat; SFMMU_VTOP_DBG_DBG("hatp=%p addr=%p masp=%p\n", hatp, addr, masp); if (mdb_vread(mhatp, sizeof (mhat), (uintptr_t)hatp) == -1) { mdb_warn("couldn't read hat at %p\n", hatp); return (DCMD_ERR); } if (mhatp->sfmmu_as != asp) { mdb_warn("%p is not a valid address space\n", asp); return (DCMD_ERR); } ret = sfmmu_vtop_impl(addr, hatp, mhatp, pap); return (ret); } static int sfmmu_vtop_impl(uintptr_t addr, sfmmu_t *sfmmup, sfmmu_t *msfmmup, physaddr_t *pap) { struct hmehash_bucket *uhme_hash; struct hmehash_bucket *khme_hash; int uhmehash_num; int khmehash_num; sfmmu_t *ksfmmup; struct hmehash_bucket mbucket; struct hmehash_bucket *hmebp; struct hmehash_bucket *shmebp; hmeblk_tag hblktag; int hmeshift; int hashno = 1; struct hme_blk *hmeblkp = NULL; struct hme_blks_max mhmeblkmax; intptr_t thmeblkp; struct sf_hment *sfhmep; int i; ism_blk_t mism_blk; ism_map_t *ism_map; ism_blk_t *ism_blkp; ism_blk_t *sism_blkp; sfmmu_t *ism_hatid = NULL; int sfhmeinx = 0; tte_t tte; pfn_t pfn; pfn_t start_pfn; page_t *pp; int ret = -1; SFMMU_VTOP_READSYM(uhme_hash, "uhme_hash", "sfmmu_vtop_impl"); SFMMU_VTOP_DBG_DBG("uhme_hash=%p\t", uhme_hash); SFMMU_VTOP_READSYM(uhmehash_num, "uhmehash_num", "sfmmu_vtop_impl"); SFMMU_VTOP_DBG_DBG("uhmehash_num=%lx\n", uhmehash_num); SFMMU_VTOP_READSYM(khme_hash, "khme_hash", "sfmmu_vtop_impl"); SFMMU_VTOP_DBG_DBG("khme_hash=%p\t", khme_hash); SFMMU_VTOP_READSYM(khmehash_num, "khmehash_num", "sfmmu_vtop_impl"); SFMMU_VTOP_DBG_DBG("khmehash_num=%lx\n", khmehash_num); SFMMU_VTOP_READSYM(ksfmmup, "ksfmmup", "sfmmu_vtop_impl"); SFMMU_VTOP_DBG_DBG("ksfmmup=%p\n", ksfmmup); ism_blkp = sism_blkp = msfmmup->sfmmu_iblk; while (ism_blkp != NULL && ism_hatid == NULL) { SFMMU_VTOP_DBG_DBG("ism_blkp=%p\n", ism_blkp); if (mdb_vread(&mism_blk, sizeof (mism_blk), (uintptr_t)ism_blkp) == -1) { mdb_warn("couldn't read ism_blk at %p\n", ism_blkp); return (DCMD_ERR); } ism_blkp = &mism_blk; ism_map = ism_blkp->iblk_maps; for (i = 0; ism_map[i].imap_ismhat && i < ISM_MAP_SLOTS; i++) { if ((caddr_t)addr >= ism_start(ism_map[i]) && (caddr_t)addr < ism_end(ism_map[i])) { sfmmup = ism_hatid = ism_map[i].imap_ismhat; addr = (caddr_t)addr - ism_start(ism_map[i]); SFMMU_VTOP_DBG_VRB("ism_blkp=%p inx=%d\n", sism_blkp, i); SFMMU_VTOP_DBG_DBG("ism map=%p ism hat=%p " "addr=%llx\n", (caddr_t)&ism_map[i] - (caddr_t)ism_blkp + (caddr_t)sism_blkp, sfmmup, addr); break; } } ism_blkp = sism_blkp = ism_blkp->iblk_next; } hblktag.htag_id = sfmmup; do { SFMMU_VTOP_DBG_DBG("-hashno=%d-\n", hashno); hmeshift = HME_HASH_SHIFT(hashno); SFMMU_VTOP_DBG_DBG("hmeshift=%d\n", hmeshift); hblktag.htag_bspage = HME_HASH_BSPAGE(addr, hmeshift); hblktag.htag_rehash = hashno; #ifdef __sparcv9 SFMMU_VTOP_DBG_DBG("hblktag=%lx %lx\n", (uint64_t)hblktag.htag_tag[0], (uint64_t)hblktag.htag_tag[1]); #else SFMMU_VTOP_DBG_DBG("hblktag=%llx\n", (uint64_t)hblktag.htag_tag); #endif hmebp = shmebp = HME_HASH_FUNCTION(sfmmup, addr, hmeshift); SFMMU_VTOP_DBG_DBG("hmebp=%p\n", hmebp); if (mdb_vread(&mbucket, sizeof (mbucket), (uintptr_t)hmebp) == -1) { mdb_warn("couldn't read mbucket at %p\n", hmebp); return (DCMD_ERR); } hmebp = &mbucket; for (hmeblkp = hmebp->hmeblkp; hmeblkp; hmeblkp = hmeblkp->hblk_next) { SFMMU_VTOP_DBG_DBG("hmeblkp=%p\n", hmeblkp); if (hmeblkp == NULL) break; if (mdb_vread(&mhmeblkmax, sizeof (struct hme_blk), (uintptr_t)hmeblkp) == -1) { mdb_warn("couldn't read hme_blk at %p\n", hmeblkp); return (DCMD_ERR); } thmeblkp = (uintptr_t)hmeblkp; hmeblkp = &mhmeblkmax.hmx_hmeblk; if (HTAGS_EQ(hmeblkp->hblk_tag, hblktag)) { /* found hme_blk */ break; } } if (hmeblkp != NULL) { sfmmu_vtop_print_hmeblk(hmeblkp); sfhmep = mdb_sfmmu_hblktohme(hmeblkp, (caddr_t)addr, &sfhmeinx); SFMMU_VTOP_DBG_DBG("sfhmeinx=%d ", sfhmeinx); if (sfhmeinx > 0) { thmeblkp += sizeof (struct hme_blk) + sizeof (struct sf_hment) * (sfhmeinx - 1); if (mdb_vread(sfhmep, sizeof (struct sf_hment), thmeblkp) == -1) { mdb_warn("couldn't read msfhme at %p\n", sfhmep); return (DCMD_ERR); } } SFMMU_VTOP_DBG_VRB("sfmmup=%p hmebp=%p hmeblkp=%p\n", sfmmup, shmebp, thmeblkp); tte = sfhmep->hme_tte; SFMMU_VTOP_DBG_VRB("tte=%llx ", tte.ll); if (TTE_IS_VALID(&tte)) { start_pfn = TTE_TO_TTEPFN(&tte); *pap = (start_pfn << MMU_PAGESHIFT) + (addr & TTE_PAGE_OFFSET(tte.tte_size)); pfn = *pap >> MMU_PAGESHIFT; pp = (sfhmep->hme_page != 0) ? sfhmep->hme_page + (pfn - start_pfn) : 0; SFMMU_VTOP_DBG_VRB("pfn=%lx pp=%p\n", pfn, pp); ret = 0; } break; } hashno++; } while (HME_REHASH(msfmmup) && (hashno <= MAX_HASHCNT)); return (ret); } static void sfmmu_vtop_print_hmeblk(struct hme_blk *hmeblkp) { if ((sfmmu_vtop_dbg & SFMMU_VTOP_DBG_DEBUG) == NULL) return; mdb_printf(" hblk_nextpa=%llx\n", hmeblkp->hblk_nextpa); #ifdef __sparcv9 mdb_printf(" hblktag=%lx %lx\n", hmeblkp->hblk_tag.htag_tag[0], hmeblkp->hblk_tag.htag_tag[1]); #else mdb_printf(" hblktag=%llx\n", hmeblkp->hblk_tag.htag_tag); #endif mdb_printf(" hblk_next=%p\n", hmeblkp->hblk_next); mdb_printf(" hblk_shadow=%p\n", hmeblkp->hblk_shadow); mdb_printf(" hblk_span=%d\n", hmeblkp->hblk_span); mdb_printf(" hblk_ttesz=%d\n", hmeblkp->hblk_ttesz); if (hmeblkp->hblk_shw_bit == 0) { mdb_printf(" hblk_hmecnt=%d\n", hmeblkp->hblk_hmecnt); mdb_printf(" hblk_vcnt=%d\n", hmeblkp->hblk_vcnt); } else { mdb_printf(" hblk_shw_mask=%x\n", hmeblkp->hblk_shw_mask); } } static struct sf_hment * mdb_sfmmu_hblktohme(struct hme_blk *hmeblkp, caddr_t addr, int *hmenump) { int index = 0; if (get_hblk_ttesz(hmeblkp) == TTE8K) { index = (((uintptr_t)addr >> MMU_PAGESHIFT) & (NHMENTS-1)); } if (hmenump) { *hmenump = index; } return (&hmeblkp->hblk_hme[index]); } /* * ::memseg_list dcmd */ /*ARGSUSED*/ int memseg_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct memseg ms; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_pwalk_dcmd("memseg", "memseg_list", 0, NULL, 0) == -1) { mdb_warn("can't walk memseg"); return (DCMD_ERR); } return (DCMD_OK); } if (DCMD_HDRSPEC(flags)) mdb_printf("%%?s %?s %?s %?s %?s%\n", "ADDR", "PAGES", "EPAGES", "BASE", "END"); if (mdb_vread(&ms, sizeof (struct memseg), addr) == -1) { mdb_warn("can't read memseg at %#lx", addr); return (DCMD_ERR); } mdb_printf("%0?lx %0?lx %0?lx %0?lx %0?lx\n", addr, ms.pages, ms.epages, ms.pages_base, ms.pages_end); return (DCMD_OK); } /* * walk the memseg structures */ int memseg_walk_init(mdb_walk_state_t *wsp) { if (wsp->walk_addr != NULL) { mdb_warn("memseg only supports global walks\n"); return (WALK_ERR); } if (mdb_readvar(&wsp->walk_addr, "memsegs") == -1) { mdb_warn("symbol 'memsegs' not found"); return (WALK_ERR); } wsp->walk_data = mdb_alloc(sizeof (struct memseg), UM_SLEEP); return (WALK_NEXT); } int memseg_walk_step(mdb_walk_state_t *wsp) { int status; if (wsp->walk_addr == 0) { return (WALK_DONE); } if (mdb_vread(wsp->walk_data, sizeof (struct memseg), wsp->walk_addr) == -1) { mdb_warn("failed to read struct memseg at %p", wsp->walk_addr); return (WALK_DONE); } status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); wsp->walk_addr = (uintptr_t)(((struct memseg *)wsp->walk_data)->next); return (status); } void memseg_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (struct memseg)); } int platform_vtop(uintptr_t addr, struct as *asp, physaddr_t *pap) { int rv; sfmmu_vtop_dbg = sfmmu_vtop_dbg_wanted; SFMMU_VTOP_DBG_DBG("platform_vtop: called.\n"); if (asp == NULL) { return (DCMD_ERR); } if ((rv = sfmmu_vtop_common(asp, addr, pap)) == 0) { mdb_printf("address space %p: ", asp); } return (rv); } /* * ::tsbinfo help */ void tsbinfo_help(void) { mdb_printf("-l\tlist valid TSB entries.\n" "-a\tlist all TSB entries. Can only be used with -l.\n"); } /* * ::tsbinfo dcmd */ int tsbinfo_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint_t lflag = 0, aflag = 0; struct tsb_info tsbinfo; unsigned int entries = 0; struct tsbe *tsbp, *tsbend, *tsbstart; caddr_t va; uintptr_t pa; uint_t tsbbytes; char tsbsize[16]; #define FLAGS_SIZE sizeof ("RELOC,FLUSH,SWAPPED") char tsbflags[FLAGS_SIZE + 1]; static const mdb_bitmask_t ttesz_mask_bits[] = { { "8K", TSB8K, TSB8K }, { "64K", TSB64K, TSB64K }, { "512K", TSB512K, TSB512K }, { "4M", TSB4M, TSB4M }, { "32M", TSB32M, TSB32M }, { "256M", TSB256M, TSB256M }, { NULL, 0, 0 } }; static const mdb_bitmask_t flags_bits[] = { { "RELOC", TSB_RELOC_FLAG, TSB_RELOC_FLAG }, { "FLUSH", TSB_FLUSH_NEEDED, TSB_FLUSH_NEEDED }, { "SWAPPED", TSB_SWAPPED, TSB_SWAPPED }, { NULL, 0, 0 } }; if (!(flags & DCMD_ADDRSPEC)) { return (DCMD_USAGE); } if (mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &lflag, 'a', MDB_OPT_SETBITS, TRUE, &aflag, NULL) != argc) { return (DCMD_USAGE); } /* -a only valid with -l */ if (aflag && !lflag) { return (DCMD_USAGE); } /* Print header? */ if (DCMD_HDRSPEC(flags) || lflag) { mdb_printf("%%-?s %-?s %-8s %-*s %s%\n", "TSBINFO", "TSB", "SIZE", FLAGS_SIZE, "FLAGS", "TTE SIZES"); } if (mdb_vread(&tsbinfo, sizeof (struct tsb_info), addr) == -1) { mdb_warn("failed to read struct tsb_info at %p", addr); return (DCMD_ERR); } mdb_printf("%0?lx ", addr); /* Print a "-" if the TSB is swapped out. */ if ((tsbinfo.tsb_flags & TSB_SWAPPED) == 0) { mdb_printf("%0?lx ", tsbinfo.tsb_va); } else { mdb_printf("%0?-s ", "-"); } tsbbytes = TSB_BYTES(tsbinfo.tsb_szc); #define KB 1024 #define MB (KB*KB) if (tsbbytes >= MB) { mdb_snprintf(tsbsize, sizeof (tsbsize), "%dM", tsbbytes / MB); } else { mdb_snprintf(tsbsize, sizeof (tsbsize), "%dK", tsbbytes / KB); } #undef MB #undef KB mdb_printf("%-8s ", tsbsize); if (tsbinfo.tsb_flags == 0) { mdb_printf("%-*s ", FLAGS_SIZE, "-"); } else { mdb_snprintf(tsbflags, sizeof (tsbflags), "%b", tsbinfo.tsb_flags, flags_bits); mdb_printf("%-*s ", FLAGS_SIZE, tsbflags); } mdb_printf("%b\n", tsbinfo.tsb_ttesz_mask, ttesz_mask_bits); /* Print TSB entries? */ if (lflag) { if ((tsbinfo.tsb_flags & TSB_SWAPPED) == 0) { entries = TSB_ENTRIES(tsbinfo.tsb_szc); tsbp = mdb_alloc(sizeof (struct tsbe) * entries, UM_SLEEP); if (mdb_vread(tsbp, sizeof (struct tsbe) * entries, (uintptr_t)tsbinfo.tsb_va) == -1) { mdb_warn("failed to read TSB at %p", tsbinfo.tsb_va); return (DCMD_ERR); } mdb_printf( "TSB @ %lx (%d entries)\n" "%-?s %-17s %s\n" "%%-?s %1s %1s %-11s " "%1s %1s %1s %1s %1s %1s %8s " "%1s %1s %1s %1s %1s %1s %1s " "%1s %1s %1s %1s %1s %1s%\n", tsbinfo.tsb_va, entries, "", "TAG", "TTE", "ADDR", "I", "L", "VA 63:22", "V", "S", "N", "I", "H", "S", "PA 42:13", "N", "U", "R", "W", "E", "X", "L", "P", "V", "E", "P", "W", "G"); tsbend = tsbp + entries; for (tsbstart = tsbp; tsbp < tsbend; tsbp++) { if (aflag || (tsbp->tte_tag.tag_invalid == 0)) { va = (caddr_t) (((uint64_t)tsbp->tte_tag.tag_vahi << 32) + tsbp->tte_tag.tag_valo); pa = (tsbp->tte_data.tte_pahi << 19) + tsbp->tte_data.tte_palo; mdb_printf("%0?lx %-1u %-1u %011lx " "%1u %-1u %-1u %-1u %-1u %1u %08x " "%1u %1u %1u %1u %1u %1u %1u " "%1u %1u %1u %1u %1u %1u\n", tsbinfo.tsb_va + (tsbp - tsbstart) * sizeof (struct tsbe), tsbp->tte_tag.tag_invalid, tsbp->tte_tag.tag_locked, va, tsbp->tte_data.tte_val, tsbp->tte_data.tte_size, tsbp->tte_data.tte_nfo, tsbp->tte_data.tte_ie, tsbp->tte_data.tte_hmenum, #ifdef sun4v 0, #else tsbp->tte_data.tte_size2, #endif pa, tsbp->tte_data.tte_no_sync, tsbp->tte_data.tte_suspend, tsbp->tte_data.tte_ref, tsbp->tte_data.tte_wr_perm, #ifdef sun4v 0, #else tsbp->tte_data.tte_exec_synth, #endif tsbp->tte_data.tte_exec_perm, tsbp->tte_data.tte_lock, tsbp->tte_data.tte_cp, tsbp->tte_data.tte_cv, tsbp->tte_data.tte_se, tsbp->tte_data.tte_priv, tsbp->tte_data.tte_hwwr, #ifdef sun4v 0 #else tsbp->tte_data.tte_glb #endif /*CSTYLED*/ ); } } mdb_printf("\n"); /* blank line for readability */ mdb_free(tsbstart, sizeof (struct tsbe) * entries); } else { mdb_printf("TSB swapped out\n"); } } return (DCMD_OK); }