/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/sysmacros.h>
#include <ctype.h>
#include <sys/mdb_modapi.h>
#include <sys/cpuvar.h>
#include <sys/machcpuvar.h>
#include <sys/error.h>
/*ARGSUSED*/
int
resumable(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t verbose = 0;
cpu_t cpu;
uintptr_t current, first;
if (flags & DCMD_ADDRSPEC)
return (DCMD_USAGE);
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, 1, &verbose, NULL) != argc)
return (DCMD_USAGE);
if (mdb_readvar(&first, "cpu_list") == -1) {
mdb_warn("failed to read 'cpu_list'");
return (DCMD_ERR);
}
if (verbose)
mdb_printf("CPUID ADDRESS\n");
current = first;
do {
if (mdb_vread(&cpu, sizeof (cpu), current) == -1) {
mdb_warn("failed to read cpu at %p", current);
return (DCMD_ERR);
}
if (verbose) {
if (cpu.cpu_m.cpu_rq_lastre == 0)
mdb_printf("%-5d empty\n", cpu.cpu_id);
else
mdb_printf("%-5d %lx\n", cpu.cpu_id,
cpu.cpu_m.cpu_rq_lastre);
} else if (cpu.cpu_m.cpu_rq_lastre != 0)
mdb_printf("%lx\n", cpu.cpu_m.cpu_rq_lastre);
} while ((current = (uintptr_t)cpu.cpu_next) != first);
return (DCMD_OK);
}
/*ARGSUSED*/
int
nonresumable(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t verbose = 0;
cpu_t cpu;
uintptr_t current, first;
if (flags & DCMD_ADDRSPEC)
return (DCMD_USAGE);
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, 1, &verbose, NULL) != argc)
return (DCMD_USAGE);
if (mdb_readvar(&first, "cpu_list") == -1) {
mdb_warn("failed to read 'cpu_list'");
return (DCMD_ERR);
}
if (verbose)
mdb_printf("CPUID ADDRESS\n");
current = first;
do {
if (mdb_vread(&cpu, sizeof (cpu), current) == -1) {
mdb_warn("failed to read cpu at %p", current);
return (DCMD_ERR);
}
if (verbose) {
if (cpu.cpu_m.cpu_nrq_lastnre == 0)
mdb_printf("%-5d empty\n", cpu.cpu_id);
else
mdb_printf("%-5d %lx\n", cpu.cpu_id,
cpu.cpu_m.cpu_nrq_lastnre);
} else if (cpu.cpu_m.cpu_nrq_lastnre != 0)
mdb_printf("%lx\n", cpu.cpu_m.cpu_nrq_lastnre);
} while ((current = (uintptr_t)cpu.cpu_next) != first);
return (DCMD_OK);
}
/*ARGSUSED*/
int
rqueue(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t verbose = 0;
cpu_t cpu;
uintptr_t ao, lower, upper, current;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, 1, &verbose, NULL) != argc)
return (DCMD_USAGE);
if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
mdb_warn("failed to find cpu at %p", addr);
return (DCMD_ERR);
}
ao = (uintptr_t)cpu.cpu_m.cpu_rq_lastre; /* beginning and end */
lower = (uintptr_t)cpu.cpu_m.cpu_rq_va + CPU_RQ_SIZE;
upper = lower + CPU_RQ_SIZE - Q_ENTRY_SIZE;
if (ao < lower || upper < ao) {
if (verbose)
mdb_printf("empty\n");
return (DCMD_OK);
}
for (current = ao; current >= lower; current -= Q_ENTRY_SIZE)
mdb_printf("%lx\n", current);
for (current = upper; current > ao; current -= Q_ENTRY_SIZE)
mdb_printf("%lx\n", current);
return (DCMD_OK);
}
/*ARGSUSED*/
int
nrqueue(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t verbose = 0;
cpu_t cpu;
uintptr_t lower, ao, upper;
uintptr_t current;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, 1, &verbose, NULL) != argc)
return (DCMD_USAGE);
if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
mdb_warn("failed to find cpu at %p", addr);
return (DCMD_ERR);
}
ao = (uintptr_t)cpu.cpu_m.cpu_nrq_lastnre; /* beginning and end */
lower = (uintptr_t)cpu.cpu_m.cpu_nrq_va + CPU_NRQ_SIZE;
upper = lower + CPU_NRQ_SIZE - Q_ENTRY_SIZE;
if (ao < lower || upper < ao) {
if (verbose)
mdb_printf("empty\n");
return (DCMD_OK);
}
for (current = ao; current >= lower; current -= Q_ENTRY_SIZE)
mdb_printf("%lx\n", current);
for (current = upper; current > ao; current -= Q_ENTRY_SIZE)
mdb_printf("%lx\n", current);
return (DCMD_OK);
}
/*ARGSUSED*/
int
errh_prtaddr(uintptr_t addr, const void *data, void *private)
{
mdb_printf("%lx\n", addr);
return (WALK_NEXT);
}
/*ARGSUSED*/
int
rq_walk_init(mdb_walk_state_t *wsp)
{
cpu_t cpu;
uintptr_t *ao, *lower, *upper;
if (wsp->walk_addr == NULL) {
mdb_warn("address of struct cpu_t is required\n");
return (WALK_ERR);
}
if (mdb_vread(&cpu, sizeof (cpu_t), wsp->walk_addr) == -1) {
mdb_warn("failed to find cpu at %p", wsp->walk_addr);
return (WALK_ERR);
}
wsp->walk_callback = (mdb_walk_cb_t)errh_prtaddr;
wsp->walk_addr = (uintptr_t)cpu.cpu_m.cpu_rq_lastre;
wsp->walk_data = mdb_alloc(sizeof (uintptr_t) * 3, UM_SLEEP);
ao = lower = upper = wsp->walk_data;
lower += 1;
upper += 2;
*ao = (uintptr_t)wsp->walk_addr; /* beginning and end */
*lower = (uintptr_t)cpu.cpu_m.cpu_rq_va + CPU_RQ_SIZE;
*upper = (uintptr_t)*lower + CPU_RQ_SIZE - Q_ENTRY_SIZE;
if (wsp->walk_addr < *lower || *upper < wsp->walk_addr) {
mdb_free(wsp->walk_data, sizeof (uintptr_t) * 3);
return (WALK_DONE);
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
nrq_walk_init(mdb_walk_state_t *wsp)
{
cpu_t cpu;
uintptr_t *ao, *lower, *upper;
if (wsp->walk_addr == NULL) {
mdb_warn("address of struct cpu_t is required\n");
return (WALK_ERR);
}
if (mdb_vread(&cpu, sizeof (cpu_t), wsp->walk_addr) == -1) {
mdb_warn("failed to find cpu at %p", wsp->walk_addr);
return (WALK_ERR);
}
wsp->walk_callback = (mdb_walk_cb_t)errh_prtaddr;
wsp->walk_addr = (uintptr_t)cpu.cpu_m.cpu_nrq_lastnre;
wsp->walk_data = mdb_alloc(sizeof (uintptr_t) * 3, UM_SLEEP);
ao = lower = upper = wsp->walk_data;
lower += 1;
upper += 2;
*ao = (uintptr_t)wsp->walk_addr; /* beginning and end */
*lower = (uintptr_t)cpu.cpu_m.cpu_nrq_va + CPU_NRQ_SIZE;
*upper = (uintptr_t)*lower + CPU_NRQ_SIZE - Q_ENTRY_SIZE;
if (wsp->walk_addr < *lower || *upper < wsp->walk_addr) {
mdb_free(wsp->walk_data, sizeof (uintptr_t) * 3);
return (WALK_DONE);
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
errh_walk_step(mdb_walk_state_t *wsp)
{
int status;
uintptr_t *ao, *lower, *upper;
if (wsp->walk_addr == NULL)
return (WALK_DONE);
status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
wsp->walk_cbdata);
wsp->walk_addr -= Q_ENTRY_SIZE;
ao = lower = upper = wsp->walk_data;
lower += 1;
upper += 2;
if (wsp->walk_addr < *lower)
wsp->walk_addr = *upper; /* wrap around */
else if (wsp->walk_addr == *ao)
return (WALK_DONE); /* end of loop */
return (status);
}
void
errh_walk_fini(mdb_walk_state_t *wsp)
{
mdb_free(wsp->walk_data, sizeof (uintptr_t) * 3);
}
/*
* MDB module linkage information:
*
* Declare a list of structures describing dcmds, and a function
* named _mdb_init to return a pointer to module information.
*/
static const mdb_dcmd_t dcmds[] = {
{ "errhre", "[-v]", "addr of sun4v resumable error element",
resumable },
{ "errhnre", "[-v]", "addr of sun4v nonresumable error element",
nonresumable },
{ "errhrq", ":", "addr of sun4v resumable errors in RQ", rqueue },
{ "errhnrq", ":", "addr of sun4v nonresumable errors in NRQ", nrqueue },
{ NULL }
};
static const mdb_walker_t walkers[] = {
{ "errhrq", "walk a cpu-specific sun4v resumble error queue",
rq_walk_init, errh_walk_step, errh_walk_fini, NULL },
{ "errhnrq", "walk a cpu-specific sun4v nonresumble error queue",
nrq_walk_init, errh_walk_step, errh_walk_fini, NULL },
{ NULL }
};
static const mdb_modinfo_t modinfo = {
MDB_API_VERSION, dcmds, walkers
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}