#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/obpdefs.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/machsystm.h>
#include <vm/hat_sfmmu.h>
#include <sys/autoconf.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/modctl.h>
#include <sys/fhc.h>
#include <sys/ac.h>
#include <sys/cpu_module.h>
#include <sys/x_call.h>
#include <sys/fpu/fpusystm.h>
#include <sys/lgrp.h>
#include <sys/nexusdebug.h>
static int ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result);
static int ac_attach(dev_info_t *, ddi_attach_cmd_t);
static int ac_detach(dev_info_t *, ddi_detach_cmd_t);
static int ac_open(dev_t *, int, int, cred_t *);
static int ac_close(dev_t, int, int, cred_t *);
static int ac_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static void ac_add_kstats(struct ac_soft_state *);
static void ac_del_kstats(struct ac_soft_state *);
static int ac_misc_kstat_update(kstat_t *, int);
static void ac_add_picN_kstats(dev_info_t *dip);
static int ac_counters_kstat_update(kstat_t *, int);
static void ac_get_memory_status(struct ac_soft_state *, enum ac_bank_id);
static void ac_eval_memory_status(struct ac_soft_state *, enum ac_bank_id);
static void ac_ecache_flush(uint64_t, uint64_t);
static int ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag);
static int ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag);
static int ac_reset_timeout(int rw);
static void ac_timeout(void *);
static int ac_enter_transition(void);
static void ac_exit_transition(void);
int ac_add_memory(ac_cfga_pkt_t *);
int ac_del_memory(ac_cfga_pkt_t *);
int ac_mem_stat(ac_cfga_pkt_t *, int);
int ac_mem_test_start(ac_cfga_pkt_t *, int);
int ac_mem_test_stop(ac_cfga_pkt_t *, int);
int ac_mem_test_read(ac_cfga_pkt_t *, int);
int ac_mem_test_write(ac_cfga_pkt_t *, int);
void ac_mem_test_stop_on_close(uint_t, uint_t);
typedef enum {
AC_AUDIT_OSTATE_CONFIGURE,
AC_AUDIT_OSTATE_UNCONFIGURE,
AC_AUDIT_OSTATE_SUCCEEDED,
AC_AUDIT_OSTATE_CONFIGURE_FAILED,
AC_AUDIT_OSTATE_UNCONFIGURE_FAILED
} ac_audit_evt_t;
static void ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt);
static char *ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event);
static ac_mem_version_t ac_mem_version = AC_MEM_ADMIN_VERSION;
static int ac_mem_exercise(ac_cfga_pkt_t *, int);
static struct cb_ops ac_cb_ops = {
ac_open,
ac_close,
nulldev,
nulldev,
nodev,
nulldev,
nulldev,
ac_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_MP | D_NEW | D_HOTPLUG,
CB_REV,
nodev,
nodev
};
static struct dev_ops ac_ops = {
DEVO_REV,
0,
ac_info,
nulldev,
nulldev,
ac_attach,
ac_detach,
nulldev,
&ac_cb_ops,
(struct bus_ops *)0,
nulldev,
ddi_quiesce_not_needed,
};
void *acp;
static kstat_t *ac_picN_ksp[AC_NUM_PICS];
static int ac_attachcnt = 0;
static kmutex_t ac_attachcnt_mutex;
static kmutex_t ac_hot_plug_mode_mutex;
static timeout_id_t ac_hot_plug_timeout;
static int ac_hot_plug_timeout_interval = 10;
#define AC_GETSOFTC(I) \
((struct ac_soft_state *)ddi_get_soft_state(acp, (I)))
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"AC Leaf",
&ac_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&acp, sizeof (struct ac_soft_state),
1)) != 0)
return (error);
if ((error = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&acp);
return (error);
}
mutex_init(&ac_attachcnt_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&ac_hot_plug_mode_mutex, NULL, MUTEX_DRIVER, NULL);
return (0);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) == 0) {
ddi_soft_state_fini(&acp);
mutex_destroy(&ac_attachcnt_mutex);
mutex_destroy(&ac_hot_plug_mode_mutex);
}
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev;
int instance;
if (infocmd == DDI_INFO_DEVT2INSTANCE) {
dev = (dev_t)arg;
instance = AC_GETINSTANCE(getminor(dev));
*result = (void *)(uintptr_t)instance;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
ac_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
int instance;
struct ac_soft_state *softsp;
struct bd_list *list = NULL;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
instance = ddi_get_instance(devi);
if (ddi_soft_state_zalloc(acp, instance) != DDI_SUCCESS) {
cmn_err(CE_WARN, "ddi_soft_state_zalloc failed for ac%d",
instance);
return (DDI_FAILURE);
}
softsp = ddi_get_soft_state(acp, instance);
softsp->dip = devi;
softsp->pdip = ddi_get_parent(softsp->dip);
if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip,
DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
cmn_err(CE_WARN, "ac%d: unable to retrieve %s property",
instance, OBP_BOARDNUM);
goto bad;
}
DPRINTF(AC_ATTACH_DEBUG, ("ac%d: devi= 0x%p\n,"
" softsp=0x%p\n", instance, (void *)devi, (void *)softsp));
if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0)) {
cmn_err(CE_WARN, "ac%d: unable to map registers", instance);
goto bad;
}
softsp->ac_id = (uint32_t *)softsp->ac_base;
softsp->ac_memctl = (uint64_t *)((char *)softsp->ac_base +
AC_OFF_MEMCTL);
softsp->ac_memdecode0 = (uint64_t *)((char *)softsp->ac_base +
AC_OFF_MEMDEC0);
softsp->ac_memdecode1 = (uint64_t *)((char *)softsp->ac_base +
AC_OFF_MEMDEC1);
softsp->ac_counter = (uint64_t *)((char *)softsp->ac_base +
AC_OFF_CNTR);
softsp->ac_mccr = (uint32_t *)((char *)softsp->ac_base +
AC_OFF_MCCR);
(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
"pm-hardware-state", "no-suspend-resume");
list = fhc_bdlist_lock(softsp->board);
if (list == NULL) {
cmn_err(CE_PANIC, "ac%d: Board %d not found in database",
instance, softsp->board);
}
list->sc.ac_compid = *softsp->ac_id;
list->ac_softsp = softsp;
if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) {
if (ddi_create_minor_node(devi, NAME_BANK0, S_IFCHR,
(AC_PUTINSTANCE(instance) | 0),
DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
cmn_err(CE_WARN, "ac%d: \"%s\" "
"ddi_create_minor_node failed", instance,
NAME_BANK0);
}
if (ddi_create_minor_node(devi, NAME_BANK1, S_IFCHR,
(AC_PUTINSTANCE(instance) | 1),
DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
cmn_err(CE_WARN, "ac%d: \"%s\" "
"ddi_create_minor_node failed", instance,
NAME_BANK0);
}
fhc_del_memloc(softsp->board);
ac_get_memory_status(softsp, Bank0);
ac_get_memory_status(softsp, Bank1);
ac_eval_memory_status(softsp, Bank0);
ac_eval_memory_status(softsp, Bank1);
}
fhc_bdlist_unlock();
ac_add_kstats(softsp);
ddi_report_dev(devi);
return (DDI_SUCCESS);
bad:
ddi_soft_state_free(acp, instance);
return (DDI_FAILURE);
}
static int
ac_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
int instance;
struct ac_soft_state *softsp;
struct bd_list *list;
instance = ddi_get_instance(devi);
softsp = ddi_get_soft_state(acp, instance);
switch (cmd) {
case DDI_SUSPEND:
return (DDI_SUCCESS);
case DDI_DETACH:
list = fhc_bdlist_lock(softsp->board);
if (fhc_bd_detachable(softsp->board))
break;
else
fhc_bdlist_unlock();
default:
return (DDI_FAILURE);
}
ASSERT(list->ac_softsp == softsp);
if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) {
int cpui;
if (softsp->bank[Bank0].busy != 0 ||
softsp->bank[Bank0].ostate == SYSC_CFGA_OSTATE_CONFIGURED ||
softsp->bank[Bank1].busy != 0 ||
softsp->bank[Bank1].ostate == SYSC_CFGA_OSTATE_CONFIGURED) {
fhc_bdlist_unlock();
return (DDI_FAILURE);
}
for (cpui = 0; cpui < NCPU; cpui++)
xc_one(cpui, ac_ecache_flush, 0, 0);
}
list->ac_softsp = NULL;
ac_del_kstats(softsp);
ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0);
fhc_bdlist_unlock();
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(acp, instance);
ddi_prop_remove_all(devi);
return (DDI_SUCCESS);
}
static int
ac_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
int instance;
dev_t dev;
struct ac_soft_state *softsp;
struct bd_list *board;
int vis;
dev = *devp;
instance = AC_GETINSTANCE(getminor(dev));
softsp = AC_GETSOFTC(instance);
if (softsp == NULL) {
#ifdef DEBUG
cmn_err(CE_WARN, "ac%d device not attached", instance);
#endif
return (ENXIO);
}
board = fhc_bdlist_lock(softsp->board);
vis = (board != NULL) && MEM_BOARD_VISIBLE(board);
fhc_bdlist_unlock();
if (!vis)
return (ENXIO);
if (otyp != OTYP_CHR) {
return (EINVAL);
}
return (DDI_SUCCESS);
}
static int
ac_close(dev_t devt, int flag, int otyp, cred_t *credp)
{
struct ac_soft_state *softsp;
int instance;
instance = AC_GETINSTANCE(getminor(devt));
softsp = AC_GETSOFTC(instance);
ASSERT(softsp != NULL);
ac_mem_test_stop_on_close(softsp->board, AC_GETBANK(getminor(devt)));
return (DDI_SUCCESS);
}
static int
ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag)
{
#ifdef _MULTI_DATAMODEL
if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
ac_cfga_cmd32_t ac_cmd32;
if (ddi_copyin((void *)arg, &ac_cmd32,
sizeof (ac_cfga_cmd32_t), flag) != 0) {
return (EFAULT);
}
pkt->cmd_cfga.force = ac_cmd32.force;
pkt->cmd_cfga.test = ac_cmd32.test;
pkt->cmd_cfga.arg = ac_cmd32.arg;
pkt->cmd_cfga.errtype = ac_cmd32.errtype;
pkt->cmd_cfga.outputstr =
(char *)(uintptr_t)ac_cmd32.outputstr;
pkt->cmd_cfga.private =
(void *)(uintptr_t)ac_cmd32.private;
} else
#endif
if (ddi_copyin((void *)arg, &(pkt->cmd_cfga),
sizeof (ac_cfga_cmd_t), flag) != 0) {
return (EFAULT);
}
pkt->errbuf = kmem_zalloc(SYSC_OUTPUT_LEN, KM_SLEEP);
return (0);
}
static int
ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag)
{
int ret = TRUE;
#ifdef _MULTI_DATAMODEL
if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
if (ddi_copyout(&(pkt->cmd_cfga.errtype),
(void *)&(((ac_cfga_cmd32_t *)arg)->errtype),
sizeof (ac_err_t), flag) != 0) {
ret = FALSE;
}
} else
#endif
if (ddi_copyout(&(pkt->cmd_cfga.errtype),
(void *)&(((ac_cfga_cmd_t *)arg)->errtype),
sizeof (ac_err_t), flag) != 0) {
ret = FALSE;
}
if ((ret != FALSE) && ((pkt->cmd_cfga.outputstr != NULL) &&
(ddi_copyout(pkt->errbuf, pkt->cmd_cfga.outputstr,
SYSC_OUTPUT_LEN, flag) != 0))) {
ret = FALSE;
}
kmem_free(pkt->errbuf, SYSC_OUTPUT_LEN);
return (ret);
}
static int
ac_ioctl(
dev_t devt,
int cmd,
intptr_t arg,
int flag,
cred_t *cred_p,
int *rval_p)
{
struct ac_soft_state *softsp;
ac_cfga_pkt_t cfga_pkt, *pkt;
int instance;
int retval;
instance = AC_GETINSTANCE(getminor(devt));
softsp = AC_GETSOFTC(instance);
if (softsp == NULL) {
#ifdef DEBUG
cmn_err(CE_NOTE, "ac%d device not attached", instance);
#endif
return (ENXIO);
}
switch (cmd) {
case AC_MEM_ADMIN_VER:
if (ddi_copyout(&ac_mem_version, (void *)arg,
sizeof (ac_mem_version_t), flag) != 0)
return (EFAULT);
return (DDI_SUCCESS);
case AC_MEM_CONFIGURE:
case AC_MEM_UNCONFIGURE:
case AC_MEM_STAT:
case AC_MEM_TEST_START:
case AC_MEM_TEST_STOP:
case AC_MEM_TEST_READ:
case AC_MEM_TEST_WRITE:
case AC_MEM_EXERCISE:
break;
default:
return (ENOTTY);
}
if (cmd != AC_MEM_STAT && !fpu_exists) {
return (ENOTSUP);
}
pkt = &cfga_pkt;
if ((retval = ac_pkt_init(pkt, arg, flag)) != 0)
return (retval);
pkt->softsp = softsp;
pkt->bank = AC_GETBANK(getminor(devt));
switch (cmd) {
case AC_MEM_CONFIGURE:
if ((flag & FWRITE) == 0) {
retval = EBADF;
break;
}
if (pkt->cmd_cfga.private != NULL) {
retval = EINVAL;
break;
}
ac_policy_audit_messages(AC_AUDIT_OSTATE_CONFIGURE, pkt);
retval = ac_add_memory(pkt);
if (!retval)
ac_policy_audit_messages(
AC_AUDIT_OSTATE_SUCCEEDED, pkt);
else
ac_policy_audit_messages(
AC_AUDIT_OSTATE_CONFIGURE_FAILED, pkt);
break;
case AC_MEM_UNCONFIGURE:
if ((flag & FWRITE) == 0) {
retval = EBADF;
break;
}
if (pkt->cmd_cfga.private != NULL) {
retval = EINVAL;
break;
}
ac_policy_audit_messages(AC_AUDIT_OSTATE_UNCONFIGURE, pkt);
retval = ac_del_memory(pkt);
if (!retval) {
ac_policy_audit_messages(
AC_AUDIT_OSTATE_SUCCEEDED, pkt);
} else
ac_policy_audit_messages(
AC_AUDIT_OSTATE_UNCONFIGURE_FAILED, pkt);
break;
case AC_MEM_STAT:
retval = ac_mem_stat(pkt, flag);
break;
case AC_MEM_TEST_START:
if ((flag & FWRITE) == 0) {
retval = EBADF;
break;
}
retval = ac_mem_test_start(pkt, flag);
break;
case AC_MEM_TEST_STOP:
if ((flag & FWRITE) == 0) {
retval = EBADF;
break;
}
retval = ac_mem_test_stop(pkt, flag);
break;
case AC_MEM_TEST_READ:
if ((flag & FWRITE) == 0) {
retval = EBADF;
break;
}
retval = ac_mem_test_read(pkt, flag);
break;
case AC_MEM_TEST_WRITE:
if ((flag & FWRITE) == 0) {
retval = EBADF;
break;
}
retval = ac_mem_test_write(pkt, flag);
break;
case AC_MEM_EXERCISE:
retval = ac_mem_exercise(pkt, flag);
break;
default:
ASSERT(0);
retval = ENOTTY;
break;
}
if (ac_pkt_fini(pkt, arg, flag) != TRUE)
retval = EFAULT;
return (retval);
}
static void
ac_add_kstats(struct ac_soft_state *softsp)
{
struct kstat *ac_ksp, *ac_counters_ksp;
struct ac_kstat *ac_named_ksp;
struct kstat_named *ac_counters_named_data;
if ((ac_ksp = kstat_create("unix", softsp->board,
AC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
sizeof (struct ac_kstat) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT)) == NULL) {
cmn_err(CE_WARN, "ac%d: kstat_create failed",
ddi_get_instance(softsp->dip));
return;
}
ac_named_ksp = (struct ac_kstat *)(ac_ksp->ks_data);
kstat_named_init(&ac_named_ksp->ac_memctl,
MEMCTL_KSTAT_NAMED,
KSTAT_DATA_UINT64);
kstat_named_init(&ac_named_ksp->ac_memdecode0,
MEMDECODE0_KSTAT_NAMED,
KSTAT_DATA_UINT64);
kstat_named_init(&ac_named_ksp->ac_memdecode1,
MEMDECODE1_KSTAT_NAMED,
KSTAT_DATA_UINT64);
kstat_named_init(&ac_named_ksp->ac_mccr,
MCCR_KSTAT_NAMED,
KSTAT_DATA_UINT32);
kstat_named_init(&ac_named_ksp->ac_counter,
CNTR_KSTAT_NAMED,
KSTAT_DATA_UINT64);
kstat_named_init(&ac_named_ksp->ac_bank0_status,
BANK_0_KSTAT_NAMED,
KSTAT_DATA_CHAR);
kstat_named_init(&ac_named_ksp->ac_bank1_status,
BANK_1_KSTAT_NAMED,
KSTAT_DATA_CHAR);
ac_ksp->ks_update = ac_misc_kstat_update;
ac_ksp->ks_private = (void *)softsp;
softsp->ac_ksp = ac_ksp;
kstat_install(ac_ksp);
mutex_enter(&ac_attachcnt_mutex);
if (ac_attachcnt == 0)
ac_add_picN_kstats(softsp->dip);
ac_attachcnt ++;
mutex_exit(&ac_attachcnt_mutex);
if ((ac_counters_ksp = kstat_create("ac",
ddi_get_instance(softsp->dip), "counters",
"bus", KSTAT_TYPE_NAMED, AC_NUM_PICS + 1,
KSTAT_FLAG_WRITABLE)) == NULL) {
cmn_err(CE_WARN, "ac%d counters: kstat_create failed",
ddi_get_instance(softsp->dip));
return;
}
ac_counters_named_data =
(struct kstat_named *)(ac_counters_ksp->ks_data);
kstat_named_init(&ac_counters_named_data[0],
"pcr", KSTAT_DATA_UINT64);
kstat_named_init(&ac_counters_named_data[1],
"pic0", KSTAT_DATA_UINT64);
kstat_named_init(&ac_counters_named_data[2],
"pic1", KSTAT_DATA_UINT64);
ac_counters_ksp->ks_update = ac_counters_kstat_update;
ac_counters_ksp->ks_private = (void *)softsp;
kstat_install(ac_counters_ksp);
softsp->ac_counters_ksp = ac_counters_ksp;
}
static void
ac_add_picN_kstats(dev_info_t *dip)
{
typedef struct ac_event_mask {
char *event_name;
uint64_t pcr_mask;
} ac_event_mask_t;
ac_event_mask_t ac_events_arr[] = {
{"mem_bank0_rds", 0x1}, {"mem_bank0_wrs", 0x2},
{"mem_bank0_stall", 0x3}, {"mem_bank1_rds", 0x4},
{"mem_bank1_wrs", 0x5}, {"mem_bank1_stall", 0x6},
{"clock_cycles", 0x7}, {"addr_pkts", 0x8},
{"data_pkts", 0x9}, {"flow_ctl_cyc", 0xa},
{"fast_arb_pkts", 0xb}, {"bus_cont_cyc", 0xc},
{"data_bus_can", 0xd}, {"ac_addr_pkts", 0xe},
{"ac_data_pkts", 0xf}, {"rts_pkts", 0x10},
{"rtsa_pkts", 0x11}, {"rto_pkts", 0x12},
{"rs_pkts", 0x13}, {"wb_pkts", 0x14},
{"ws_pkts", 0x15}, {"rio_pkts", 0x16},
{"rbio_pkts", 0x17}, {"wio_pkts", 0x18},
{"wbio_pkts", 0x19}, {"upa_a_rds_m", 0x1a},
{"upa_a_rdo_v", 0x1b}, {"upa_b_rds_m", 0x1c},
{"upa_b_rdo_v", 0x1d}, {"upa_a_preqs_fr", 0x20},
{"upa_a_sreqs_to", 0x21}, {"upa_a_preqs_to", 0x22},
{"upa_a_rds_fr", 0x23}, {"upa_a_rdsa_fr", 0x24},
{"upa_a_rdo_fr", 0x25}, {"upa_a_rdd_fr", 0x26},
{"upa_a_rio_rbio", 0x27}, {"upa_a_wio_wbio", 0x28},
{"upa_a_cpb_to", 0x29}, {"upa_a_inv_to", 0x2a},
{"upa_a_hits_buff", 0x2b}, {"upa_a_wb", 0x2c},
{"upa_a_wi", 0x2d}, {"upa_b_preqs_fr", 0x30},
{"upa_b_sreqs_to", 0x31}, {"upa_b_preqs_to", 0x32},
{"upa_b_rds_fr", 0x33}, {"upa_b_rdsa_fr", 0x34},
{"upa_b_rdo_fr", 0x35}, {"upa_b_rdd_fr", 0x36},
{"upa_b_rio_rbio", 0x37}, {"upa_b_wio_wbio", 0x38},
{"upa_b_cpb_to", 0x39}, {"upa_b_inv_to", 0x3a},
{"upa_b_hits_buff", 0x3b}, {"upa_b_wb", 0x3c},
{"upa_b_wi", 0x3d}
};
#define AC_NUM_EVENTS sizeof (ac_events_arr) / sizeof (ac_events_arr[0])
ac_event_mask_t ac_clear_pic[AC_NUM_PICS] = {
{"clear_pic", (uint64_t)~(0x3f)},
{"clear_pic", (uint64_t)~(0x3f << 8)}
};
struct kstat_named *ac_pic_named_data;
int event, pic;
char pic_name[30];
int instance = ddi_get_instance(dip);
int pic_shift = 0;
for (pic = 0; pic < AC_NUM_PICS; pic++) {
(void) sprintf(pic_name, "pic%d", pic);
if ((ac_picN_ksp[pic] = kstat_create("ac",
instance, pic_name, "bus", KSTAT_TYPE_NAMED,
AC_NUM_EVENTS + 1, 0)) == NULL) {
cmn_err(CE_WARN, "ac %s: kstat_create failed",
pic_name);
if (pic == 1) {
kstat_delete(ac_picN_ksp[0]);
ac_picN_ksp[0] = NULL;
}
return;
}
ac_pic_named_data =
(struct kstat_named *)(ac_picN_ksp[pic]->ks_data);
if (pic == 1)
pic_shift = 8;
for (event = 0; event < AC_NUM_EVENTS; event ++) {
ac_pic_named_data[event].value.ui64 =
ac_events_arr[event].pcr_mask << pic_shift;
kstat_named_init(&ac_pic_named_data[event],
ac_events_arr[event].event_name,
KSTAT_DATA_UINT64);
}
ac_pic_named_data[AC_NUM_EVENTS].value.ui64 =
ac_clear_pic[pic].pcr_mask;
kstat_named_init(&ac_pic_named_data[AC_NUM_EVENTS],
ac_clear_pic[pic].event_name,
KSTAT_DATA_UINT64);
kstat_install(ac_picN_ksp[pic]);
}
}
static void
ac_del_kstats(struct ac_soft_state *softsp)
{
struct kstat *ac_ksp;
int pic;
ac_ksp = softsp->ac_ksp;
softsp->ac_ksp = NULL;
if (ac_ksp != NULL) {
ASSERT(ac_ksp->ks_private == (void *)softsp);
kstat_delete(ac_ksp);
}
ac_ksp = softsp->ac_counters_ksp;
softsp->ac_counters_ksp = NULL;
if (ac_ksp != NULL) {
ASSERT(ac_ksp->ks_private == (void *)softsp);
kstat_delete(ac_ksp);
}
mutex_enter(&ac_attachcnt_mutex);
ac_attachcnt --;
if (ac_attachcnt == 0) {
for (pic = 0; pic < AC_NUM_PICS; pic++) {
if (ac_picN_ksp[pic] != (kstat_t *)NULL) {
kstat_delete(ac_picN_ksp[pic]);
ac_picN_ksp[pic] = NULL;
}
}
}
mutex_exit(&ac_attachcnt_mutex);
}
static enum ac_bank_status
ac_kstat_stat(sysc_cfga_rstate_t rst, sysc_cfga_ostate_t ost)
{
switch (rst) {
case SYSC_CFGA_RSTATE_EMPTY:
return (StNoMem);
case SYSC_CFGA_RSTATE_DISCONNECTED:
return (StBad);
case SYSC_CFGA_RSTATE_CONNECTED:
switch (ost) {
case SYSC_CFGA_OSTATE_UNCONFIGURED:
return (StSpare);
case SYSC_CFGA_OSTATE_CONFIGURED:
return (StActive);
default:
return (StUnknown);
}
default:
return (StUnknown);
}
}
static enum ac_bank_condition
ac_kstat_cond(sysc_cfga_cond_t cond)
{
switch (cond) {
case SYSC_CFGA_COND_UNKNOWN:
return (ConUnknown);
case SYSC_CFGA_COND_OK:
return (ConOK);
case SYSC_CFGA_COND_FAILING:
return (ConFailing);
case SYSC_CFGA_COND_FAILED:
return (ConFailed);
case SYSC_CFGA_COND_UNUSABLE:
return (ConBad);
default:
return (ConUnknown);
}
}
static int
ac_misc_kstat_update(kstat_t *ksp, int rw)
{
struct ac_kstat *acksp;
struct ac_soft_state *softsp;
acksp = (struct ac_kstat *)ksp->ks_data;
softsp = (struct ac_soft_state *)ksp->ks_private;
ASSERT(softsp->ac_ksp == NULL || ksp == softsp->ac_ksp);
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
acksp->ac_memctl.value.ui64 = *softsp->ac_memctl;
acksp->ac_memdecode0.value.ui64 = *softsp->ac_memdecode0;
acksp->ac_memdecode1.value.ui64 = *softsp->ac_memdecode1;
acksp->ac_mccr.value.ui32 = *softsp->ac_mccr;
acksp->ac_counter.value.ui64 = *softsp->ac_counter;
acksp->ac_bank0_status.value.c[0] =
ac_kstat_stat(softsp->bank[0].rstate,
softsp->bank[0].ostate);
acksp->ac_bank0_status.value.c[1] =
ac_kstat_cond(softsp->bank[0].condition);
acksp->ac_bank1_status.value.c[0] =
ac_kstat_stat(softsp->bank[1].rstate,
softsp->bank[1].ostate);
acksp->ac_bank1_status.value.c[1] =
ac_kstat_cond(softsp->bank[1].condition);
}
return (0);
}
static int
ac_counters_kstat_update(kstat_t *ksp, int rw)
{
struct kstat_named *ac_counters_data;
struct ac_soft_state *softsp;
uint64_t pic_register;
ac_counters_data = (struct kstat_named *)ksp->ks_data;
softsp = (struct ac_soft_state *)ksp->ks_private;
if (ac_reset_timeout(rw) != 0)
return (-1);
if (rw == KSTAT_WRITE) {
*softsp->ac_mccr =
(uint32_t)ac_counters_data[0].value.ui64;
} else {
ac_counters_data[0].value.ui64 = *softsp->ac_mccr;
pic_register = *softsp->ac_counter;
ac_counters_data[1].value.ui64 =
AC_COUNTER_TO_PIC0(pic_register);
ac_counters_data[2].value.ui64 =
AC_COUNTER_TO_PIC1(pic_register);
}
return (0);
}
static void
ac_get_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id)
{
char *property = (id == Bank0) ? AC_BANK0_STATUS : AC_BANK1_STATUS;
char *propval;
int proplen;
uint64_t memdec = (id == Bank0) ?
*(softsp->ac_memdecode0) : *(softsp->ac_memdecode1);
uint_t grp_size;
softsp->bank[id].busy = 0;
softsp->bank[id].status_change = ddi_get_time();
if (GRP_SIZE_IS_SET(memdec)) {
grp_size = GRP_SPANMB(memdec);
softsp->bank[id].real_size = softsp->bank[id].use_size =
(id == Bank0) ? (grp_size / INTLV0(*softsp->ac_memctl)) :
(grp_size / INTLV1(*softsp->ac_memctl));
} else {
softsp->bank[id].real_size = softsp->bank[id].use_size = 0;
}
if (ddi_prop_op(DDI_DEV_T_ANY, softsp->dip, PROP_LEN_AND_VAL_ALLOC,
DDI_PROP_DONTPASS, property, (caddr_t)&propval, &proplen) ==
DDI_PROP_SUCCESS) {
if (strcmp(propval, AC_BANK_NOMEM) == 0) {
softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY;
softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
} else if (strcmp(propval, AC_BANK_OK) == 0) {
softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
softsp->bank[id].condition = SYSC_CFGA_COND_OK;
} else if (strcmp(propval, AC_BANK_SPARE) == 0) {
softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
} else if (strcmp(propval, AC_BANK_FAILED) == 0) {
softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE;
} else {
cmn_err(CE_WARN, "ac%d: board %d, bank %d: "
"unknown %smemory state [%s]",
ddi_get_instance(softsp->dip), softsp->board, id,
(memdec & AC_MEM_VALID) ? "connected " : "",
propval);
if (memdec & AC_MEM_VALID) {
softsp->bank[id].rstate =
SYSC_CFGA_RSTATE_CONNECTED;
softsp->bank[id].ostate =
SYSC_CFGA_OSTATE_CONFIGURED;
softsp->bank[id].condition =
SYSC_CFGA_COND_OK;
} else {
softsp->bank[id].rstate =
SYSC_CFGA_RSTATE_DISCONNECTED;
softsp->bank[id].ostate =
SYSC_CFGA_OSTATE_UNCONFIGURED;
softsp->bank[id].condition =
SYSC_CFGA_COND_UNUSABLE;
}
}
kmem_free(propval, proplen);
} else {
if (memdec & AC_MEM_VALID) {
softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
softsp->bank[id].condition = SYSC_CFGA_COND_OK;
} else {
softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY;
softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
}
}
if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) {
if ((memdec & AC_MEM_VALID) != 0) {
uint64_t base_pa;
ASSERT((*softsp->ac_memctl & AC_CSR_REFEN) != 0);
base_pa = GRP_REALBASE(memdec);
fhc_add_memloc(softsp->board, base_pa, grp_size);
}
}
}
static void
ac_eval_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id)
{
uint64_t memdec = (id == Bank0) ?
*(softsp->ac_memdecode0) : *(softsp->ac_memdecode1);
uint64_t base_pa;
if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED &&
softsp->bank[id].ostate == SYSC_CFGA_OSTATE_UNCONFIGURED &&
(memdec & AC_MEM_VALID) == 0) {
cmn_err(CE_WARN, "ac%d: board %d, bank %d: "
"spare memory bank not valid - it was ",
ddi_get_instance(softsp->dip), softsp->board, id);
cmn_err(CE_WARN, "misconfigured by the system "
"firmware. Disabling...");
softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE;
}
if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) {
ASSERT((memdec & AC_MEM_VALID) != 0);
base_pa = GRP_REALBASE(memdec);
cmn_err(CE_CONT, "?ac%d board %d bank %d: "
"base 0x%" PRIx64 " size %dmb rstate %d "
"ostate %d condition %d\n",
ddi_get_instance(softsp->dip),
softsp->board, id, base_pa, softsp->bank[id].real_size,
softsp->bank[id].rstate, softsp->bank[id].ostate,
softsp->bank[id].condition);
}
}
static void
ac_ecache_flush(uint64_t a, uint64_t b)
{
cpu_flush_ecache();
}
static char *
ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event)
{
char *type_str;
switch (ostate) {
case SYSC_CFGA_OSTATE_UNCONFIGURED:
switch (event) {
case AC_AUDIT_OSTATE_UNCONFIGURE:
type_str = "unconfiguring";
break;
case AC_AUDIT_OSTATE_SUCCEEDED:
case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED:
type_str = "unconfigured";
break;
default:
type_str = "unconfigure?";
break;
}
break;
case SYSC_CFGA_OSTATE_CONFIGURED:
switch (event) {
case AC_AUDIT_OSTATE_CONFIGURE:
type_str = "configuring";
break;
case AC_AUDIT_OSTATE_SUCCEEDED:
case AC_AUDIT_OSTATE_CONFIGURE_FAILED:
type_str = "configured";
break;
default:
type_str = "configure?";
break;
}
break;
default:
type_str = "undefined occupant state";
break;
}
return (type_str);
}
static void
ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt)
{
struct ac_soft_state *softsp = pkt->softsp;
switch (event) {
case AC_AUDIT_OSTATE_CONFIGURE:
cmn_err(CE_NOTE,
"%s memory bank %d in slot %d",
ac_ostate_typestr(SYSC_CFGA_OSTATE_CONFIGURED,
event), pkt->bank,
softsp->board);
break;
case AC_AUDIT_OSTATE_UNCONFIGURE:
cmn_err(CE_NOTE,
"%s memory bank %d in slot %d",
ac_ostate_typestr(
SYSC_CFGA_OSTATE_UNCONFIGURED,
event), pkt->bank,
softsp->board);
break;
case AC_AUDIT_OSTATE_SUCCEEDED:
cmn_err(CE_NOTE,
"memory bank %d in slot %d is %s",
pkt->bank, softsp->board,
ac_ostate_typestr(
softsp->bank[pkt->bank].ostate,
event));
break;
case AC_AUDIT_OSTATE_CONFIGURE_FAILED:
cmn_err(CE_NOTE,
"memory bank %d in slot %d not %s",
pkt->bank,
softsp->board,
ac_ostate_typestr(
SYSC_CFGA_OSTATE_CONFIGURED,
event));
break;
case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED:
cmn_err(CE_NOTE,
"memory bank %d in slot %d not %s",
pkt->bank,
softsp->board,
ac_ostate_typestr(
SYSC_CFGA_OSTATE_UNCONFIGURED,
event));
break;
default:
cmn_err(CE_NOTE,
"unknown audit of memory bank %d in slot %d",
pkt->bank, softsp->board);
break;
}
}
#include <vm/page.h>
#include <vm/hat.h>
static int
ac_mem_exercise(ac_cfga_pkt_t *pkt, int flag)
{
struct ac_mem_info *mem_info;
pfn_t base;
pgcnt_t npgs;
mem_info = &pkt->softsp->bank[pkt->bank];
if (mem_info->rstate == SYSC_CFGA_RSTATE_CONNECTED) {
uint64_t base_pa, bank_size;
uint64_t decode;
decode = (pkt->bank == Bank0) ?
*pkt->softsp->ac_memdecode0 : *pkt->softsp->ac_memdecode1;
base_pa = GRP_REALBASE(decode);
bank_size = GRP_UK2SPAN(decode);
base = base_pa >> PAGESHIFT;
npgs = bank_size >> PAGESHIFT;
} else {
base = 0;
npgs = 0;
}
switch (pkt->cmd_cfga.arg) {
case AC_MEMX_RELOCATE_ALL: {
pfn_t pfn, pglim;
struct ac_memx_relocate_stats rstat;
if (npgs == 0 ||
mem_info->ostate != SYSC_CFGA_OSTATE_CONFIGURED) {
return (EINVAL);
}
if (mem_info->busy != FALSE) {
return (EBUSY);
}
bzero(&rstat, sizeof (rstat));
rstat.base = (uint_t)base;
rstat.npgs = (uint_t)npgs;
pglim = base + npgs;
for (pfn = base; pfn < pglim; pfn++) {
page_t *pp, *pp_repl;
retry:
pp = page_numtopp_nolock(pfn);
if (pp != NULL) {
if (!page_trylock(pp, SE_EXCL)) {
pp = NULL;
rstat.nolock++;
}
if (pp != NULL && page_pptonum(pp) != pfn) {
page_unlock(pp);
goto retry;
}
} else {
rstat.nopaget++;
}
if (pp != NULL && PP_ISFREE(pp)) {
page_unlock(pp);
rstat.isfree++;
pp = NULL;
}
if (pp != NULL) {
spgcnt_t npgs;
int result;
pp_repl = NULL;
result = page_relocate(&pp, &pp_repl, 1, 1,
&npgs, NULL);
if (result == 0) {
while (npgs-- > 0) {
page_t *tpp;
ASSERT(pp_repl != NULL);
tpp = pp_repl;
page_sub(&pp_repl, tpp);
page_unlock(tpp);
}
rstat.reloc++;
} else {
page_unlock(pp);
rstat.noreloc++;
}
}
}
if (pkt->cmd_cfga.private != NULL && ddi_copyout(&rstat,
pkt->cmd_cfga.private, sizeof (rstat), flag) != 0)
return (EFAULT);
return (DDI_SUCCESS);
}
default:
return (EINVAL);
}
}
static int
ac_reset_timeout(int rw)
{
mutex_enter(&ac_hot_plug_mode_mutex);
if ((ac_hot_plug_timeout == (timeout_id_t)NULL) &&
(rw == KSTAT_READ)) {
mutex_exit(&ac_hot_plug_mode_mutex);
return (0);
} else if ((ac_hot_plug_timeout == (timeout_id_t)NULL) &&
(rw == KSTAT_WRITE)) {
if (ac_enter_transition() == TRUE)
fhc_bdlist_unlock();
else {
mutex_exit(&ac_hot_plug_mode_mutex);
return (-1);
}
cmn_err(CE_WARN, "This machine is being taken out of "
"hot-plug mode.");
cmn_err(CE_CONT, "Do not attempt to hot-plug boards "
"or power supplies in this system until further notice.");
} else if (ac_hot_plug_timeout != (timeout_id_t)NULL) {
(void) untimeout(ac_hot_plug_timeout);
ac_hot_plug_timeout = NULL;
}
ac_hot_plug_timeout = timeout(ac_timeout, NULL,
drv_usectohz(ac_hot_plug_timeout_interval * 1000000));
mutex_exit(&ac_hot_plug_mode_mutex);
return (0);
}
static void
ac_timeout(void *arg)
{
struct ac_soft_state *softsp;
fhc_bd_t *board;
#ifdef lint
arg = arg;
#endif
ac_hot_plug_timeout = (timeout_id_t)NULL;
(void) fhc_bdlist_lock(-1);
board = fhc_bd_first();
while (board != NULL) {
softsp = board->ac_softsp;
if (softsp == NULL) {
board = fhc_bd_next(board);
continue;
}
*softsp->ac_mccr = AC_CLEAR_PCR(*softsp->ac_mccr);
*softsp->ac_mccr = AC_SET_HOT_PLUG(*softsp->ac_mccr);
*softsp->ac_counter = AC_SET_PIC_BUS_PAUSE(softsp->board);
board = fhc_bd_next(board);
}
ac_exit_transition();
fhc_bdlist_unlock();
cmn_err(CE_NOTE, "This machine is now in hot-plug mode.");
cmn_err(CE_CONT, "Board and power supply hot-plug operations "
"can be resumed.");
}
static int
ac_enter_transition(void)
{
fhc_bd_t *list;
sysc_cfga_stat_t *sysc_stat_lk;
(void) fhc_bdlist_lock(-1);
list = fhc_bd_clock();
sysc_stat_lk = &list->sc;
if (sysc_stat_lk->in_transition == TRUE) {
fhc_bdlist_unlock();
return (FALSE);
} else {
sysc_stat_lk->in_transition = TRUE;
return (TRUE);
}
}
static void
ac_exit_transition(void)
{
fhc_bd_t *list;
sysc_cfga_stat_t *sysc_stat_lk;
ASSERT(fhc_bdlist_locked());
list = fhc_bd_clock();
sysc_stat_lk = &list->sc;
ASSERT(sysc_stat_lk->in_transition == TRUE);
sysc_stat_lk->in_transition = FALSE;
}