/*
* 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
* 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.
*/
/*
* Interface for Serengeti IOSRAM mailbox
* OS <-> SC communication protocol
*/
#include <sys/machsystm.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/plat_datapath.h>
/*
* Panic Shutdown event support
*/
/*
* The ID of the soft interrupt which triggers the bringing down of a Domain
* when a PANIC_SHUTDOWN event is received.
*/
/*
* A queue for making sure outgoing messages are in order as ScApp
* does not support interleaving messages.
*/
/*
* Handle unsolicited capability message.
*/
/*
* Datapath error and fault messages arrive unsolicited. The message data
* is contained in a plat_datapath_info_t structure.
*/
typedef struct {
/*
* Unsolicited datapath error messages are processed via a soft interrupt,
* triggered in unsolicited interrupt processing.
*/
static char *dperrtype[] = {
};
/*
* Variable indicating if we are already processing requests.
* Setting this value must be protected by outbox_queue_lock.
*/
static int outbox_busy = 0;
/*
* local stuff
*/
static int sbbc_mbox_recv_msg();
static int mbox_write(struct sbbc_mbox_header *,
struct sbbc_fragment *, sbbc_msg_t *);
sbbc_msg_t *);
static int mbox_has_free_space(struct sbbc_mbox_header *);
static void mbox_skip_next_msg(struct sbbc_mbox_header *);
static void wakeup_next(void);
static uint_t cap_ecc_msg_handler(char *);
/*
* Interrupt handlers
*/
static int sbbc_mbox_msgin(void);
static int sbbc_mbox_msgout(void);
static int sbbc_mbox_spacein(void);
static int sbbc_mbox_spaceout(void);
/*
* ECC event mailbox message taskq and parameters
*/
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;
/*
* Called when SBBC driver is loaded
* Initialise global mailbox stuff, etc
*/
void
{
int i;
if (master_mbox == NULL) {
}
/*
* mutex'es for the wait-lists
*/
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
}
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++)
/*
* Two mailbox channels SC -> OS , read-only
*/
"Can't allocate memory for inbound mailbox\n");
}
"Can't allocate memory for outbound mailbox\n");
}
/*
* Add PANIC_SHUTDOWN Event mutex
*/
/* Initialize datapath error message handler mutex */
/* Initialize capability message handler event mutex */
/*
* NOT USED YET
*/
}
/*
* called when the SBBC driver is unloaded
*/
void
{
int i;
int err;
/*
* destroy ECC event mailbox taskq
*/
if (sbbc_ecc_mbox_taskq != NULL) {
}
/*
* unregister interrupts
*/
(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);
/*
* Remove Panic Shutdown and Datapath Error event support.
*
* NOTE: If we have not added the soft interrupt handlers for these
* then we know that we have not registered the event handlers either.
*/
if (panic_softintr_id != 0) {
if (err != 0) {
"handler. Err=%d", err);
}
}
if (dp_softintr_id != 0) {
if (err != 0) {
"handler. Err=%d", err);
}
}
/*
* destroy all its mutex'es, lists etc
*/
/*
* mutex'es for the wait-lists
*/
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
}
if (err != 0) {
"handler. Err=%d", err);
}
}
/*
* Update iosram_sbbc to the new softstate after a tunnel switch.
* Move software interrupts from the old dip to the new dip.
*/
int
{
int msg_type;
int rc = 0;
int err;
if (master_mbox == NULL)
return (ENXIO);
if (intr->sbbc_intr_id) {
!= DDI_SUCCESS) {
"Can't add SBBC mailbox "
"softint for msg_type %x\n",
msg_type);
}
}
}
}
/*
* Add PANIC_SHUTDOWN Event handler
*/
if (panic_softintr_id) {
if (err != DDI_SUCCESS) {
"Shutdown handler. Err=%d", err);
}
}
/*
* Add Datapath Error Event handler
*/
if (dp_softintr_id) {
if (err != DDI_SUCCESS) {
"Error Event handler. Err=%d", err);
}
}
return (rc);
}
/*
* Called when the IOSRAM tunnel is created for the 'chosen' node.
*
* Read the mailbox header from the IOSRAM
* tunnel[SBBC_MAILBOX_KEY]
* Register the mailbox interrupt handlers
*/
int
{
int i;
int err;
int rc = 0;
/*
* This function should only be called once when
* the chosen node is initialized.
*/
if (master_mbox == NULL)
return (ENXIO);
/*
* read the header at offset 0
*/
sizeof (struct sbbc_mbox_header))) {
return (rc);
}
/*
* add the interrupt handlers for the mailbox
* interrupts
*/
for (i = 0; i < MBOX_INTRS; i++) {
switch (i) {
case MBOX_MSGIN_INTR:
break;
case MBOX_MSGOUT_INTR:
break;
case MBOX_SPACEIN_INTR:
break;
case MBOX_SPACEOUT_INTR:
break;
}
"Can't register Mailbox interrupts \n");
}
}
/*
* Add PANIC_SHUTDOWN Event handler
*/
if (err == DDI_SUCCESS) {
NULL, &panic_hdlr_lock);
if (err != 0) {
"Shutdown handler. Err=%d", err);
}
} else {
"softintr handler");
}
/*
* Add Unsolicited Datapath Error Events handler
*/
if (err == DDI_SUCCESS) {
NULL, &dp_hdlr_lock);
NULL, &dp_hdlr_lock);
if (err != 0) {
"error handler. Err=%d", err);
}
} else {
"softintr handler");
}
/*
* Register an interrupt handler with the sgbbc driver for the
* unsolicited INFO_MBOX response for the capability bitmap.
* This message is expected whenever the SC is (re)booted or
* failed over.
*/
if (err != 0) {
" handler with Err=%d", err);
}
/*
* Now is the opportunity to register
* the deferred mbox intrs.
*/
return (rc);
}
/*
* Called when chosen IOSRAM is initialized
* to register the deferred mbox intrs.
*/
static void
{
if (!intr->registered) {
"postreg for msgtype=%x\n", msg_type);
!= DDI_SUCCESS) {
"deferred mailbox softint \n");
} else
}
}
}
}
/*
* Register a handler for a message type
* NB NB NB
* arg must be either NULL or the address of a sbbc_fragment
* pointer
*/
int
{
int rc = 0;
/*
* Validate arguments
*/
if (msg_type >= SBBC_MBOX_MSG_TYPES)
return (EINVAL);
/*
* Verify that we have already set up the master sbbc
*/
return (ENXIO);
/* Find the end of the link list */
}
/* Return if the handler has been registered */
return (EBUSY);
}
/*
* The requested handler has not been installed.
* Allocate some memory.
*/
/* not registered yet */
intr->registered = 0;
else
/*
* register only if the chosen IOSRAM is
* initialized, otherwise defer the registration
* until IOSRAM initialization.
*/
if (master_iosram->iosram_sbbc) {
} else
} else {
"deferring msg=%x registration\n", msg_type);
}
return (rc);
}
/*
* Unregister a handler for a message type
*/
int
{
/*
* Verify that we have already set up the master sbbc
*/
return (ENXIO);
if (msg_type >= SBBC_MBOX_MSG_TYPES ||
return (EINVAL);
}
/*
* No handlers installed
*/
return (EINVAL);
}
/* Save the next pointer */
/* Found a match. Remove it from the link list */
if (intr->sbbc_intr_id)
else
break;
}
/* update pointers */
}
return (0);
}
/*
* Interrupt handlers - one for each mailbox
* interrupt type
*/
/*
* mailbox message received
*/
static int
{
/*
* We are only locking the InBox here, not the whole
* mailbox. This is based on the assumption of
* complete separation of mailboxes - outbox is
* We only ever update the producer for the
* outbox and the consumer for the inbox.
*/
for (;;) {
/*
* Get as many incoming messages as possible
*/
while (sbbc_mbox_recv_msg() == 0)
/* empty */;
/*
* send interrupt to SC to let it know that
* space is available over here
*/
(void) iosram_send_intr(SBBC_MAILBOX_SPACE_IN);
/*
* Read the inbox one more time to see if new messages
* has come in after we exit the loop.
*/
if (sbbc_mbox_recv_msg() == 0) {
} else {
break;
}
}
return (DDI_INTR_CLAIMED);
}
/*
* mailbox message sent
*/
static int
{
/*
* Should never get this
*/
return (DDI_INTR_CLAIMED);
}
/*
* space in the inbox
*/
static int
{
/*
* Should never get this
*/
return (DDI_INTR_CLAIMED);
}
/*
* space in the outbox
*/
static int
{
/*
* cv_broadcast() the threads waiting on the
* outbox's mb_full
*/
return (DDI_INTR_CLAIMED);
}
/*
* Client Interface
*
* The main interface will be
*
* sbbc_mbox_request_response(sbbc_msg_t *request,
* sbbc_msg_t *response, time_t wait_time)
*
* 1) the client calls request_response
* 2) a new unique msg ID is assigned for that msg
* 3) if there is space available in the outbox
* - the request msg is written to the mbox_out mailbox
* and the mailbox info updated.
* - allocate a sbbc_msg_waiter struct for this
* message, initialise the w_cv condvar.
* - get the mailbox mbox_wait_lock mutex for this
* message type
* - the response msg is put on the mbox_wait_list for
* that message type to await the SC's response
* - wait on the w_cv condvar protected by the
* mbox_wait_lock
* - SBBC_MAILBOX_OUT interrupt is sent to the SC
*
* 4) if no space in the outbox,
* - the request message blocks waiting
* for a SBBC_MAILBOX_SPACE_OUT interrupt
* It will block on the mailbox mb_full condvar.
* - go to (3) above
* 5) When we get a SBBC_MAILBOX_IN interrupt.
* - read the message ID of the next message (FIFO)
* - find that ID on the wait list
* - no wait list entry => unsolicited message. If theres
* a handler, trigger it
* - if someone is waiting, read the message in from
* SRAM, handling fragmentation, wraparound, etc
* - if the whole message has been read, signal
* the waiter
* - read next message until mailbox empty
* - send SBBC_MAILBOX_SPACE_IN interrupt to the SC
*
* 6) If a response is required and none is received, the client
* will timeout after <wait_time> seconds and the message
* status will be set to ETIMEDOUT.
*/
int
{
int rc = 0;
int flags;
static fn_t f = "sbbc_mbox_request_response";
return (EINVAL);
/*
* Verify that we have already set up the master sbbc
*/
if (master_mbox == NULL)
return (ENXIO);
/*
* We want to place a lower limit on the shortest amount of time we
* will wait before timing out while communicating with the SC via
* the mailbox.
*/
if (wait_time < sbbc_mbox_min_timeout)
/*
* If there is a message being processed, sleep until it is our turn.
*/
/*
* allocate an ID for this message, let it wrap
* around transparently.
* msg_id == 0 is unsolicited message
*/
if (msg_id == 0)
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, msg_len = 0x%x\n",
/*
* A new message can actually grab the lock before the thread
* that has just been signaled. Therefore, we need to double
* check to make sure that outbox_busy is not already set
* after we wake up.
*
* Potentially this could mean starvation for certain unfortunate
* threads that keep getting woken up and putting back to sleep.
* But the window of such contention is very small to begin with.
*/
while (outbox_busy) {
/*
* If we have timed out, set status to ETIMEOUT and return.
*/
if (clockleft < 0) {
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
f, msg_id);
"Timed out obtaining SBBC outbox lock");
return (ETIMEDOUT);
}
}
outbox_busy = 1;
/*
* We are only locking the OutBox from here, not the whole
* mailbox. This is based on the assumption of
* complete separation of mailboxes - outbox is
* We only ever update the producer for the
* outbox and the consumer for the inbox.
*/
/*
* No response expected ? Just send the message and return
*/
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
wakeup_next();
return (rc);
}
/*
* allocate/initialise a waiter
*/
wakeup_next();
return (ENOMEM);
}
wakeup_next();
if (rc != 0) {
/* Free the waiter */
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
return (rc);
}
/*
* Lock this waiter list and add the waiter
*/
} else {
}
/*
* wait here for a response to our message
* holding the mbox_wait_lock for the list ensures
* that the interrupt handler can't get in before
* we block.
* NOTE: We use the request msg_type for the
* the wait_list. This ensures that the
* msg_type won't change.
*/
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up for response\n",
f, msg_id);
/*
* If we have timed out, set msg_status to ETIMEDOUT,
* and remove the waiter from the waiter list.
*/
if (clockleft < 0) {
/*
* Remove the waiter from the waiter list.
* If we can't find the waiter in the list,
* 1. msg_status == EINPROGRESS
* It is being processed. We will give it
* a chance to finish.
* 2. msg_status != EINPROGRESS
* It is done processing. We can safely
* remove it.
* If we can find the waiter, it has timed out.
*/
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
f, msg_id);
SGSBBC_DBG_MBOX("%s: Waiting for msg_id = 0x%x "
"complete.\n", f, msg_id);
}
} else {
SGSBBC_DBG_MBOX("%s: setting msg_id = 0x%x "
"to ETIMEDOUT\n", f, msg_id);
}
}
/*
* lose the waiter
*/
return (rc);
}
static void
{
/*
* Done sending the current message or encounter an error.
* Wake up the one request in the outbox_queue.
*/
outbox_busy = 0;
}
/* ARGSUSED */
int
{
int rc = 0;
int bytes_written;
static fn_t f = "sbbc_mbox_send_msg";
/*
* First check that the SC has enabled its mailbox
*/
if (rc)
return (rc);
if (!(intr_enabled & SBBC_MAILBOX_OUT))
return (ENOTSUP);
/*
* read the mailbox header
*/
return (rc);
/*
* Allocate/initialise a fragment for this message
*/
frag.f_frag_offset = 0;
/*
* Throw in the message data
*/
/*
* If not enough space is available
* write what we can and wait for
* an interrupt to tell us that more
* space is available
*/
bytes_written = 0;
do {
return (rc);
}
if (rc == 0) {
/*
* Always tell the SC when there is a message.
* Ignore returned value as not being able to
* signal the SC about space available does
* not stop the SC from processing input.
*/
(void) iosram_send_intr(SBBC_MAILBOX_OUT);
}
if (mbox_has_free_space(&header) <=
sizeof (struct sbbc_fragment)) {
int tmprc;
/* Return ETIMEDOUT if we timed out */
if (clockleft < 0) {
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x "
"has timed out\n", f, msg_id);
"Timed out sending message "
"to SC");
return (ETIMEDOUT);
}
/* Read updated header from IOSRAM */
&header)) {
return (tmprc);
}
}
}
SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, bytes_written = 0x%x, "
"msg_len = 0x%x\n", f,
/*
* this could be a spurious interrupt
* as the SC may be merrily readings its
* mail even as send, but what can you do ? No
* synchronization method between SC <-> OS
* SRAM data eaters means that this is inevitable.
* It would take a bigger brain to fix this.
*
*/
(void) iosram_send_intr(SBBC_MAILBOX_OUT);
return (rc);
}
/*
* get next message
* Read the next message from SRAM
* Check if theres an entry on the wait queue
* for this message
* If yes, read the message in and signal
* the waiter (if all the message has been received)
* No, its unsolicited, if theres a handler installed for
* this message type trigger it, otherwise toss
* the message
*/
int
{
static fn_t f = "sbbc_mbox_recv_msg";
/*
* First check that the OS has enabled its mailbox
*/
if (rc) {
return (rc);
}
if (!(intr_enabled & SBBC_MAILBOX_IN))
return (ENOTSUP);
/*
* read the mailbox header
*/
return (rc);
/*
* check if any messages available. If
* consumer == producer then no more
* messages
*/
return (-1);
}
/*
* read the fragment header for this message
*/
return (rc);
}
/* Save to local variable for easy reading */
/*
* check the message type. If its invalid, we will
* just toss the message
*/
if (type >= SBBC_MBOX_MSG_TYPES) {
goto done;
}
/*
* if theres no waiters for this message type, and theres
* no message handler installed, toss it.
*
* Unsolicited messages (f_id == 0) are tricky because we won't know
* when the handler has finished so that we can
* remove the message, so, given the small brains in operation
* here, what we do is restrict junk mail to zero-length
* messages, then we allocate a fragment using kmem,
* make a copy of the fragment in this memory,
* pass this pointer to the fragment, then skip the message.
* So even if there is data associated with the junkmail,
* the message handler doesn't get to see it
* We expect the mesaage handler to free the memory.
*/
if (type == SBBC_BROADCAST_MSG) {
/*
* Broadcast message, trigger all handlers
*/
first_hdlr = 0;
/*
* Theres no waiters, or its unsolicited anyway
*/
} else {
/*
* check the fragment message type, look at the wait list for
* that type to find its associated message
*
* First find the message. If we get it, take it off
* the waiter list and read the data. We will
* put it back on the list if necessary.
* This avoids the problem of a second message-in
* interrupt playing with this waiter.
* This will cut down on mutex spinning on the wait
* list locks, also, expect the next fragment to be
* for this messageso we might as well have it at the
* start of the list.
*
* its possible that a return message has a different type,
* (possible but not recommended!). So, if we don't find
* it on the list pointed to by the request type,
* go look at all the other lists
*/
for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
if (i == type)
continue;
!= NULL)
break;
}
}
rc = -1;
/*
* there's no waiter for this message, but that
* could mean that this message is the start of
* must by definition be unsolicited,
* so trigger the handler
*/
} else {
SGSBBC_DBG_MBOX("%s: f_id = 0x%x, msg_id = 0x%x, "
"msg_len = 0x%x\n",
SGSBBC_DBG_MBOX("%s: f_id = 0x%x, offset = 0x%x, "
"len = 0x%x, total_len = 0x%x\n",
frag.f_total_len)) {
/*
* failed or all the message has been read in
*/
SGSBBC_DBG_MBOX("%s: msg_status = %d\n",
} else {
/*
* back on the wait list
*/
goto done;
}
} else {
}
}
goto done;
}
}
/*
* Set msg_len to f_frag_len so msg_buf will be large enough
* to contain what is in the fragment.
*/
/*
* Save the f_frag_offset for copying into client's space.
* Set frag.f_frag_offset to 0 so we don't have to allocate
* too much space for reading in the message.
*/
frag.f_frag_offset = 0;
/* Allocate space for msg_buf */
" for unsolicited messages\n");
} else {
/* Save the incoming message in tmpmsg */
continue;
}
/*
* If the client has allocated enough space
* for incoming message, copy into the
* client buffer.
*/
if (f_frag_len > 0)
} else {
}
}
/*
* Only trigger the interrupt when we
* have received the whole message.
*/
if (f_frag_offset + f_frag_len ==
frag.f_total_len) {
intr->sbbc_intr_id);
}
}
}
if (f_frag_len != 0) {
/* Don't forget to free the buffer */
}
}
done:
return (rc);
}
/*
* available free space in the outbox
*/
static int
{
/*
* mailbox is empty
*/
space +=
} else {
/*
* mailbox wrapped around
*/
}
/*
* Need to make sure that the mailbox never
* gets completely full, as consumer == producer is
* our test for empty, so we drop MBOX_ALIGN_BYTES.
*/
if (space >= MBOX_ALIGN_BYTES)
else
space = 0;
return (space);
}
/*
* Write the data to IOSRAM
* Update the SRAM mailbox header
* Update the local mailbox pointers
* Only write a single fragment. If possible,
* put the whole message into a fragment.
*
* Note: We assume that there is no 'max' message
* size. We will just keep fragmenting.
* Note: We always write to SBBC_OUTBOX and
* read from SBBC_INBOX
*
* If we get an error at any time, return immediately
* without updating the mailbox header in SRAM
*/
static int
{
int rc = 0;
static fn_t f = "mbox_write";
/*
* Save to local variables to make code more readable
*/
frag_header_size = sizeof (struct sbbc_fragment);
SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, "
/*
* Write pointer in SRAM
*/
/*
* NB We assume that the consumer stays constant
* during the write. It may not necessarily
* be the case but it won't cause us any problems, just means
* we fragment more than is absolutely necessary
*
* possible cases
* 1) consumer == producer, mailbox empty
* space_at_end == mailbox end - producer
* space_at_start == producer - MBOX_ALIGN_BYTES
* 2) producer < consumer
* space_at_end = (consumer - producer - MBOX_ALIGN_BYTES)
* space_at_start == 0
* 3) producer > consumer
* space_at_end = mailbox end - producer
* space_at_start = consumer - MBOX_ALIGN_BYTES
*
* (space - MBOX_ALIGN_BYTES) because we need to avoid the
* scenario where the producer wraps around completely and
* producer == consumer, as this is our test for 'empty'.
* Also we want it to be 8-byte aligned.
* Note: start is assumed = 0
*/
if (mbox_producer < mbox_consumer) {
if (space_at_end < 0)
space_at_end = 0;
space_at_start = 0;
} else {
if (mbox_consumer == 0)
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);
if (free_space < frag_header_size) {
/*
* can't even write a fragment header, so just return
* the caller will block waiting for space
*/
frag->f_frag_len = 0;
return (ENOSPC);
}
/*
* How many bytes will be in the fragment ?
*/
SGSBBC_DBG_MBOX("%s: writing header:sram_dst = 0x%x\n",
f, sram_dst);
/*
* we can write the fragment header and some data
* First, the fragment header
*/
if (space_at_end >= frag_header_size) {
if (rc)
return (rc);
/*
* Wrap around if we reach the end
*/
}
} else {
/* wraparound */
if (space_at_end) {
if (rc)
return (rc);
}
(frag_header_size - space_at_end));
if (rc)
return (rc);
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);
/*
* Now the fragment data
*/
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)
else
if (rc)
return (rc);
/*
* Wrap around if we reach the end
*/
}
}
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) {
} else {
}
if (rc)
return (rc);
}
/*
* update header->mbox_producer (bytes_written + frag_size)
*/
}
SGSBBC_DBG_MBOX("%s: after writing data:sram_dst = 0x%x, "
return (rc);
}
/*
* Get the next frag from IOSRAM.
* Write it to the corresponding msg buf.
* The caller must update the SRAM pointers etc.
*/
static int
{
int rc = 0;
int bytes_to_read;
static fn_t f = "mbox_read";
/*
* Save to local variables to make code more readable
*/
frag_header_size = sizeof (struct sbbc_fragment);
/*
* If the message buffer size is smaller than the fragment
* size, return an error.
*/
goto done;
}
/*
* Throw in the message data
*/
/*
* We have it all, waiter, message, so lets
* go get that puppy!
* Message could be in one or two chunks -
* consumer < producer: 1 chunk, (producer - consumer)
* consumer > producer: 2 chunks, (end - consumer)
* (producer - start)
*/
/*
* wraparound
*/
/*
* find where the data is
* possible cases
* 1) consumer == producer, mailbox empty
* error
* 2) producer < consumer
* bytes_at_end = mailbox end - consumer
* bytes_at_start = producer
* 3) producer > consumer
* bytes_at_end = producer - consumer
* bytes_at_start = 0
*/
SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, mbox_producer = 0x%x, "
"frag_len = 0x%x\n",
if (mbox_producer == mbox_consumer) {
bytes_at_end = bytes_at_start = 0;
} else if (mbox_producer < mbox_consumer) {
} else {
bytes_at_start = 0;
}
SGSBBC_DBG_MBOX("%s: bytes_at_end = 0x%x, "
/*
* mailbox is corrupt
* but what to do ?
*/
"producer = %x, consumer = %x, bytes_at_start = %x, "
}
/*
* If bytes_at_end is greater than header size, read the
* part at the end of the mailbox, and then update the
* pointers and bytes_to_read.
*/
if (bytes_at_end > frag_header_size) {
/*
* We are only interested in the data segment.
*/
SGSBBC_DBG_MBOX("%s: reading data: sram_src = 0x%x, "
if (rc) {
goto done;
}
/*
* Update pointers in SRAM and message buffer.
*/
} else {
}
/*
* wraparound to start of mailbox
*/
if (bytes_to_read > 0) {
SGSBBC_DBG_MBOX("%s: reading the rest: sram_src = 0x%x, "
}
done:
return (rc);
}
/*
* move past the next message in the inbox
*/
static void
{
}
/*
* Move on to the next message
*/
next_msg += sizeof (struct sbbc_fragment);
}
return;
}
static struct sbbc_msg_waiter *
{
} else {
}
break;
}
}
return (waiter);
}
static int
{
int rc;
/*
* Initialize a sbbc_mbox_header pointer to 0 so that we
* can use it to calculate the offsets of fields inside
* the structure.
*/
hd = (struct sbbc_mbox_header *)0;
sizeof (struct sbbc_mbox_header)))
return (rc);
/*
* Since the header is read in a byte-by-byte fashion
* using ddi_rep_get8, we need to re-read the producer
* or consumer pointer as integer in case it has changed
* after part of the previous value has been read.
*/
switch (mailbox) {
case SBBC_INBOX:
sizeof (uint32_t));
break;
case SBBC_OUTBOX:
sizeof (uint32_t));
break;
default:
break;
}
return (rc);
}
/*
* There are only two fields updated by the domain,
* the inbox consumer field and the outbox producer
* field. These fields are protected by the respective
* mbox_{in|out}->mb_lock so that accesses will
* be serialised. The only coherency issue is writing
* back the header, so we do it here after grabbing
* the global mailbox lock.
*/
static void
{
/*
* Initialize a sbbc_mbox_header pointer to 0 so that we
* can use it to calculate the offsets of fields inside
* the structure.
*/
hd = (struct sbbc_mbox_header *)0;
switch (mailbox) {
case SBBC_INBOX:
break;
case SBBC_OUTBOX:
break;
default:
break;
}
/*
* to be unaligned, we skip on modulo MBOX_ALIGN_BYTES.
* This is OK because all the mailbox handlers will
* conform to this.
*/
if (value % MBOX_ALIGN_BYTES) {
}
sizeof (uint32_t))) {
}
/*
* Update internal pointers so they won't be out of sync with
* the values in IOSRAM.
*/
switch (mailbox) {
case SBBC_INBOX:
break;
case SBBC_OUTBOX:
break;
}
}
static int
struct sbbc_fragment *frag)
{
int rc = 0;
/*
* read the fragment header for this message
*/
/*
* wraparound ?
*/
sizeof (struct sbbc_fragment)) >=
return (rc);
}
sizeof (struct sbbc_fragment)) %
return (rc);
}
} else {
sizeof (struct sbbc_fragment))) {
return (rc);
}
}
return (0);
}
/*
* This function is triggered by a soft interrupt and it's purpose is to call
* to kadmin() to shutdown the Domain.
*/
/*ARGSUSED0*/
static uint_t
{
/*
* If kadmin fails for some reason then we bring the system down
* via power_down(), or failing that using halt().
*/
power_down("kadmin() failed, trying power_down()");
halt("power_down() failed, trying halt()");
/*
* We should never make it this far, so something must have gone
* horribly, horribly wrong.
*/
/*NOTREACHED*/
return (DDI_INTR_UNCLAIMED);
}
/*
* This function handles unsolicited PANIC_SHUTDOWN events
*/
static uint_t
{
static fn_t f = "sbbc_panic_shutdown_handler()";
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
switch (*payload) {
case SC_EVENT_PANIC_ENV:
/*
* Let the user know why the domain is going down.
*/
/*
* trigger sbbc_do_fast_shutdown().
*/
/*NOTREACHED*/
break;
case SC_EVENT_PANIC_KEYSWITCH:
/*
* The SC warns a user if they try a destructive keyswitch
* command on a Domain which is currently running Solaris.
* If the user chooses to continue despite our best advise
* then we bring down the Domain immediately without trying
* to shut the system down gracefully.
*/
break;
default:
*payload);
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_CLAIMED);
}
/*
* dp_get_cores()
*
* Checks cpu implementation for the input cpuid and returns
* the number of cores.
* If implementation cannot be determined, returns 1
*/
static int
{
/* find first with valid implementation */
break;
}
return (2);
else
return (1);
}
/*
* dp_payload_add_cpus()
*
* From datapath mailbox message, determines the number of and safari IDs
* for affected cpus, then adds this info to the datapath ereport.
*
*/
static int
{
/* check for multiple core architectures */
case DP_CDS_TYPE:
break;
case DP_DX_TYPE:
break;
case DP_RP_TYPE:
break;
default:
ASSERT(0);
return (-1);
}
/*
* populate dparray with impacted cores (only those present)
*/
case DP_CDS_TYPE:
/*
* For a CDS error, it's the reporting cpuid
* and it's other core (if present)
*/
break;
case DP_DX_TYPE:
/*
* For a DX error, it's the reporting cpuid (all
* cores) and the other CPU sharing the same
* DX<-->DCDS interface (all cores)
*/
/* reporting cpuid */
/* find partner cpuid */
else
/* add partner cpuid */
break;
case DP_RP_TYPE:
/*
* For a RP error, it's all cpuids (all cores) on
* the reporting board
*/
cpuid |= SG_CORE_ID_MASK;
}
break;
}
/*
* The datapath message could not be associated with any
* configured CPU.
*/
if (!jj) {
return (-1);
}
return (0);
}
/*
* sbbc_dp_trans_event() - datapath message handler.
*
* Process datapath error and fault messages received from the SC. Checks
* for, and disregards, messages associated with I/O boards. Otherwise,
* extracts message info to produce a datapath ereport.
*/
/*ARGSUSED*/
static uint_t
{
const char *f = "sbbc_dp_trans_event()";
int board;
int msgtype;
/* set i/f message and payload pointers */
msg = &dp_payload_msg;
dpmsg = &dp_payload;
/* check for valid type */
return (DDI_INTR_CLAIMED);
}
/* check for I/O board message - Schizo AIDs are 25 - 30 */
return (DDI_INTR_CLAIMED);
}
/* allocate space for ereport */
/*
* Member Name Data Type Comments
* ----------- --------- -----------
* version uint8 0
* class string "asic"
* ENA uint64 ENA Format 1
* detector fmri aggregated ID data for SC-DE
*
* Datapath ereport subclasses and data payloads:
* There will be two types of ereports (error and fault) which will be
* identified by the "type" member.
*
* ereport.asic.serengeti.cds.cds-dp
* ereport.asic.serengeti.dx.dx-dp (board)
* ereport.asic.serengeti.rp.rp-dp (centerplane)
*
* Member Name Data Type Comments
* ----------- --------- -----------
* erptype uint16 derived from message type: error or
* fault
* t-value uint32 SC's datapath SERD timeout threshold
* dp-list-sz uint8 number of dp-list array elements
* dp-list array of uint16 Safari IDs of affected cpus
* sn-list array of uint64 Serial numbers of affected cpus
*/
/* compose common ereport elements */
/*
* Create legacy FMRI for the detector
*/
case DP_CDS_TYPE:
case DP_DX_TYPE:
break;
case DP_RP_TYPE:
break;
default:
break;
}
/* build ereport class name */
/* add payload elements */
if (msgtype == MBOX_EVENT_DP_ERROR)
else
/* post ereport */
/* free ereport memory */
return (DDI_INTR_CLAIMED);
}
static uint_t
{
static fn_t f = "sbbc_datapath_error_msg_handler()";
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
/* trigger sbbc_dp_trans_event() */
return (DDI_INTR_CLAIMED);
}
static uint_t
{
static fn_t f = "sbbc_datapath_fault_msg_handler()";
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
/* trigger sbbc_dp_trans_event() */
return (DDI_INTR_CLAIMED);
}
/*
* Log an ECC event message to the SC. This is called from the
* sbbc_ecc_mbox_taskq or directly from plat_send_ecc_mailbox_msg
* for indictment messages.
*/
int
{
int rv;
if (rv != 0) {
/*
* Indictment messages use the return value to indicate a
* problem in the mailbox. For Error mailbox messages, we'll
* have to use a syslog message.
*/
if (msgp->ecc_log_error) {
if (sbbc_ecc_mbox_send_errs == 0) {
"message (0x%x/0x%x) to the System "
"Controller. Error: %d, Message Status: %d",
}
if (++sbbc_ecc_mbox_send_errs >=
}
}
case INFO_MBOX_ECC:
hdr = (plat_ecc_msg_hdr_t *)
if (hdr->emh_msg_type ==
break;
}
/*FALLTHROUGH*/
case INFO_MBOX_ECC_CAP:
/*
* The positive response comes only
* from the AVL FS1 updated SC.
* If the firmware is either downgraded
* or failover to an older version, then
* lets reset the SC capability to
* default.
*/
break;
default:
break;
}
}
if (msgp->ecc_log_error) {
if (sbbc_ecc_mbox_inval_errs == 0) {
"occurred in the System Controller while "
"processing this message (0x%x/0x%x)",
}
if (++sbbc_ecc_mbox_inval_errs >=
}
} else {
if (++sbbc_ecc_mbox_other_errs >=
}
}
}
} else {
case INFO_MBOX_ECC_CAP:
/*
* Successfully received the response
* for the capability message, so updating
* the SC ECC messaging capability.
*/
cap = (plat_capability_data_t *)
(cap->capd_capability);
break;
case INFO_MBOX_ECC:
hdr = (plat_ecc_msg_hdr_t *)
/*
* Successfully received a response
* to a request for DIMM serial ids.
*/
(void) plat_store_mem_sids(ddata);
}
break;
default:
break;
}
}
}
return (rv);
}
/*
* Enqueue ECC event message on taskq to SC. This is invoked from
* plat_send_ecc_mailbox_msg() for each ECC event generating a message.
*/
void
{
/*
* Create the ECC event mailbox taskq, if it does not yet exist.
* This must be done here rather than in sbbc_mbox_init(). The
* sgsbbc driver is loaded very early in the boot flow. Calling
* taskq_create() from sbbc_mbox_init could lead to a boot deadlock.
*
* There might be a tiny probability that two ECC handlers on
* different processors could arrive here simultaneously. If
* the taskq has not been created previously, then these two
* simultaneous events could cause the creation of an extra taskq.
* Given the extremely small likelihood (if not outright impossibility)
* of this occurrence, sbbc_ecc_mbox_taskq is not protected by a lock.
*/
if (sbbc_ecc_mbox_taskq == NULL) {
if (sbbc_ecc_mbox_taskq == NULL) {
if (sbbc_ecc_mbox_taskq_errs == 0) {
"task queue for ECC event logging to "
"System Controller");
}
if (++sbbc_ecc_mbox_taskq_errs >=
}
kmem_free((void *)sbbc_ecc_msgp,
sizeof (sbbc_ecc_mbox_t));
return;
}
/*
* Reset error counter so that first taskq_dispatch
* error will be output
*/
}
/*
* Enqueue the message
*/
TQ_NOSLEEP) == NULL) {
if (sbbc_ecc_mbox_taskq_errs == 0) {
"message to System Controller");
}
if (++sbbc_ecc_mbox_taskq_errs >= sbbc_ecc_mbox_err_throttle) {
}
}
}
static uint_t
{
static fn_t f = "cap_ecc_msg_handler";
"null addr");
return (DDI_INTR_CLAIMED);
}
"null data buffer");
return (DDI_INTR_CLAIMED);
}
switch (cap->capd_msg_type) {
SGSBBC_DBG_MBOX("%s: capability 0x%x\n", f,
break;
default:
SGSBBC_DBG_MBOX("%s: Unknown message type = 0x%x\n", f,
cap->capd_msg_type);
break;
}
return (DDI_INTR_CLAIMED);
}