/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
*/
#include <stddef.h>
#include <strings.h>
#include <scsi/libses.h>
#include <scsi/libses_plugin.h>
#include <scsi/plugins/ses/framework/ses2.h>
#include "ses2_impl.h"
static int
ses2_ctl_common_setdef(ses_node_t *np, ses2_diag_page_t page, void *data)
{
ses2_cmn_elem_ctl_impl_t *eip = data;
nvlist_t *props = ses_node_props(np);
if (page != SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS)
return (0);
SES_NV_CTLBOOL_INVERT(props, SES_PROP_SWAP, eip->seci_rst_swap);
SES_NV_CTLBOOL(props, SES_PROP_DISABLED, eip->seci_disable);
SES_NV_CTLBOOL(props, SES_PROP_PRDFAIL, eip->seci_prdfail);
eip->seci_select = 1;
return (0);
}
/*ARGSUSED*/
static void *
ses2_aes_index(ses_plugin_t *sp, ses_node_t *np, void *data, size_t pagelen,
size_t *len)
{
ses2_aes_page_impl_t *apip = data;
uint64_t cl_index, eo_index, type, unctype;
nvlist_t *props = ses_node_props(np), *uncprops;
ses2_aes_descr_eip_impl_t *dep;
size_t desclen;
int i, pos, c, clcount = 0;
ses_node_t *uncle;
boolean_t found_class_index = B_FALSE, zero_index_detected = B_FALSE;
VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX,
&eo_index) == 0);
VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
&cl_index) == 0);
VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
&type) == 0);
if (pagelen < offsetof(ses2_aes_page_impl_t, sapi_data))
return (0);
/*
* Checking if AES page contains the class index or not.
* The AES page should contain the element only index
* which spans different element types.
* J4410 enclosure AES page contains the class element index
* and we work around it by checking the parent node and its
* sibling and count the number of elements that should
* have the AES data.
*/
for (dep = (ses2_aes_descr_eip_impl_t *)apip->sapi_data, pos = 0, i = 0;
pos < SCSI_READ16(&apip->sapi_page_length);
dep = (ses2_aes_descr_eip_impl_t *)(apip->sapi_data + pos), i++) {
if (!SES_WITHIN_PAGE_STRUCT(dep, data, pagelen))
break;
desclen = dep->sadei_length +
offsetof(ses2_aes_descr_eip_impl_t, sadei_length) +
sizeof (dep->sadei_length);
if (!SES_WITHIN_PAGE(dep, desclen, data, pagelen))
break;
pos += desclen;
if (dep->sadei_element_index == 0) {
if (!zero_index_detected) {
zero_index_detected = B_TRUE;
} else {
found_class_index = B_TRUE;
break;
}
}
}
/*
* If the specified element index is the class index
* check if the parent node and its siblings are one of types,
* Array Device, Device, ESC electronincs, initiator port, target
* port or SAS expander. The count indicates the type of the element
* that is associated with the AES data that is being probed.
*/
if (found_class_index) {
for (uncle = ses_node_parent(np); uncle != NULL;
uncle = ses_node_prev_sibling(uncle)) {
uncprops = ses_node_props(uncle);
VERIFY(nvlist_lookup_uint64(uncprops,
SES_PROP_ELEMENT_TYPE, &unctype) == 0);
if ((unctype == SES_ET_DEVICE) ||
(unctype == SES_ET_ESC_ELECTRONICS) ||
(unctype == SES_ET_SCSI_TARGET_PORT) ||
(unctype == SES_ET_SCSI_INITIATOR_PORT) ||
(unctype == SES_ET_ARRAY_DEVICE) ||
(unctype == SES_ET_SAS_EXPANDER))
clcount++;
}
for (dep = (ses2_aes_descr_eip_impl_t *)apip->sapi_data,
pos = 0, i = 0, c = 0;
pos < SCSI_READ16(&apip->sapi_page_length);
dep = (ses2_aes_descr_eip_impl_t *)(apip->sapi_data + pos),
i++) {
if (!SES_WITHIN_PAGE_STRUCT(dep, data, pagelen))
break;
desclen = dep->sadei_length +
offsetof(ses2_aes_descr_eip_impl_t, sadei_length) +
sizeof (dep->sadei_length);
if (!SES_WITHIN_PAGE(dep, desclen, data, pagelen))
break;
pos += desclen;
if (!dep->sadei_eip &&
type != SES_ET_DEVICE &&
type != SES_ET_ARRAY_DEVICE) {
/*
* We can't really do anything with this,
* because while the standard requires that
* these descriptors be in the same order as
* those in the status page, some element types
* may optionally include AES data. This means
* we cannot know which element this descriptor
* refers to unless EIP is 1. Sadly, the
* standard only says that this "should" be
* true. It's impossible to guess what use
* this is supposed to have otherwise.
* See 6.1.13.1.
*/
continue;
} else if (dep->sadei_eip) {
if (dep->sadei_element_index == 0) {
c++; /* increment class counter. */
i = 0; /* reset index counter. */
}
if ((clcount == c) &&
((dep->sadei_element_index == cl_index) ||
(i == cl_index))) {
*len = desclen;
return (dep);
} else {
continue;
}
} else {
continue;
}
}
} else {
for (dep = (ses2_aes_descr_eip_impl_t *)apip->sapi_data,
pos = 0, i = 0;
pos < SCSI_READ16(&apip->sapi_page_length);
dep = (ses2_aes_descr_eip_impl_t *)(apip->sapi_data + pos),
i++) {
if (!SES_WITHIN_PAGE_STRUCT(dep, data, pagelen))
break;
desclen = dep->sadei_length +
offsetof(ses2_aes_descr_eip_impl_t, sadei_length) +
sizeof (dep->sadei_length);
if (!SES_WITHIN_PAGE(dep, desclen, data, pagelen))
break;
pos += desclen;
if (!dep->sadei_eip &&
type != SES_ET_DEVICE &&
type != SES_ET_ARRAY_DEVICE) {
continue;
} else if (dep->sadei_eip &&
dep->sadei_element_index != eo_index) {
/*
* The element index field from AES descriptor
* is element only index which doesn't include
* the OVERALL STATUS fields so we should
* compare with SES_PROP_ELEMENT_ONLY_INDEX
* not SES_PROP_ELEMENT_INDEX.
*/
continue;
} else if (dep->sadei_eip || i == eo_index) {
*len = desclen;
return (dep);
}
}
}
return (NULL);
}
/*ARGSUSED*/
static void *
ses2_threshold_index(ses_plugin_t *sp, ses_node_t *np, void *data,
size_t pagelen, size_t *len)
{
uint64_t index;
nvlist_t *props = ses_node_props(np);
ses2_threshold_in_page_impl_t *tpip = data;
ses2_threshold_impl_t *tp;
VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
&index) == 0);
*len = sizeof (ses2_threshold_impl_t);
tp = &tpip->stipi_thresholds[index];
if (!SES_WITHIN_PAGE_STRUCT(tp, data, pagelen))
return (NULL);
return (&tpip->stipi_thresholds[index]);
}
/*ARGSUSED*/
static void *
ses2_element_index(ses_plugin_t *sp, ses_node_t *np, void *data,
size_t pagelen, size_t *len)
{
uint64_t index;
nvlist_t *props = ses_node_props(np);
ses2_elem_desc_page_impl_t *edip = data;
ses2_elem_descriptor_impl_t *dp;
int i;
uint16_t dlen;
if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX, &index) != 0)
return (NULL);
if (!SES_WITHIN_PAGE(data, sizeof (*dp), data, pagelen))
return (NULL);
/*
* This variable-length list of variable-length strings format sucks
* for performance; we ALWAYS have to walk the whole bloody thing to
* find a particular node's entry.
*/
for (i = 0, dp = (ses2_elem_descriptor_impl_t *)edip->sedpi_data;
i < index; i++) {
if (!SES_WITHIN_PAGE_STRUCT(dp, data, pagelen))
return (NULL);
dlen = SCSI_READ16(&dp->sedi_descriptor_length);
dp = (ses2_elem_descriptor_impl_t *)
((uint8_t *)dp->sedi_descriptor + dlen);
}
if (!SES_WITHIN_PAGE_STRUCT(dp, data, pagelen))
return (NULL);
*len = SCSI_READ16(&dp->sedi_descriptor_length);
if (!SES_WITHIN_PAGE(dp,
*len + offsetof(ses2_elem_descriptor_impl_t, sedi_descriptor),
data, pagelen))
return (NULL);
return (dp->sedi_descriptor);
}
/*ARGSUSED*/
static void *
ses2_status_index(ses_plugin_t *sp, ses_node_t *np, void *data,
size_t pagelen, size_t *len)
{
uint64_t index;
nvlist_t *props = ses_node_props(np);
ses2_status_page_impl_t *spip = data;
if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
&index) != 0)
return (NULL);
if ((index + 1) * sizeof (ses2_elem_status_impl_t) +
offsetof(ses2_status_page_impl_t, sspi_data) > pagelen)
return (NULL);
*len = sizeof (ses2_elem_status_impl_t);
return ((ses2_elem_status_impl_t *)spip->sspi_data + index);
}
/*ARGSUSED*/
static size_t
ses2_ctl_len(uint_t nelem, int page, size_t datalen)
{
ASSERT(page == SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS);
return (nelem * sizeof (ses2_elem_ctl_impl_t) +
offsetof(ses2_control_page_impl_t, scpi_data[0]));
}
/*ARGSUSED*/
static void *
ses2_ctl_fill(ses_plugin_t *sp, void *pagedata, size_t pagelen,
ses_node_t *np)
{
uint64_t index;
nvlist_t *props = ses_node_props(np);
ses2_control_page_impl_t *pip = pagedata;
void *data;
ses2_diag_page_t page = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS;
if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
&index) != 0) {
(void) ses_error(ESES_BAD_RESPONSE, "missing element index "
"for enclosure node");
return (NULL);
}
data = &pip->scpi_data[index];
if (ses2_ctl_common_setdef(np, page, data) != 0 ||
ses2_element_setdef(np, page, data) != 0 ||
ses2_enclosure_setdef(np, page, data) != 0)
return (NULL);
return (data);
}
/*ARGSUSED*/
static size_t
ses2_stringout_len(uint_t nelem, int page, size_t datalen)
{
ASSERT(page == SES2_DIAGPAGE_STRING_IO);
return (datalen + offsetof(ses2_string_out_page_impl_t, ssopi_data[0]));
}
/*ARGSUSED*/
static size_t
ses2_threshout_len(uint_t nelem, int page, size_t datalen)
{
ASSERT(page == SES2_DIAGPAGE_THRESHOLD_IO);
return (nelem * sizeof (ses2_threshold_impl_t) +
offsetof(ses2_threshold_out_page_impl_t, stopi_thresholds[0]));
}
/*ARGSUSED*/
static void *
ses2_threshout_ctl_fill(ses_plugin_t *sp, void *pagedata, size_t pagelen,
ses_node_t *np)
{
uint64_t index;
nvlist_t *props = ses_node_props(np);
ses2_threshold_out_page_impl_t *pip = pagedata;
ses2_diag_page_t page = SES2_DIAGPAGE_THRESHOLD_IO;
void *data;
VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
&index) == 0);
data = &pip[index];
if (ses2_ctl_common_setdef(np, page, data) != 0 ||
ses2_element_setdef(np, page, data) != 0 ||
ses2_enclosure_setdef(np, page, data) != 0)
return (NULL);
return (data);
}
/*ARGSUSED*/
static size_t
ses2_substrout_len(uint_t nelem, int page, size_t datalen)
{
ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO);
return (datalen +
offsetof(ses2_substring_out_page_impl_t, ssopi_data[0]));
}
/*ARGSUSED*/
static size_t
ses2_ucodeout_len(uint_t nelem, int page, size_t datalen)
{
size_t len;
ASSERT(page == SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS);
len = datalen +
offsetof(ses2_ucode_ctl_page_impl_t, sucpi_ucode_data[0]);
return (P2ROUNDUP(len, 4));
}
/*ARGSUSED*/
static void *
ses2_ucodeout_ctl_fill(ses_plugin_t *sp, void *data, size_t pagelen,
ses_node_t *np)
{
ses_snap_t *snap = ses_node_snapshot(np);
nvlist_t *props = ses_node_props(np);
ses2_ucode_ctl_page_impl_t *uip = data;
uint64_t eid;
if (ses_node_type(np) != SES_NODE_ENCLOSURE) {
(void) ses_error(ESES_BAD_TYPE,
"microcode download page only valid for enclosure "
"nodes");
return (NULL);
}
VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
SCSI_WRITE32(&uip->sucpi_generation_code,
ses_snap_generation(snap));
uip->sucpi_subenclosure_identifier = eid;
return (data);
}
/*ARGSUSED*/
static size_t
ses2_subnickout_len(uint_t nelem, int page, size_t datalen)
{
ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS);
return (sizeof (ses2_subnick_ctl_page_impl_t));
}
ses_pagedesc_t ses2_pages[] = {
{
.spd_pagenum = SES2_DIAGPAGE_SUPPORTED_PAGES,
.spd_req = SES_REQ_MANDATORY_ALL,
.spd_gcoff = -1
},
{
.spd_pagenum = SES2_DIAGPAGE_CONFIG,
.spd_req = SES_REQ_MANDATORY_STANDARD,
.spd_gcoff = offsetof(ses2_config_page_impl_t, scpi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
.spd_req = SES_REQ_MANDATORY_STANDARD,
.spd_index = ses2_status_index,
.spd_gcoff = offsetof(ses2_status_page_impl_t, sspi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_HELP_TEXT,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff = -1
},
{
.spd_pagenum = SES2_DIAGPAGE_STRING_IO,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff = -1
},
{
.spd_pagenum = SES2_DIAGPAGE_THRESHOLD_IO,
.spd_index = ses2_threshold_index,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_threshold_in_page_impl_t, stipi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_ELEMENT_DESC,
.spd_index = ses2_element_index,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff = offsetof(ses2_elem_desc_page_impl_t, sedpi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_ADDL_ELEM_STATUS,
.spd_index = ses2_aes_index,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff = offsetof(ses2_aes_page_impl_t, sapi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_HELP_TEXT,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff = offsetof(ses2_subhelp_page_impl_t, sspi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_substring_in_page_impl_t, ssipi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_SUPPORTED_SES_PAGES,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff = -1
},
{
.spd_pagenum = SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_ucode_status_page_impl_t, suspi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_subnick_status_page_impl_t, sspci_generation_code)
},
/* Control pages */
{
.spd_pagenum = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
.spd_ctl_len = ses2_ctl_len,
.spd_ctl_fill = ses2_ctl_fill,
.spd_req = SES_REQ_MANDATORY_STANDARD,
.spd_gcoff = offsetof(ses2_control_page_impl_t, scpi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_STRING_IO,
.spd_ctl_len = ses2_stringout_len,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff = -1
},
{
.spd_pagenum = SES2_DIAGPAGE_THRESHOLD_IO,
.spd_ctl_len = ses2_threshout_len,
.spd_ctl_fill = ses2_threshout_ctl_fill,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_threshold_out_page_impl_t, stopi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO,
.spd_ctl_len = ses2_substrout_len,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_substring_out_page_impl_t, ssopi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS,
.spd_ctl_len = ses2_ucodeout_len,
.spd_ctl_fill = ses2_ucodeout_ctl_fill,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_ucode_ctl_page_impl_t, sucpi_generation_code)
},
{
.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS,
.spd_ctl_len = ses2_subnickout_len,
.spd_req = SES_REQ_OPTIONAL_STANDARD,
.spd_gcoff =
offsetof(ses2_subnick_ctl_page_impl_t, sspci_generation_code)
},
{
.spd_pagenum = -1,
.spd_gcoff = -1
}
};