#include <sys/types.h>
#include <sys/systm.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/uadmin.h>
#include <sys/machsystm.h>
#include <sys/disp.h>
#include <sys/taskq.h>
#include <sys/sgevents.h>
#include <sys/sgsbbc_priv.h>
#include <sys/sgsbbc_iosram_priv.h>
#include <sys/sgsbbc_mailbox_priv.h>
#include <sys/plat_ecc_unum.h>
#include <sys/plat_ecc_dimm.h>
#include <sys/serengeti.h>
#include <sys/fm/util.h>
#include <sys/promif.h>
#include <sys/plat_datapath.h>
sbbc_mailbox_t *master_mbox = NULL;
static kmutex_t panic_hdlr_lock;
static ddi_softintr_t panic_softintr_id = 0;
static sg_panic_shutdown_t panic_payload;
static sbbc_msg_t panic_payload_msg;
static kcondvar_t outbox_queue;
static kmutex_t outbox_queue_lock;
static plat_capability_data_t cap_payload;
static sbbc_msg_t cap_payload_msg;
static kmutex_t cap_msg_hdlr_lock;
typedef struct {
uint8_t type;
uint8_t pad;
uint16_t cpuid;
uint32_t t_value;
} plat_datapath_info_t;
static ddi_softintr_t dp_softintr_id = 0;
static kmutex_t dp_hdlr_lock;
static plat_datapath_info_t dp_payload;
static sbbc_msg_t dp_payload_msg;
static char *dperrtype[] = {
DP_ERROR_CDS,
DP_ERROR_DX,
DP_ERROR_RP
};
static int outbox_busy = 0;
static int sbbc_mbox_send_msg(sbbc_msg_t *, int, uint_t, time_t, clock_t);
static int sbbc_mbox_recv_msg();
static int mbox_write(struct sbbc_mbox_header *,
struct sbbc_fragment *, sbbc_msg_t *);
static int mbox_read(struct sbbc_mbox_header *, struct sbbc_fragment *,
sbbc_msg_t *);
static int mbox_has_free_space(struct sbbc_mbox_header *);
static void mbox_skip_next_msg(struct sbbc_mbox_header *);
static int mbox_read_header(uint32_t, struct sbbc_mbox_header *);
static void mbox_update_header(uint32_t, struct sbbc_mbox_header *);
static int mbox_read_frag(struct sbbc_mbox_header *, struct sbbc_fragment *);
static struct sbbc_msg_waiter *mbox_find_waiter(uint16_t, uint32_t);
static void wakeup_next(void);
static uint_t sbbc_panic_shutdown_handler(char *arg);
static uint_t sbbc_do_fast_shutdown(char *arg);
static void sbbc_mbox_post_reg(sbbc_softstate_t *softsp);
static uint_t cap_ecc_msg_handler(char *);
static uint_t sbbc_datapath_error_msg_handler(char *arg);
static uint_t sbbc_datapath_fault_msg_handler(char *arg);
static uint_t sbbc_dp_trans_event(char *arg);
static int sbbc_mbox_msgin(void);
static int sbbc_mbox_msgout(void);
static int sbbc_mbox_spacein(void);
static int sbbc_mbox_spaceout(void);
static taskq_t *sbbc_ecc_mbox_taskq = NULL;
static int sbbc_ecc_mbox_taskq_errs = 0;
static int sbbc_ecc_mbox_send_errs = 0;
static int sbbc_ecc_mbox_inval_errs = 0;
static int sbbc_ecc_mbox_other_errs = 0;
int sbbc_ecc_mbox_err_throttle = ECC_MBOX_TASKQ_ERR_THROTTLE;
void
sbbc_mbox_init()
{
int i;
master_mbox = kmem_zalloc(sizeof (sbbc_mailbox_t), KM_NOSLEEP);
if (master_mbox == NULL) {
cmn_err(CE_PANIC, "Can't allocate memory for mailbox\n");
}
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
mutex_init(&master_mbox->mbox_wait_lock[i],
NULL, MUTEX_DEFAULT, NULL);
master_mbox->mbox_wait_list[i] = NULL;
}
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++)
master_mbox->intrs[i] = NULL;
master_mbox->mbox_in = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
if (master_mbox->mbox_in == NULL) {
cmn_err(CE_PANIC,
"Can't allocate memory for inbound mailbox\n");
}
master_mbox->mbox_out = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
if (master_mbox->mbox_out == NULL) {
cmn_err(CE_PANIC,
"Can't allocate memory for outbound mailbox\n");
}
mutex_init(&master_mbox->mbox_in->mb_lock, NULL,
MUTEX_DEFAULT, NULL);
mutex_init(&master_mbox->mbox_out->mb_lock, NULL,
MUTEX_DEFAULT, NULL);
mutex_init(&panic_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&dp_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
master_mbox->mbox_in->mb_type =
master_mbox->mbox_out->mb_type = 0;
cv_init(&outbox_queue, NULL, CV_DEFAULT, NULL);
mutex_init(&outbox_queue_lock, NULL, MUTEX_DEFAULT, NULL);
}
void
sbbc_mbox_fini()
{
int i;
int err;
if (sbbc_ecc_mbox_taskq != NULL) {
taskq_destroy(sbbc_ecc_mbox_taskq);
sbbc_ecc_mbox_taskq = NULL;
sbbc_ecc_mbox_taskq_errs = 0;
}
(void) iosram_unreg_intr(SBBC_MAILBOX_IN);
(void) iosram_unreg_intr(SBBC_MAILBOX_IN);
(void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_IN);
(void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_OUT);
if (panic_softintr_id != 0) {
ddi_remove_softintr(panic_softintr_id);
err = sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
sbbc_panic_shutdown_handler);
if (err != 0) {
cmn_err(CE_WARN, "Failed to unreg Panic Shutdown "
"handler. Err=%d", err);
}
}
if (dp_softintr_id != 0) {
ddi_remove_softintr(dp_softintr_id);
err = sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
sbbc_datapath_error_msg_handler);
err |= sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
sbbc_datapath_fault_msg_handler);
if (err != 0) {
cmn_err(CE_WARN, "Failed to unreg Datapath Error "
"handler. Err=%d", err);
}
}
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
mutex_destroy(&master_mbox->mbox_wait_lock[i]);
}
mutex_destroy(&master_mbox->mbox_in->mb_lock);
mutex_destroy(&master_mbox->mbox_out->mb_lock);
mutex_destroy(&panic_hdlr_lock);
mutex_destroy(&dp_hdlr_lock);
kmem_free(master_mbox->mbox_in, sizeof (sbbc_mbox_t));
kmem_free(master_mbox->mbox_out, sizeof (sbbc_mbox_t));
kmem_free(master_mbox, sizeof (sbbc_mailbox_t));
cv_destroy(&outbox_queue);
mutex_destroy(&outbox_queue_lock);
err = sbbc_mbox_unreg_intr(INFO_MBOX, cap_ecc_msg_handler);
if (err != 0) {
cmn_err(CE_WARN, "Failed to unregister capability message "
"handler. Err=%d", err);
}
mutex_destroy(&cap_msg_hdlr_lock);
}
int
sbbc_mbox_switch(sbbc_softstate_t *softsp)
{
sbbc_intrs_t *intr;
int msg_type;
int rc = 0;
int err;
if (master_mbox == NULL)
return (ENXIO);
ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
for (intr = master_mbox->intrs[msg_type]; intr != NULL;
intr = intr->sbbc_intr_next) {
if (intr->sbbc_intr_id) {
ddi_remove_softintr(intr->sbbc_intr_id);
if (ddi_add_softintr(softsp->dip,
DDI_SOFTINT_HIGH,
&intr->sbbc_intr_id, NULL, NULL,
intr->sbbc_handler, intr->sbbc_arg)
!= DDI_SUCCESS) {
cmn_err(CE_WARN,
"Can't add SBBC mailbox "
"softint for msg_type %x\n",
msg_type);
rc = ENXIO;
}
}
}
}
if (panic_softintr_id) {
ddi_remove_softintr(panic_softintr_id);
err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
&panic_softintr_id, NULL, NULL,
sbbc_do_fast_shutdown, NULL);
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to register Panic "
"Shutdown handler. Err=%d", err);
(void) sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
sbbc_panic_shutdown_handler);
rc = ENXIO;
}
}
if (dp_softintr_id) {
ddi_remove_softintr(dp_softintr_id);
err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
&dp_softintr_id, NULL, NULL,
sbbc_dp_trans_event, NULL);
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to register Datapath "
"Error Event handler. Err=%d", err);
(void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
sbbc_datapath_error_msg_handler);
(void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
sbbc_datapath_fault_msg_handler);
rc = ENXIO;
}
}
return (rc);
}
int
sbbc_mbox_create(sbbc_softstate_t *softsp)
{
struct sbbc_mbox_header header;
int i;
int err;
int rc = 0;
ASSERT(MUTEX_HELD(&chosen_lock));
if (master_mbox == NULL)
return (ENXIO);
if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)&header,
sizeof (struct sbbc_mbox_header))) {
return (rc);
}
for (i = 0; i < MBOX_INTRS; i++) {
sbbc_intrfunc_t intr_handler;
uint_t *state;
kmutex_t *lock;
uint32_t intr_num;
switch (i) {
case MBOX_MSGIN_INTR:
intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgin;
intr_num = SBBC_MAILBOX_IN;
break;
case MBOX_MSGOUT_INTR:
intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgout;
intr_num = SBBC_MAILBOX_OUT;
break;
case MBOX_SPACEIN_INTR:
intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spacein;
intr_num = SBBC_MAILBOX_SPACE_IN;
break;
case MBOX_SPACEOUT_INTR:
intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spaceout;
intr_num = SBBC_MAILBOX_SPACE_OUT;
break;
}
state = (uint_t *)&master_mbox->intr_state[i].mbox_intr_state;
lock = &master_mbox->intr_state[i].mbox_intr_lock;
if (iosram_reg_intr(intr_num, intr_handler, (caddr_t)NULL,
state, lock)) {
cmn_err(CE_WARN,
"Can't register Mailbox interrupts \n");
}
}
panic_payload_msg.msg_buf = (caddr_t)&panic_payload;
panic_payload_msg.msg_len = sizeof (panic_payload);
err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &panic_softintr_id,
NULL, NULL, sbbc_do_fast_shutdown, NULL);
if (err == DDI_SUCCESS) {
err = sbbc_mbox_reg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
sbbc_panic_shutdown_handler, &panic_payload_msg,
NULL, &panic_hdlr_lock);
if (err != 0) {
cmn_err(CE_WARN, "Failed to register Panic "
"Shutdown handler. Err=%d", err);
}
} else {
cmn_err(CE_WARN, "Failed to add Panic Shutdown "
"softintr handler");
}
dp_payload_msg.msg_buf = (caddr_t)&dp_payload;
dp_payload_msg.msg_len = sizeof (dp_payload);
err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &dp_softintr_id,
NULL, NULL, sbbc_dp_trans_event, NULL);
if (err == DDI_SUCCESS) {
err = sbbc_mbox_reg_intr(MBOX_EVENT_DP_ERROR,
sbbc_datapath_error_msg_handler, &dp_payload_msg,
NULL, &dp_hdlr_lock);
err |= sbbc_mbox_reg_intr(MBOX_EVENT_DP_FAULT,
sbbc_datapath_fault_msg_handler, &dp_payload_msg,
NULL, &dp_hdlr_lock);
if (err != 0) {
cmn_err(CE_WARN, "Failed to register Datapath "
"error handler. Err=%d", err);
}
} else {
cmn_err(CE_WARN, "Failed to add Datapath error "
"softintr handler");
}
cap_payload_msg.msg_buf = (caddr_t)&cap_payload;
cap_payload_msg.msg_len = sizeof (cap_payload);
err = sbbc_mbox_reg_intr(INFO_MBOX, cap_ecc_msg_handler,
&cap_payload_msg, NULL, &cap_msg_hdlr_lock);
if (err != 0) {
cmn_err(CE_WARN, "Failed to register capability message"
" handler with Err=%d", err);
}
sbbc_mbox_post_reg(softsp);
return (rc);
}
static void
sbbc_mbox_post_reg(sbbc_softstate_t *softsp)
{
uint32_t msg_type;
sbbc_intrs_t *intr;
ASSERT(master_mbox);
for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
intr = master_mbox->intrs[msg_type];
while (intr != NULL) {
if (!intr->registered) {
SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_post_reg: "
"postreg for msgtype=%x\n", msg_type);
if (ddi_add_softintr(softsp->dip,
DDI_SOFTINT_HIGH, &intr->sbbc_intr_id,
NULL, NULL, intr->sbbc_handler,
(caddr_t)intr->sbbc_arg)
!= DDI_SUCCESS) {
cmn_err(CE_WARN, "Can't add SBBC "
"deferred mailbox softint \n");
} else
intr->registered = 1;
}
intr = intr->sbbc_intr_next;
}
}
}
int
sbbc_mbox_reg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler,
sbbc_msg_t *arg, uint_t *state, kmutex_t *lock)
{
sbbc_intrs_t *intr, *previntr;
int rc = 0;
if (msg_type >= SBBC_MBOX_MSG_TYPES)
return (EINVAL);
if (master_iosram == NULL || master_mbox == NULL)
return (ENXIO);
mutex_enter(&master_iosram->iosram_lock);
msg_type &= SBBC_MSG_TYPE_MASK;
previntr = intr = master_mbox->intrs[msg_type];
while (intr != NULL && intr->sbbc_handler != intr_handler) {
previntr = intr;
intr = intr->sbbc_intr_next;
}
if (intr != NULL) {
mutex_exit(&master_iosram->iosram_lock);
return (EBUSY);
}
intr = kmem_zalloc(sizeof (sbbc_intrs_t), KM_SLEEP);
intr->sbbc_handler = intr_handler;
intr->sbbc_arg = (caddr_t)arg;
intr->sbbc_intr_state = state;
intr->sbbc_intr_lock = lock;
intr->sbbc_intr_next = NULL;
intr->registered = 0;
if (previntr != NULL)
previntr->sbbc_intr_next = intr;
else
master_mbox->intrs[msg_type] = intr;
if (master_iosram->iosram_sbbc) {
if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
DDI_SOFTINT_HIGH,
&intr->sbbc_intr_id, NULL, NULL,
intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Can't add SBBC mailbox softint \n");
rc = ENXIO;
} else
intr->registered = 1;
} else {
SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_reg_intr: "
"deferring msg=%x registration\n", msg_type);
}
mutex_exit(&master_iosram->iosram_lock);
return (rc);
}
int
sbbc_mbox_unreg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler)
{
sbbc_intrs_t *intr, *previntr, *nextintr;
if (master_iosram == NULL || master_mbox == NULL)
return (ENXIO);
msg_type &= SBBC_MSG_TYPE_MASK;
if (msg_type >= SBBC_MBOX_MSG_TYPES ||
intr_handler == (sbbc_intrfunc_t)NULL) {
return (EINVAL);
}
mutex_enter(&master_iosram->iosram_lock);
previntr = intr = master_mbox->intrs[msg_type];
if (intr == NULL) {
mutex_exit(&master_iosram->iosram_lock);
return (EINVAL);
}
while (intr != NULL) {
nextintr = intr->sbbc_intr_next;
if (intr->sbbc_handler == intr_handler) {
if (intr->sbbc_intr_id)
ddi_remove_softintr(intr->sbbc_intr_id);
kmem_free(intr, sizeof (sbbc_intrs_t));
if (previntr != master_mbox->intrs[msg_type])
previntr->sbbc_intr_next = nextintr;
else
master_mbox->intrs[msg_type] = nextintr;
break;
}
previntr = intr;
intr = nextintr;
}
mutex_exit(&master_iosram->iosram_lock);
return (0);
}
static int
sbbc_mbox_msgin()
{
mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_state =
SBBC_INTR_RUNNING;
mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
mutex_enter(&master_mbox->mbox_in->mb_lock);
for (;;) {
while (sbbc_mbox_recv_msg() == 0)
;
(void) iosram_send_intr(SBBC_MAILBOX_SPACE_IN);
mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].
mbox_intr_lock);
if (sbbc_mbox_recv_msg() == 0) {
mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
mbox_intr_lock);
} else {
master_mbox->intr_state[MBOX_MSGIN_INTR].
mbox_intr_state = SBBC_INTR_IDLE;
mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
mbox_intr_lock);
break;
}
}
mutex_exit(&master_mbox->mbox_in->mb_lock);
return (DDI_INTR_CLAIMED);
}
static int
sbbc_mbox_msgout()
{
return (DDI_INTR_CLAIMED);
}
static int
sbbc_mbox_spacein()
{
return (DDI_INTR_CLAIMED);
}
static int
sbbc_mbox_spaceout()
{
mutex_enter(&master_mbox->mbox_out->mb_lock);
cv_broadcast(&master_mbox->mbox_out->mb_full);
mutex_exit(&master_mbox->mbox_out->mb_lock);
return (DDI_INTR_CLAIMED);
}
int
sbbc_mbox_request_response(sbbc_msg_t *request,
sbbc_msg_t *response, time_t wait_time)
{
struct sbbc_msg_waiter *waiter;
uint_t msg_id;
int rc = 0;
int flags;
uint16_t msg_type;
clock_t stop_time;
clock_t clockleft;
kmutex_t *mbox_wait_lock;
kmutex_t *mb_lock;
static fn_t f = "sbbc_mbox_request_response";
if ((request == NULL) ||
(request->msg_type.type >= SBBC_MBOX_MSG_TYPES) ||
((response != NULL) &&
(response->msg_type.type >= SBBC_MBOX_MSG_TYPES)))
return (EINVAL);
msg_type = request->msg_type.type;
if (master_mbox == NULL)
return (ENXIO);
mbox_wait_lock = &master_mbox->mbox_wait_lock[msg_type];
flags = WAIT_FOR_REPLY|WAIT_FOR_SPACE;
if (wait_time < sbbc_mbox_min_timeout)
wait_time = sbbc_mbox_default_timeout;
stop_time = ddi_get_lbolt() + wait_time * drv_usectohz(MICROSEC);
mutex_enter(&outbox_queue_lock);
msg_id = ++(master_mbox->mbox_msg_id);
if (msg_id == 0)
msg_id = ++(master_mbox->mbox_msg_id);
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, msg_len = 0x%x\n",
f, msg_id, request->msg_len);
while (outbox_busy) {
clockleft = cv_timedwait(&outbox_queue, &outbox_queue_lock,
stop_time);
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up\n", f, msg_id);
if (clockleft < 0) {
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
f, msg_id);
cmn_err(CE_NOTE,
"Timed out obtaining SBBC outbox lock");
request->msg_status = ETIMEDOUT;
if (response != NULL)
response->msg_status = ETIMEDOUT;
mutex_exit(&outbox_queue_lock);
return (ETIMEDOUT);
}
}
outbox_busy = 1;
mutex_exit(&outbox_queue_lock);
mb_lock = &master_mbox->mbox_out->mb_lock;
mutex_enter(mb_lock);
if (response == NULL) {
rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time,
stop_time);
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
f, msg_id, rc);
wakeup_next();
mutex_exit(mb_lock);
request->msg_status = rc;
return (rc);
}
waiter = kmem_zalloc(sizeof (struct sbbc_msg_waiter), KM_NOSLEEP);
if (waiter == (struct sbbc_msg_waiter *)NULL) {
cmn_err(CE_WARN, "SBBC Mailbox can't allocate waiter\n");
wakeup_next();
mutex_exit(mb_lock);
return (ENOMEM);
}
waiter->w_id = 0;
waiter->w_msg = response;
waiter->w_msg->msg_status = EINPROGRESS;
cv_init(&waiter->w_cv, NULL, CV_DEFAULT, NULL);
rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time, stop_time);
wakeup_next();
if (rc != 0) {
request->msg_status = response->msg_status = rc;
mutex_exit(mb_lock);
cv_destroy(&waiter->w_cv);
kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
f, msg_id, rc);
return (rc);
}
waiter->w_id = msg_id;
mutex_enter(mbox_wait_lock);
if (master_mbox->mbox_wait_list[msg_type] == NULL) {
master_mbox->mbox_wait_list[msg_type] = waiter;
waiter->w_next = NULL;
} else {
struct sbbc_msg_waiter *tmp;
tmp = master_mbox->mbox_wait_list[msg_type];
master_mbox->mbox_wait_list[msg_type] = waiter;
waiter->w_next = tmp;
}
mutex_exit(mb_lock);
clockleft = cv_timedwait(&waiter->w_cv, mbox_wait_lock, stop_time);
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up for response\n",
f, msg_id);
if (clockleft < 0) {
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
f, msg_id);
if (mbox_find_waiter(msg_type, msg_id) == NULL) {
if (waiter->w_msg->msg_status == EINPROGRESS) {
SGSBBC_DBG_MBOX("%s: Waiting for msg_id = 0x%x "
"complete.\n", f, msg_id);
cv_wait(&waiter->w_cv, mbox_wait_lock);
}
} else {
SGSBBC_DBG_MBOX("%s: setting msg_id = 0x%x "
"to ETIMEDOUT\n", f, msg_id);
cmn_err(CE_NOTE, "Timed out waiting for SC response");
rc = waiter->w_msg->msg_status = ETIMEDOUT;
}
}
cv_destroy(&waiter->w_cv);
kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
mutex_exit(mbox_wait_lock);
return (rc);
}
static void
wakeup_next()
{
mutex_enter(&outbox_queue_lock);
outbox_busy = 0;
cv_signal(&outbox_queue);
mutex_exit(&outbox_queue_lock);
}
int
sbbc_mbox_send_msg(sbbc_msg_t *msg, int flags, uint_t msg_id,
time_t wait_time, clock_t stop_time)
{
struct sbbc_mbox_header header;
struct sbbc_fragment frag;
int rc = 0;
int bytes_written;
uint32_t intr_enabled;
clock_t clockleft;
static fn_t f = "sbbc_mbox_send_msg";
rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
(caddr_t)&intr_enabled, sizeof (intr_enabled));
if (rc)
return (rc);
if (!(intr_enabled & SBBC_MAILBOX_OUT))
return (ENOTSUP);
if (rc = mbox_read_header(SBBC_OUTBOX, &header))
return (rc);
frag.f_id = msg_id;
frag.f_type = msg->msg_type;
frag.f_status = 0;
frag.f_total_len = msg->msg_len;
frag.f_frag_offset = 0;
bcopy(&msg->msg_data, &frag.f_data, sizeof (msg->msg_data));
bytes_written = 0;
do {
rc = mbox_write(&header, &frag, msg);
if (rc != 0 && rc != ENOSPC) {
return (rc);
}
if (rc == 0) {
(void) iosram_send_intr(SBBC_MAILBOX_OUT);
}
bytes_written += frag.f_frag_len;
frag.f_frag_offset += frag.f_frag_len;
if ((bytes_written < msg->msg_len) || (rc == ENOSPC)) {
if (mbox_has_free_space(&header) <=
sizeof (struct sbbc_fragment)) {
int tmprc;
clockleft = cv_timedwait(
&master_mbox->mbox_out->mb_full,
&master_mbox->mbox_out->mb_lock,
stop_time);
if (clockleft < 0) {
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x "
"has timed out\n", f, msg_id);
cmn_err(CE_NOTE,
"Timed out sending message "
"to SC");
return (ETIMEDOUT);
}
if (tmprc = mbox_read_header(SBBC_OUTBOX,
&header)) {
return (tmprc);
}
}
}
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, bytes_written = 0x%x, "
"msg_len = 0x%x\n", f,
msg_id, bytes_written, msg->msg_len);
} while ((bytes_written < msg->msg_len) || (rc == ENOSPC));
(void) iosram_send_intr(SBBC_MAILBOX_OUT);
return (rc);
}
int
sbbc_mbox_recv_msg()
{
struct sbbc_mbox_header header;
struct sbbc_fragment frag;
sbbc_msg_t tmpmsg;
int rc = 0, i, first_hdlr, last_hdlr;
uint32_t intr_enabled;
sbbc_intrs_t *intr;
struct sbbc_msg_waiter *waiter;
uint16_t type;
uint32_t f_id;
uint32_t f_frag_offset, f_frag_len;
kmutex_t *mbox_wait_lock;
static fn_t f = "sbbc_mbox_recv_msg";
rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
(caddr_t)&intr_enabled, sizeof (intr_enabled));
if (rc) {
return (rc);
}
if (!(intr_enabled & SBBC_MAILBOX_IN))
return (ENOTSUP);
if (rc = mbox_read_header(SBBC_INBOX, &header))
return (rc);
if ((header.mailboxes[SBBC_INBOX].mbox_consumer ==
header.mailboxes[SBBC_INBOX].mbox_producer)) {
return (-1);
}
if (rc = mbox_read_frag(&header, &frag)) {
return (rc);
}
type = frag.f_type.type;
f_id = frag.f_id;
SGSBBC_DBG_MBOX("%s: f_id = 0x%x\n", f, f_id);
if (type >= SBBC_MBOX_MSG_TYPES) {
goto done;
}
if (type == SBBC_BROADCAST_MSG) {
first_hdlr = 0;
last_hdlr = SBBC_MBOX_MSG_TYPES - 1;
} else if ((master_mbox->mbox_wait_list[type] == NULL) || (f_id == 0)) {
first_hdlr = last_hdlr = type;
} else {
mbox_wait_lock = &master_mbox->mbox_wait_lock[type];
mutex_enter(mbox_wait_lock);
if ((waiter = mbox_find_waiter(type, f_id)) == NULL) {
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
if (i == type)
continue;
if ((waiter = mbox_find_waiter(i, f_id))
!= NULL)
break;
}
}
mutex_exit(mbox_wait_lock);
if (waiter == NULL) {
rc = -1;
first_hdlr = last_hdlr = type;
} else {
SGSBBC_DBG_MBOX("%s: f_id = 0x%x, msg_id = 0x%x, "
"msg_len = 0x%x\n",
f, f_id, waiter->w_id,
waiter->w_msg->msg_len);
rc = mbox_read(&header, &frag, waiter->w_msg);
SGSBBC_DBG_MBOX("%s: f_id = 0x%x, offset = 0x%x, "
"len = 0x%x, total_len = 0x%x\n",
f, frag.f_id, frag.f_frag_offset,
frag.f_frag_len, frag.f_total_len);
if (rc || ((frag.f_frag_offset + frag.f_frag_len) ==
frag.f_total_len)) {
mutex_enter(mbox_wait_lock);
waiter->w_msg->msg_status = (rc == ENOMEM)?
rc : frag.f_status;
SGSBBC_DBG_MBOX("%s: msg_status = %d\n",
f, waiter->w_msg->msg_status);
cv_signal(&waiter->w_cv);
mutex_exit(mbox_wait_lock);
} else {
mutex_enter(mbox_wait_lock);
if (waiter->w_msg->msg_status == ETIMEDOUT) {
cv_signal(&waiter->w_cv);
mutex_exit(mbox_wait_lock);
goto done;
}
if (master_mbox->mbox_wait_list[type] == NULL) {
master_mbox->mbox_wait_list[type] =
waiter;
waiter->w_next = NULL;
} else {
struct sbbc_msg_waiter *tmp;
tmp = master_mbox->mbox_wait_list[type];
master_mbox->mbox_wait_list[type] =
waiter;
waiter->w_next = tmp;
}
mutex_exit(mbox_wait_lock);
}
goto done;
}
}
f_frag_len = tmpmsg.msg_len = frag.f_frag_len;
f_frag_offset = frag.f_frag_offset;
frag.f_frag_offset = 0;
if (f_frag_len != 0 && (tmpmsg.msg_buf =
kmem_alloc(f_frag_len, KM_NOSLEEP)) == NULL) {
rc = ENOMEM;
cmn_err(CE_WARN, "Can't allocate memory"
" for unsolicited messages\n");
} else {
rc = mbox_read(&header, &frag, &tmpmsg);
for (i = first_hdlr; rc == 0 && i <= last_hdlr; i++) {
intr = master_mbox->intrs[i];
if ((intr == NULL) || (intr->sbbc_intr_id == 0)) {
continue;
}
while (intr != NULL) {
sbbc_msg_t *arg = (sbbc_msg_t *)intr->sbbc_arg;
if (arg != (void *)NULL) {
if (arg->msg_len >= frag.f_total_len) {
if (f_frag_len > 0)
bcopy(tmpmsg.msg_buf,
arg->msg_buf +
f_frag_offset,
f_frag_len);
} else {
arg->msg_status = ENOMEM;
}
}
if (f_frag_offset + f_frag_len ==
frag.f_total_len) {
ddi_trigger_softintr(
intr->sbbc_intr_id);
}
intr = intr->sbbc_intr_next;
}
}
if (f_frag_len != 0) {
kmem_free(tmpmsg.msg_buf, f_frag_len);
}
}
done:
mbox_skip_next_msg(&header);
return (rc);
}
static int
mbox_has_free_space(struct sbbc_mbox_header *header)
{
uint32_t space = 0;
ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
if (header->mailboxes[SBBC_OUTBOX].mbox_producer ==
header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
space += header->mailboxes[SBBC_OUTBOX].mbox_len -
header->mailboxes[SBBC_OUTBOX].mbox_producer;
space +=
header->mailboxes[SBBC_OUTBOX].mbox_producer;
} else if (header->mailboxes[SBBC_OUTBOX].mbox_producer >
header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
space += header->mailboxes[SBBC_OUTBOX].mbox_len -
header->mailboxes[SBBC_OUTBOX].mbox_producer;
space += header->mailboxes[SBBC_OUTBOX].mbox_consumer;
} else {
space += header->mailboxes[SBBC_OUTBOX].mbox_consumer -
header->mailboxes[SBBC_OUTBOX].mbox_producer;
}
if (space >= MBOX_ALIGN_BYTES)
space -= MBOX_ALIGN_BYTES;
else
space = 0;
return (space);
}
static int
mbox_write(struct sbbc_mbox_header *header,
struct sbbc_fragment *frag, sbbc_msg_t *msg)
{
int bytes_written, bytes_remaining, free_space;
int rc = 0;
caddr_t src;
uint32_t sram_dst;
int space_at_end, space_at_start;
uint32_t mbox_offset, mbox_len;
uint32_t mbox_producer, mbox_consumer;
uint32_t f_total_len, f_frag_offset;
uint32_t frag_header_size;
static fn_t f = "mbox_write";
ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
mbox_offset = header->mailboxes[SBBC_OUTBOX].mbox_offset;
mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
mbox_producer = header->mailboxes[SBBC_OUTBOX].mbox_producer;
mbox_consumer = header->mailboxes[SBBC_OUTBOX].mbox_consumer;
f_total_len = frag->f_total_len;
f_frag_offset = frag->f_frag_offset;
frag_header_size = sizeof (struct sbbc_fragment);
SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, "
"mbox_producer = 0x%x\n", f, mbox_consumer, mbox_producer);
sram_dst = mbox_offset + mbox_producer;
if (mbox_producer < mbox_consumer) {
space_at_end = mbox_consumer - mbox_producer - MBOX_ALIGN_BYTES;
if (space_at_end < 0)
space_at_end = 0;
space_at_start = 0;
} else {
space_at_end = mbox_len - mbox_producer;
if (mbox_consumer == 0)
space_at_end -= MBOX_ALIGN_BYTES;
space_at_start = mbox_consumer - MBOX_ALIGN_BYTES;
if (space_at_start < 0)
space_at_start = 0;
}
SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
f, space_at_end, space_at_start);
free_space = space_at_end + space_at_start;
if (free_space < frag_header_size) {
frag->f_frag_len = 0;
return (ENOSPC);
}
bytes_remaining = f_total_len - f_frag_offset;
frag->f_frag_len = min(bytes_remaining, free_space - frag_header_size);
SGSBBC_DBG_MBOX("%s: writing header:sram_dst = 0x%x\n",
f, sram_dst);
if (space_at_end >= frag_header_size) {
rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, (caddr_t)frag,
frag_header_size);
if (rc)
return (rc);
sram_dst = (uint32_t)(sram_dst + frag_header_size);
if (sram_dst >= (mbox_len + mbox_offset)) {
sram_dst = mbox_offset;
}
space_at_end -= frag_header_size;
} else {
if (space_at_end) {
rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
(caddr_t)frag, space_at_end);
if (rc)
return (rc);
sram_dst = (uint32_t)mbox_offset;
}
rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
(caddr_t)((caddr_t)frag + space_at_end),
(frag_header_size - space_at_end));
if (rc)
return (rc);
sram_dst += frag_header_size - space_at_end;
space_at_start -= (frag_header_size - space_at_end);
space_at_end = 0;
}
SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
f, space_at_end, space_at_start);
free_space -= frag_header_size;
src = (caddr_t)(msg->msg_buf + f_frag_offset);
bytes_written = 0;
if (space_at_end) {
SGSBBC_DBG_MBOX("%s: writing data:sram_dst = 0x%x, "
"bytes_remaining = 0x%x\n",
f, sram_dst, bytes_remaining);
if (space_at_end < bytes_remaining)
bytes_written = space_at_end;
else
bytes_written = bytes_remaining;
rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
bytes_written);
if (rc)
return (rc);
sram_dst = (uint32_t)(sram_dst + bytes_written);
if (sram_dst >= (mbox_len + mbox_offset)) {
sram_dst = mbox_offset;
}
src = (caddr_t)(src + bytes_written);
bytes_remaining -= bytes_written;
}
if ((bytes_remaining > 0) && space_at_start) {
SGSBBC_DBG_MBOX("%s: writing the rest:sram_dst = 0x%x, "
"bytes_remaining = 0x%x\n",
f, sram_dst, bytes_remaining);
if (space_at_start < bytes_remaining) {
rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
space_at_start);
bytes_written += space_at_start;
} else {
rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
bytes_remaining);
bytes_written += bytes_remaining;
}
if (rc)
return (rc);
}
frag->f_frag_len = bytes_written;
sram_dst = mbox_producer + bytes_written + frag_header_size;
if (sram_dst >= mbox_len) {
sram_dst = sram_dst % mbox_len;
}
SGSBBC_DBG_MBOX("%s: after writing data:sram_dst = 0x%x, "
"bytes_written = 0x%x\n", f, sram_dst, bytes_written);
header->mailboxes[SBBC_OUTBOX].mbox_producer = sram_dst;
mbox_update_header(SBBC_OUTBOX, header);
return (rc);
}
static int
mbox_read(struct sbbc_mbox_header *header,
struct sbbc_fragment *frag, sbbc_msg_t *msg)
{
int rc = 0;
uint32_t sram_src, sram_end;
caddr_t msg_buf;
int bytes_at_start, bytes_at_end;
int bytes_to_read;
uint32_t frag_header_size, frag_total_size;
uint32_t f_frag_offset, f_frag_len;
uint32_t mbox_producer, mbox_consumer;
uint32_t mbox_len, mbox_offset;
static fn_t f = "mbox_read";
ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
mbox_producer = header->mailboxes[SBBC_INBOX].mbox_producer;
mbox_consumer = header->mailboxes[SBBC_INBOX].mbox_consumer;
mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
mbox_offset = header->mailboxes[SBBC_INBOX].mbox_offset;
frag_header_size = sizeof (struct sbbc_fragment);
f_frag_offset = frag->f_frag_offset;
f_frag_len = frag->f_frag_len;
frag_total_size = frag_header_size + f_frag_len;
if (msg->msg_len < f_frag_len) {
rc = ENOMEM;
goto done;
}
msg_buf = (caddr_t)(msg->msg_buf + f_frag_offset);
bcopy(&frag->f_data, &msg->msg_data, sizeof (msg->msg_data));
sram_end = (uint32_t)(mbox_offset + mbox_len);
sram_src = (uint32_t)(mbox_offset + mbox_consumer + frag_header_size);
if (sram_src >= sram_end)
sram_src -= mbox_len;
SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, mbox_producer = 0x%x, "
"frag_len = 0x%x\n",
f, mbox_consumer, mbox_producer, f_frag_len);
if (mbox_producer == mbox_consumer) {
bytes_at_end = bytes_at_start = 0;
} else if (mbox_producer < mbox_consumer) {
bytes_at_end = mbox_len - mbox_consumer;
bytes_at_start = mbox_producer;
} else {
bytes_at_end = mbox_producer - mbox_consumer;
bytes_at_start = 0;
}
SGSBBC_DBG_MBOX("%s: bytes_at_end = 0x%x, "
"bytes_at_start = 0x%x\n", f, bytes_at_end, bytes_at_start);
if ((bytes_at_end + bytes_at_start) < frag_total_size) {
cmn_err(CE_PANIC, "Corrupt INBOX!\n"
"producer = %x, consumer = %x, bytes_at_start = %x, "
"bytes_at_end = %x\n", mbox_producer, mbox_consumer,
bytes_at_start, bytes_at_end);
}
if (bytes_at_end > frag_header_size) {
bytes_at_end -= frag_header_size;
bytes_to_read = (bytes_at_end >= f_frag_len)?
f_frag_len : bytes_at_end;
SGSBBC_DBG_MBOX("%s: reading data: sram_src = 0x%x, "
"bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
bytes_to_read);
if (rc) {
goto done;
}
sram_src = (uint32_t)mbox_offset;
msg_buf = (caddr_t)(msg_buf + bytes_to_read);
bytes_to_read = f_frag_len - bytes_to_read;
} else {
bytes_to_read = f_frag_len;
}
if (bytes_to_read > 0) {
SGSBBC_DBG_MBOX("%s: reading the rest: sram_src = 0x%x, "
"bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
bytes_to_read);
}
done:
msg->msg_bytes += f_frag_len;
return (rc);
}
static void
mbox_skip_next_msg(struct sbbc_mbox_header *header)
{
struct sbbc_fragment frag;
uint32_t next_msg;
ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
if (mbox_read_frag(header, &frag)) {
cmn_err(CE_PANIC, "INBOX is Corrupt !\n");
}
next_msg = header->mailboxes[SBBC_INBOX].mbox_consumer;
next_msg += sizeof (struct sbbc_fragment);
next_msg += frag.f_frag_len;
if (next_msg >= header->mailboxes[SBBC_INBOX].mbox_len) {
next_msg = (next_msg +
header->mailboxes[SBBC_INBOX].mbox_len) %
header->mailboxes[SBBC_INBOX].mbox_len;
}
header->mailboxes[SBBC_INBOX].mbox_consumer =
next_msg;
mbox_update_header(SBBC_INBOX, header);
return;
}
static struct sbbc_msg_waiter *
mbox_find_waiter(uint16_t msg_type, uint32_t msg_id)
{
struct sbbc_msg_waiter *waiter, *prev;
prev = NULL;
for (waiter = master_mbox->mbox_wait_list[msg_type];
waiter != NULL; waiter = waiter->w_next) {
if (waiter->w_id == msg_id) {
if (prev != NULL) {
prev->w_next = waiter->w_next;
} else {
master_mbox->mbox_wait_list[msg_type] =
waiter->w_next;
}
break;
}
prev = waiter;
}
return (waiter);
}
static int
mbox_read_header(uint32_t mailbox, struct sbbc_mbox_header *header)
{
struct sbbc_mbox_header *hd;
uint32_t offset;
int rc;
hd = (struct sbbc_mbox_header *)0;
if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)header,
sizeof (struct sbbc_mbox_header)))
return (rc);
switch (mailbox) {
case SBBC_INBOX:
offset = (uint32_t)(uintptr_t)
(&hd->mailboxes[SBBC_INBOX].mbox_producer);
rc = iosram_read(SBBC_MAILBOX_KEY, offset,
(caddr_t)&header->mailboxes[SBBC_INBOX].mbox_producer,
sizeof (uint32_t));
break;
case SBBC_OUTBOX:
offset = (uint32_t)(uintptr_t)
(&hd->mailboxes[SBBC_OUTBOX].mbox_consumer);
rc = iosram_read(SBBC_MAILBOX_KEY, offset,
(caddr_t)&header->mailboxes[SBBC_OUTBOX].mbox_consumer,
sizeof (uint32_t));
break;
default:
cmn_err(CE_PANIC, "Invalid Mbox header type\n");
break;
}
return (rc);
}
static void
mbox_update_header(uint32_t mailbox, struct sbbc_mbox_header *header)
{
struct sbbc_mbox_header *hd;
uint32_t value, offset, mbox_len;
hd = (struct sbbc_mbox_header *)0;
switch (mailbox) {
case SBBC_INBOX:
value = header->mailboxes[SBBC_INBOX].mbox_consumer;
offset = (uint32_t)(uintptr_t)
(&hd->mailboxes[SBBC_INBOX].mbox_consumer);
mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
break;
case SBBC_OUTBOX:
value = header->mailboxes[SBBC_OUTBOX].mbox_producer;
offset = (uint32_t)(uintptr_t)
(&hd->mailboxes[SBBC_OUTBOX].mbox_producer);
mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
break;
default:
cmn_err(CE_PANIC, "Invalid Mbox header type\n");
break;
}
if (value % MBOX_ALIGN_BYTES) {
value += (MBOX_ALIGN_BYTES - (value % MBOX_ALIGN_BYTES));
value %= mbox_len;
}
if (iosram_write(SBBC_MAILBOX_KEY, offset, (caddr_t)&value,
sizeof (uint32_t))) {
cmn_err(CE_PANIC, "Mailbox Corrupt ! \n");
}
switch (mailbox) {
case SBBC_INBOX:
header->mailboxes[SBBC_INBOX].mbox_consumer = value;
break;
case SBBC_OUTBOX:
header->mailboxes[SBBC_OUTBOX].mbox_producer = value;
break;
}
}
static int
mbox_read_frag(struct sbbc_mbox_header *header,
struct sbbc_fragment *frag)
{
int rc = 0;
uint32_t sram_src, bytes;
caddr_t dst;
ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
sram_src = (uint32_t)(header->mailboxes[SBBC_INBOX].mbox_offset +
header->mailboxes[SBBC_INBOX].mbox_consumer);
if ((header->mailboxes[SBBC_INBOX].mbox_consumer +
sizeof (struct sbbc_fragment)) >=
header->mailboxes[SBBC_INBOX].mbox_len) {
dst = (caddr_t)frag;
bytes = header->mailboxes[SBBC_INBOX].mbox_len -
header->mailboxes[SBBC_INBOX].mbox_consumer;
if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, dst, bytes)) {
return (rc);
}
dst += bytes;
sram_src = header->mailboxes[SBBC_INBOX].mbox_offset;
bytes = (header->mailboxes[SBBC_INBOX].mbox_consumer +
sizeof (struct sbbc_fragment)) %
header->mailboxes[SBBC_INBOX].mbox_len;
if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src,
dst, bytes)) {
return (rc);
}
} else {
if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, (caddr_t)frag,
sizeof (struct sbbc_fragment))) {
return (rc);
}
}
return (0);
}
static uint_t
sbbc_do_fast_shutdown(char *arg)
{
(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
power_down("kadmin() failed, trying power_down()");
halt("power_down() failed, trying halt()");
return (DDI_INTR_UNCLAIMED);
}
static uint_t
sbbc_panic_shutdown_handler(char *arg)
{
static fn_t f = "sbbc_panic_shutdown_handler()";
sg_panic_shutdown_t *payload = NULL;
sbbc_msg_t *msg = NULL;
if (arg == NULL) {
SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
return (DDI_INTR_UNCLAIMED);
}
msg = (sbbc_msg_t *)arg;
if (msg->msg_buf == NULL) {
SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
return (DDI_INTR_UNCLAIMED);
}
payload = (sg_panic_shutdown_t *)msg->msg_buf;
switch (*payload) {
case SC_EVENT_PANIC_ENV:
cmn_err(CE_WARN, "%s", PANIC_ENV_EVENT_MSG);
ddi_trigger_softintr(panic_softintr_id);
break;
case SC_EVENT_PANIC_KEYSWITCH:
break;
default:
SGSBBC_DBG_EVENT(CE_NOTE, "%s: Unknown payload:%d", f,
*payload);
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_CLAIMED);
}
static int
dp_get_cores(uint16_t cpuid)
{
int bd, ii, impl, nc;
bd = cpuid / 4;
nc = SG_MAX_CPUS_PER_BD;
for (ii = 0; ii < nc; ii++)
if (cpu[MAKE_CPUID(bd, ii)]) {
impl = cpunodes[MAKE_CPUID(bd, ii)].implementation;
break;
}
if (IS_JAGUAR(impl) || IS_PANTHER(impl))
return (2);
else
return (1);
}
static int
dp_payload_add_cpus(plat_datapath_info_t *dpmsg, nvlist_t *erp)
{
int jj = 0, numcpus = 0;
int bd, procpos, ii, num, ncores, ret;
uint16_t *dparray, cpuid;
uint64_t *snarray;
ncores = dp_get_cores(dpmsg->cpuid);
switch (dpmsg->type) {
case DP_CDS_TYPE:
numcpus = ncores;
break;
case DP_DX_TYPE:
numcpus = 2 * ncores;
break;
case DP_RP_TYPE:
numcpus = SG_MAX_CPUS_PER_BD;
break;
default:
ASSERT(0);
return (-1);
}
num = numcpus;
dparray = kmem_zalloc(num * sizeof (uint16_t *), KM_SLEEP);
bd = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
procpos = SG_CPUID_TO_PORTID(dpmsg->cpuid) & 0x3;
mutex_enter(&cpu_lock);
switch (dpmsg->type) {
case DP_CDS_TYPE:
cpuid = dpmsg->cpuid & 0x1FF;
if (cpu[cpuid])
dparray[jj++] = cpuid;
cpuid = dpmsg->cpuid | SG_CORE_ID_MASK;
if (cpu[cpuid])
dparray[jj++] = cpuid;
break;
case DP_DX_TYPE:
cpuid = dpmsg->cpuid & 0x1FF;
if (cpu[cpuid])
dparray[jj++] = cpuid;
cpuid = dpmsg->cpuid | SG_CORE_ID_MASK;
if (cpu[cpuid])
dparray[jj++] = cpuid;
if (procpos == 0 || procpos == 2)
cpuid = dpmsg->cpuid + 1;
else
cpuid = dpmsg->cpuid - 1;
cpuid &= 0x1FF;
if (cpu[cpuid])
dparray[jj++] = cpuid;
cpuid |= SG_CORE_ID_MASK;
if (cpu[cpuid])
dparray[jj++] = cpuid;
break;
case DP_RP_TYPE:
for (ii = 0; ii < SG_MAX_CMPS_PER_BD; ii++) {
cpuid = MAKE_CPUID(bd, ii);
if (cpu[cpuid])
dparray[jj++] = cpuid;
cpuid |= SG_CORE_ID_MASK;
if (cpu[cpuid])
dparray[jj++] = cpuid;
}
break;
}
mutex_exit(&cpu_lock);
if (!jj) {
kmem_free(dparray, num * sizeof (uint16_t *));
ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
ASSERT(ret == 0);
return (-1);
}
snarray = kmem_zalloc(jj * sizeof (uint64_t), KM_SLEEP);
for (ii = 0; ii < jj; ii++)
snarray[ii] = cpunodes[dparray[ii]].device_id;
ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
ret |= nvlist_add_uint16_array(erp, DP_LIST, dparray, jj);
ret |= nvlist_add_uint64_array(erp, SN_LIST, snarray, jj);
ASSERT(ret == 0);
kmem_free(dparray, num * sizeof (uint16_t *));
kmem_free(snarray, jj * sizeof (uint64_t *));
return (0);
}
static uint_t
sbbc_dp_trans_event(char *arg)
{
const char *f = "sbbc_dp_trans_event()";
nvlist_t *erp, *detector, *hcelem;
char buf[FM_MAX_CLASS];
int board;
plat_datapath_info_t *dpmsg;
sbbc_msg_t *msg;
int msgtype;
msg = &dp_payload_msg;
dpmsg = &dp_payload;
msgtype = msg->msg_type.type;
cmn_err(CE_NOTE, "%s: msgtype=0x%x\n", f, msgtype);
cmn_err(CE_NOTE, "type=0x%x cpuid=0x%x t_value=0x%x\n", dpmsg->type,
dpmsg->cpuid, dpmsg->t_value);
if (dpmsg->type > DP_RP_TYPE) {
cmn_err(CE_WARN, "%s: dpmsg type 0x%x invalid\n",
f, dpmsg->type);
return (DDI_INTR_CLAIMED);
}
if (dpmsg->cpuid > 23) {
cmn_err(CE_NOTE, "%s: ignore I/O board msg\n", f);
return (DDI_INTR_CLAIMED);
}
erp = fm_nvlist_create(NULL);
detector = fm_nvlist_create(NULL);
board = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
switch (dpmsg->type) {
case DP_CDS_TYPE:
case DP_DX_TYPE:
(void) snprintf(buf, FM_MAX_CLASS, "SB%d", board);
break;
case DP_RP_TYPE:
(void) snprintf(buf, FM_MAX_CLASS, "RP");
break;
default:
(void) snprintf(buf, FM_MAX_CLASS, "UNKNOWN");
break;
}
hcelem = fm_nvlist_create(NULL);
(void) nvlist_add_string(hcelem, FM_FMRI_HC_NAME, FM_FMRI_LEGACY_HC);
(void) nvlist_add_string(hcelem, FM_FMRI_HC_ID, buf);
(void) nvlist_add_uint8(detector, FM_VERSION, FM_HC_SCHEME_VERSION);
(void) nvlist_add_string(detector, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
(void) nvlist_add_string(detector, FM_FMRI_HC_ROOT, "");
(void) nvlist_add_uint32(detector, FM_FMRI_HC_LIST_SZ, 1);
(void) nvlist_add_nvlist_array(detector, FM_FMRI_HC_LIST, &hcelem, 1);
(void) snprintf(buf, FM_MAX_CLASS, "asic.serengeti.%s.%s-%s",
dperrtype[dpmsg->type], dperrtype[dpmsg->type],
FM_ERROR_DATAPATH);
fm_ereport_set(erp, FM_EREPORT_VERSION, buf,
fm_ena_generate(0, FM_ENA_FMT1), detector, NULL);
if (msgtype == MBOX_EVENT_DP_ERROR)
fm_payload_set(erp,
DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_ERROR, NULL);
else
fm_payload_set(erp,
DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_FAULT, NULL);
fm_payload_set(erp, DP_TVALUE, DATA_TYPE_UINT32, dpmsg->t_value, NULL);
(void) dp_payload_add_cpus(dpmsg, erp);
fm_ereport_post(erp, EVCH_SLEEP);
fm_nvlist_destroy(erp, FM_NVA_FREE);
fm_nvlist_destroy(detector, FM_NVA_FREE);
return (DDI_INTR_CLAIMED);
}
static uint_t
sbbc_datapath_error_msg_handler(char *arg)
{
static fn_t f = "sbbc_datapath_error_msg_handler()";
sbbc_msg_t *msg = NULL;
if (arg == NULL) {
SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
return (DDI_INTR_UNCLAIMED);
}
msg = (sbbc_msg_t *)arg;
if (msg->msg_buf == NULL) {
SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
return (DDI_INTR_UNCLAIMED);
}
msg->msg_type.type = MBOX_EVENT_DP_ERROR;
ddi_trigger_softintr(dp_softintr_id);
return (DDI_INTR_CLAIMED);
}
static uint_t
sbbc_datapath_fault_msg_handler(char *arg)
{
static fn_t f = "sbbc_datapath_fault_msg_handler()";
sbbc_msg_t *msg = NULL;
if (arg == NULL) {
SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
return (DDI_INTR_UNCLAIMED);
}
msg = (sbbc_msg_t *)arg;
if (msg->msg_buf == NULL) {
SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
return (DDI_INTR_UNCLAIMED);
}
msg->msg_type.type = MBOX_EVENT_DP_FAULT;
ddi_trigger_softintr(dp_softintr_id);
return (DDI_INTR_CLAIMED);
}
int
sbbc_mbox_ecc_output(sbbc_ecc_mbox_t *msgp)
{
int rv;
plat_capability_data_t *cap;
plat_dimm_sid_board_data_t *ddata;
plat_ecc_msg_hdr_t *hdr;
rv = sbbc_mbox_request_response(&msgp->ecc_req, &msgp->ecc_resp,
sbbc_mbox_default_timeout);
if (rv != 0) {
if (msgp->ecc_log_error) {
if (sbbc_ecc_mbox_send_errs == 0) {
cmn_err(CE_NOTE, "!Solaris failed to send a "
"message (0x%x/0x%x) to the System "
"Controller. Error: %d, Message Status: %d",
msgp->ecc_resp.msg_type.type,
msgp->ecc_resp.msg_type.sub_type,
rv, msgp->ecc_resp.msg_status);
}
if (++sbbc_ecc_mbox_send_errs >=
sbbc_ecc_mbox_err_throttle) {
sbbc_ecc_mbox_send_errs = 0;
}
}
} else if (msgp->ecc_resp.msg_status != 0) {
if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
switch (msgp->ecc_resp.msg_type.sub_type) {
case INFO_MBOX_ECC:
hdr = (plat_ecc_msg_hdr_t *)
msgp->ecc_req.msg_buf;
if (hdr->emh_msg_type ==
PLAT_ECC_DIMM_SID_MESSAGE) {
rv = msgp->ecc_resp.msg_status;
break;
}
case INFO_MBOX_ECC_CAP:
plat_ecc_capability_sc_set
(PLAT_ECC_CAPABILITY_SC_DEFAULT);
break;
default:
break;
}
}
if (msgp->ecc_log_error) {
if (sbbc_ecc_mbox_inval_errs == 0) {
cmn_err(CE_NOTE, "!An internal error (%d) "
"occurred in the System Controller while "
"processing this message (0x%x/0x%x)",
msgp->ecc_resp.msg_status,
msgp->ecc_resp.msg_type.type,
msgp->ecc_resp.msg_type.sub_type);
}
if (msgp->ecc_resp.msg_status == EINVAL) {
if (++sbbc_ecc_mbox_inval_errs >=
sbbc_ecc_mbox_err_throttle) {
sbbc_ecc_mbox_inval_errs = 0;
}
rv = ENOMSG;
} else {
if (++sbbc_ecc_mbox_other_errs >=
sbbc_ecc_mbox_err_throttle) {
sbbc_ecc_mbox_other_errs = 0;
}
rv = msgp->ecc_resp.msg_status;
}
}
} else {
if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
switch (msgp->ecc_resp.msg_type.sub_type) {
case INFO_MBOX_ECC_CAP:
cap = (plat_capability_data_t *)
msgp->ecc_resp.msg_buf;
plat_ecc_capability_sc_set
(cap->capd_capability);
break;
case INFO_MBOX_ECC:
hdr = (plat_ecc_msg_hdr_t *)
msgp->ecc_resp.msg_buf;
if (hdr && (hdr->emh_msg_type ==
PLAT_ECC_DIMM_SID_MESSAGE)) {
ddata = (plat_dimm_sid_board_data_t *)
msgp->ecc_resp.msg_buf;
(void) plat_store_mem_sids(ddata);
}
break;
default:
break;
}
}
}
if (msgp->ecc_resp.msg_buf)
kmem_free((void *)msgp->ecc_resp.msg_buf,
(size_t)msgp->ecc_resp.msg_len);
kmem_free((void *)msgp->ecc_req.msg_buf, (size_t)msgp->ecc_req.msg_len);
kmem_free(msgp, sizeof (sbbc_ecc_mbox_t));
return (rv);
}
void
sbbc_mbox_queue_ecc_event(sbbc_ecc_mbox_t *sbbc_ecc_msgp)
{
if (sbbc_ecc_mbox_taskq == NULL) {
sbbc_ecc_mbox_taskq = taskq_create("ECC_event_mailbox", 1,
minclsyspri, ECC_MBOX_TASKQ_MIN, ECC_MBOX_TASKQ_MAX,
TASKQ_PREPOPULATE);
if (sbbc_ecc_mbox_taskq == NULL) {
if (sbbc_ecc_mbox_taskq_errs == 0) {
cmn_err(CE_NOTE, "Unable to create mailbox "
"task queue for ECC event logging to "
"System Controller");
}
if (++sbbc_ecc_mbox_taskq_errs >=
sbbc_ecc_mbox_err_throttle) {
sbbc_ecc_mbox_taskq_errs = 0;
}
kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
(size_t)sbbc_ecc_msgp->ecc_req.msg_len);
kmem_free((void *)sbbc_ecc_msgp,
sizeof (sbbc_ecc_mbox_t));
return;
}
sbbc_ecc_mbox_taskq_errs = 0;
}
if (taskq_dispatch(sbbc_ecc_mbox_taskq,
(task_func_t *)sbbc_mbox_ecc_output, sbbc_ecc_msgp,
TQ_NOSLEEP) == TASKQID_INVALID) {
if (sbbc_ecc_mbox_taskq_errs == 0) {
cmn_err(CE_NOTE, "Unable to send ECC event "
"message to System Controller");
}
if (++sbbc_ecc_mbox_taskq_errs >= sbbc_ecc_mbox_err_throttle) {
sbbc_ecc_mbox_taskq_errs = 0;
}
kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
(size_t)sbbc_ecc_msgp->ecc_req.msg_len);
kmem_free((void *)sbbc_ecc_msgp, sizeof (sbbc_ecc_mbox_t));
}
}
static uint_t
cap_ecc_msg_handler(char *addr)
{
sbbc_msg_t *msg = NULL;
plat_capability_data_t *cap = NULL;
static fn_t f = "cap_ecc_msg_handler";
msg = (sbbc_msg_t *)addr;
if (msg == NULL) {
SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
"null addr");
return (DDI_INTR_CLAIMED);
}
if (msg->msg_buf == NULL) {
SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
"null data buffer");
return (DDI_INTR_CLAIMED);
}
cap = (plat_capability_data_t *)msg->msg_buf;
switch (cap->capd_msg_type) {
case PLAT_ECC_CAPABILITY_MESSAGE:
SGSBBC_DBG_MBOX("%s: capability 0x%x\n", f,
cap->capd_capability);
plat_ecc_capability_sc_set(cap->capd_capability);
break;
default:
SGSBBC_DBG_MBOX("%s: Unknown message type = 0x%x\n", f,
cap->capd_msg_type);
break;
}
return (DDI_INTR_CLAIMED);
}