2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <stddef.h>
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <libnvpair.h>
2N/A
2N/A#include <scsi/libses.h>
2N/A#include "ses2_impl.h"
2N/A
2N/A#define SES_UCODE_DEF_CHUNK (32 * 1024)
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Aenc_do_ucode(ses_plugin_t *sp, ses_node_t *np, nvlist_t *nvl)
2N/A{
2N/A nvlist_t *props = ses_node_props(np);
2N/A uint64_t maxlen, bufid = 0;
2N/A uint8_t *data;
2N/A ses2_ucode_ctl_page_impl_t *uip;
2N/A size_t offset, len, pagelen;
2N/A uint_t datalen;
2N/A uint64_t mode;
2N/A uint64_t chunksz = SES_UCODE_DEF_CHUNK;
2N/A
2N/A /*
2N/A * Get the data and check the length.
2N/A */
2N/A if (nvlist_lookup_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
2N/A &data, &datalen) != 0)
2N/A return (ses_error(ESES_INVALID_PROP,
2N/A "missing or invalid %s property", SES_CTL_PROP_UCODE_DATA));
2N/A
2N/A if (nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
2N/A &mode) != 0)
2N/A return (ses_error(ESES_INVALID_PROP,
2N/A "missing or invalid %s property", SES_CTL_PROP_UCODE_MODE));
2N/A
2N/A if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_SZ,
2N/A &maxlen) != 0 || datalen > maxlen)
2N/A return (ses_error(ESES_RANGE,
2N/A "microcode image length (%u) exceeds maximum length (%llu)",
2N/A datalen, maxlen));
2N/A
2N/A /*
2N/A * Get the expected buffer ID, but allow the user to override it.
2N/A */
2N/A (void) nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_BUF,
2N/A &bufid);
2N/A
2N/A if (bufid == 0xFF)
2N/A bufid = 0;
2N/A
2N/A (void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_BUFID, &bufid);
2N/A (void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_DATA_LEN, &chunksz);
2N/A
2N/A if (chunksz & 3)
2N/A return (ses_error(ESES_RANGE,
2N/A "upload chunk size %llu is not divisible by 4", chunksz));
2N/A
2N/A for (offset = 0; offset < datalen; offset += chunksz) {
2N/A
2N/A len = MIN(datalen - offset, chunksz);
2N/A if (len & 0x3)
2N/A pagelen = (len + 4) & ~0x3;
2N/A else
2N/A pagelen = len;
2N/A
2N/A if ((uip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
2N/A SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS, pagelen,
2N/A np, B_TRUE)) == NULL)
2N/A return (-1);
2N/A
2N/A uip->sucpi_buffer_id = (uint8_t)bufid;
2N/A uip->sucpi_dl_ucode_mode = mode;
2N/A SCSI_WRITE32(&uip->sucpi_buffer_offset, offset);
2N/A SCSI_WRITE32(&uip->sucpi_ucode_image_length, datalen);
2N/A SCSI_WRITE32(&uip->sucpi_ucode_data_length, len);
2N/A
2N/A bcopy(data + offset, &uip->sucpi_ucode_data[0],
2N/A len);
2N/A
2N/A if (len != pagelen)
2N/A bzero(&uip->sucpi_ucode_data[0] + len,
2N/A pagelen - len);
2N/A }
2N/A
2N/A (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA);
2N/A (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_MODE);
2N/A (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_BUFID);
2N/A (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA_LEN);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Aenc_ctl_common(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
2N/A nvpair_t *nvp)
2N/A{
2N/A ses2_enclosure_ctl_impl_t *tp;
2N/A const char *name;
2N/A boolean_t boolval;
2N/A uint64_t intval;
2N/A
2N/A ASSERT(page == SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS);
2N/A
2N/A if ((tp = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
2N/A page, 0, np, B_FALSE)) == NULL)
2N/A return (-1);
2N/A
2N/A name = nvpair_name(nvp);
2N/A (void) nvpair_value_boolean_value(nvp, &boolval);
2N/A (void) nvpair_value_uint64(nvp, &intval);
2N/A
2N/A if (strcmp(name, SES_PROP_IDENT) == 0)
2N/A tp->seci_rqst_ident = boolval;
2N/A else if (strcmp(name, SES_PROP_WARN_REQ) == 0)
2N/A tp->seci_request_warning = boolval;
2N/A else if (strcmp(name, SES_PROP_FAIL_REQ) == 0)
2N/A tp->seci_request_failure = boolval;
2N/A else if (strcmp(name, SES_EN_PROP_POWER_DELAY) == 0)
2N/A tp->seci_power_cycle_delay = intval;
2N/A else if (strcmp(name, SES_EN_PROP_POWER_REQUEST) == 0)
2N/A tp->seci_power_cycle_request = intval;
2N/A else if (strcmp(name, SES_EN_PROP_POWER_DURATION) == 0)
2N/A tp->seci_power_off_duration = intval;
2N/A else
2N/A ses_panic("bad property %s", name);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Aenc_ctl_string(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
2N/A nvpair_t *nvp)
2N/A{
2N/A ses2_substring_out_page_impl_t *spip;
2N/A ses2_string_out_page_impl_t *pip;
2N/A const uint8_t *data;
2N/A size_t datalen;
2N/A uint_t nvlen;
2N/A nvlist_t *props = ses_node_props(np);
2N/A uint64_t eid;
2N/A
2N/A ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_STRING) == 0);
2N/A
2N/A VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
2N/A
2N/A (void) nvpair_value_byte_array(nvp, (uint8_t **)&data, &nvlen);
2N/A datalen = (size_t)nvlen;
2N/A
2N/A if ((spip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
2N/A SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO, datalen, np,
2N/A B_FALSE)) != NULL) {
2N/A spip->ssopi_subenclosure_identifier = eid;
2N/A bcopy(data, spip->ssopi_data, datalen);
2N/A } else {
2N/A if (eid != 0)
2N/A return (ses_error(ESES_NOTSUP, "target does not "
2N/A "support string data for secondary subenclosures"));
2N/A
2N/A if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
2N/A SES2_DIAGPAGE_STRING_IO, datalen, np, B_FALSE)) == NULL)
2N/A return (-1);
2N/A
2N/A bcopy(data, pip->ssopi_data, datalen);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Aenc_ctl_nick(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
2N/A nvpair_t *nvp)
2N/A{
2N/A /* LINTED - dummy variable for sizeof */
2N/A ses2_subnick_ctl_page_impl_t *pip, dummy;
2N/A const char *nick;
2N/A size_t len, max;
2N/A nvlist_t *props = ses_node_props(np);
2N/A uint64_t eid;
2N/A
2N/A ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_NICK) == 0);
2N/A ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS);
2N/A
2N/A (void) nvpair_value_string(nvp, (char **)&nick);
2N/A len = strlen(nick);
2N/A
2N/A VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
2N/A
2N/A max = sizeof (dummy.sspci_subenclosure_nickname);
2N/A if (len > max)
2N/A return (ses_error(ESES_RANGE, "nickname '%s' exceeds "
2N/A "maximum length %lu", nick, max));
2N/A
2N/A if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
2N/A page, len, np, B_FALSE)) == NULL)
2N/A return (-1);
2N/A
2N/A pip->sspci_subenclosure_identifier = eid;
2N/A bcopy(nick, pip->sspci_subenclosure_nickname, len);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic const ses2_ctl_prop_t enc_props[] = {
2N/A SES_COMMON_CTL_PROPS,
2N/A{
2N/A .scp_name = SES_PROP_IDENT,
2N/A .scp_type = DATA_TYPE_BOOLEAN_VALUE,
2N/A .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
2N/A .scp_setprop = enc_ctl_common
2N/A},
2N/A{
2N/A .scp_name = SES_PROP_WARN_REQ,
2N/A .scp_type = DATA_TYPE_BOOLEAN_VALUE,
2N/A .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
2N/A .scp_setprop = enc_ctl_common
2N/A},
2N/A{
2N/A .scp_name = SES_PROP_FAIL_REQ,
2N/A .scp_type = DATA_TYPE_BOOLEAN_VALUE,
2N/A .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
2N/A .scp_setprop = enc_ctl_common
2N/A},
2N/A{
2N/A .scp_name = SES_EN_PROP_POWER_DELAY,
2N/A .scp_type = DATA_TYPE_UINT64,
2N/A .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
2N/A .scp_setprop = enc_ctl_common
2N/A},
2N/A{
2N/A .scp_name = SES_EN_PROP_POWER_DURATION,
2N/A .scp_type = DATA_TYPE_UINT64,
2N/A .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
2N/A .scp_setprop = enc_ctl_common
2N/A},
2N/A{
2N/A .scp_name = SES_EN_PROP_POWER_REQUEST,
2N/A .scp_type = DATA_TYPE_UINT64,
2N/A .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
2N/A .scp_setprop = enc_ctl_common
2N/A},
2N/A{
2N/A .scp_name = SES_EN_PROP_STRING,
2N/A .scp_type = DATA_TYPE_BYTE_ARRAY,
2N/A .scp_num = -1,
2N/A .scp_setprop = enc_ctl_string
2N/A},
2N/A{
2N/A .scp_name = SES_EN_PROP_NICK,
2N/A .scp_type = DATA_TYPE_STRING,
2N/A .scp_num = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS,
2N/A .scp_setprop = enc_ctl_nick
2N/A},
2N/A{
2N/A NULL
2N/A}
2N/A};
2N/A
2N/Astatic int
2N/Aenc_setdef_one(ses_node_t *np, ses2_diag_page_t page, void *data)
2N/A{
2N/A ses2_enclosure_ctl_impl_t *tp = data;
2N/A nvlist_t *props = ses_node_props(np);
2N/A
2N/A if (page != SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS)
2N/A return (0);
2N/A
2N/A SES_NV_CTLBOOL(props, SES_PROP_IDENT, tp->seci_rqst_ident);
2N/A SES_NV_CTLBOOL(props, SES_PROP_WARN_REQ,
2N/A tp->seci_request_warning);
2N/A SES_NV_CTLBOOL(props, SES_PROP_FAIL_REQ,
2N/A tp->seci_request_failure);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Ases2_enclosure_ctl(ses_plugin_t *sp, ses_node_t *np, const char *op,
2N/A nvlist_t *nvl)
2N/A{
2N/A if (strcmp(op, SES_CTL_OP_SETPROP) == 0)
2N/A return (ses2_setprop(sp, np, enc_props, nvl));
2N/A else if (strcmp(op, SES_CTL_OP_DL_UCODE) == 0)
2N/A return (enc_do_ucode(sp, np, nvl));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Ases2_enclosure_setdef(ses_node_t *np, ses2_diag_page_t page, void *data)
2N/A{
2N/A nvlist_t *props = ses_node_props(np);
2N/A uint64_t type;
2N/A
2N/A VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0);
2N/A
2N/A if (type == SES_ET_ENCLOSURE &&
2N/A enc_setdef_one(np, page, data) != 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}