1275c9da8Seschrock /*
2275c9da8Seschrock  * CDDL HEADER START
3275c9da8Seschrock  *
4275c9da8Seschrock  * The contents of this file are subject to the terms of the
5275c9da8Seschrock  * Common Development and Distribution License (the "License").
6275c9da8Seschrock  * You may not use this file except in compliance with the License.
7275c9da8Seschrock  *
8275c9da8Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9275c9da8Seschrock  * or http://www.opensolaris.org/os/licensing.
10275c9da8Seschrock  * See the License for the specific language governing permissions
11275c9da8Seschrock  * and limitations under the License.
12275c9da8Seschrock  *
13275c9da8Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14275c9da8Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15275c9da8Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16275c9da8Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17275c9da8Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18275c9da8Seschrock  *
19275c9da8Seschrock  * CDDL HEADER END
20275c9da8Seschrock  */
21275c9da8Seschrock 
22275c9da8Seschrock /*
23*ac88567aSHyon Kim  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24275c9da8Seschrock  */
25275c9da8Seschrock 
26275c9da8Seschrock #include <sys/types.h>
27275c9da8Seschrock #include <stddef.h>
28275c9da8Seschrock #include <stdio.h>
29275c9da8Seschrock #include <string.h>
30275c9da8Seschrock #include <strings.h>
31275c9da8Seschrock #include <libnvpair.h>
32275c9da8Seschrock 
33275c9da8Seschrock #include <scsi/libses.h>
34275c9da8Seschrock #include "ses2_impl.h"
35275c9da8Seschrock 
36*ac88567aSHyon Kim #define	SES_UCODE_DEF_CHUNK	(32 * 1024)
37275c9da8Seschrock 
38275c9da8Seschrock /*ARGSUSED*/
39275c9da8Seschrock static int
enc_do_ucode(ses_plugin_t * sp,ses_node_t * np,nvlist_t * nvl)40275c9da8Seschrock enc_do_ucode(ses_plugin_t *sp, ses_node_t *np, nvlist_t *nvl)
41275c9da8Seschrock {
42275c9da8Seschrock 	nvlist_t *props = ses_node_props(np);
43275c9da8Seschrock 	uint64_t maxlen, bufid = 0;
44275c9da8Seschrock 	uint8_t *data;
45275c9da8Seschrock 	ses2_ucode_ctl_page_impl_t *uip;
46275c9da8Seschrock 	size_t offset, len, pagelen;
47275c9da8Seschrock 	uint_t datalen;
48275c9da8Seschrock 	uint64_t mode;
49*ac88567aSHyon Kim 	uint64_t chunksz = SES_UCODE_DEF_CHUNK;
50275c9da8Seschrock 
51275c9da8Seschrock 	/*
52275c9da8Seschrock 	 * Get the data and check the length.
53275c9da8Seschrock 	 */
54275c9da8Seschrock 	if (nvlist_lookup_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
55275c9da8Seschrock 	    &data, &datalen) != 0)
56275c9da8Seschrock 		return (ses_error(ESES_INVALID_PROP,
57275c9da8Seschrock 		    "missing or invalid %s property", SES_CTL_PROP_UCODE_DATA));
58275c9da8Seschrock 
59275c9da8Seschrock 	if (nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
60275c9da8Seschrock 	    &mode) != 0)
61275c9da8Seschrock 		return (ses_error(ESES_INVALID_PROP,
62275c9da8Seschrock 		    "missing or invalid %s property", SES_CTL_PROP_UCODE_MODE));
63275c9da8Seschrock 
64275c9da8Seschrock 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_SZ,
65275c9da8Seschrock 	    &maxlen) != 0 || datalen > maxlen)
66275c9da8Seschrock 		return (ses_error(ESES_RANGE,
67275c9da8Seschrock 		    "microcode image length (%u) exceeds maximum length (%llu)",
68275c9da8Seschrock 		    datalen, maxlen));
69275c9da8Seschrock 
70275c9da8Seschrock 	/*
71275c9da8Seschrock 	 * Get the expected buffer ID, but allow the user to override it.
72275c9da8Seschrock 	 */
73275c9da8Seschrock 	(void) nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_BUF,
74275c9da8Seschrock 	    &bufid);
75275c9da8Seschrock 
76275c9da8Seschrock 	if (bufid == 0xFF)
77275c9da8Seschrock 		bufid = 0;
78275c9da8Seschrock 
79275c9da8Seschrock 	(void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_BUFID, &bufid);
80*ac88567aSHyon Kim 	(void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_DATA_LEN, &chunksz);
81*ac88567aSHyon Kim 
82*ac88567aSHyon Kim 	if (chunksz & 3)
83*ac88567aSHyon Kim 		return (ses_error(ESES_RANGE,
84*ac88567aSHyon Kim 		    "upload chunk size %llu is not divisible by 4", chunksz));
85275c9da8Seschrock 
86*ac88567aSHyon Kim 	for (offset = 0; offset < datalen; offset += chunksz)  {
87275c9da8Seschrock 
88*ac88567aSHyon Kim 		len = MIN(datalen - offset, chunksz);
89275c9da8Seschrock 		if (len & 0x3)
90bf82a41bSeschrock 			pagelen = (len + 4) & ~0x3;
91275c9da8Seschrock 		else
92275c9da8Seschrock 			pagelen = len;
93275c9da8Seschrock 
94275c9da8Seschrock 		if ((uip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
95275c9da8Seschrock 		    SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS, pagelen,
96275c9da8Seschrock 		    np, B_TRUE)) == NULL)
97275c9da8Seschrock 			return (-1);
98275c9da8Seschrock 
99275c9da8Seschrock 		uip->sucpi_buffer_id = (uint8_t)bufid;
100275c9da8Seschrock 		uip->sucpi_dl_ucode_mode = mode;
101275c9da8Seschrock 		SCSI_WRITE32(&uip->sucpi_buffer_offset, offset);
102275c9da8Seschrock 		SCSI_WRITE32(&uip->sucpi_ucode_image_length, datalen);
103275c9da8Seschrock 		SCSI_WRITE32(&uip->sucpi_ucode_data_length, len);
104275c9da8Seschrock 
105275c9da8Seschrock 		bcopy(data + offset, &uip->sucpi_ucode_data[0],
106275c9da8Seschrock 		    len);
107275c9da8Seschrock 
108275c9da8Seschrock 		if (len != pagelen)
109275c9da8Seschrock 			bzero(&uip->sucpi_ucode_data[0] + len,
110275c9da8Seschrock 			    pagelen - len);
111275c9da8Seschrock 	}
112275c9da8Seschrock 
113275c9da8Seschrock 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA);
114275c9da8Seschrock 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_MODE);
115275c9da8Seschrock 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_BUFID);
116*ac88567aSHyon Kim 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA_LEN);
117275c9da8Seschrock 
118275c9da8Seschrock 	return (0);
119275c9da8Seschrock }
120275c9da8Seschrock 
121275c9da8Seschrock static int
enc_ctl_common(ses_plugin_t * sp,ses_node_t * np,ses2_diag_page_t page,nvpair_t * nvp)122275c9da8Seschrock enc_ctl_common(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
123275c9da8Seschrock     nvpair_t *nvp)
124275c9da8Seschrock {
125275c9da8Seschrock 	ses2_enclosure_ctl_impl_t *tp;
126275c9da8Seschrock 	const char *name;
127275c9da8Seschrock 	boolean_t boolval;
128275c9da8Seschrock 	uint64_t intval;
129275c9da8Seschrock 
130275c9da8Seschrock 	ASSERT(page == SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS);
131275c9da8Seschrock 
132275c9da8Seschrock 	if ((tp = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
133275c9da8Seschrock 	    page, 0, np, B_FALSE)) == NULL)
134275c9da8Seschrock 		return (-1);
135275c9da8Seschrock 
136275c9da8Seschrock 	name = nvpair_name(nvp);
137275c9da8Seschrock 	(void) nvpair_value_boolean_value(nvp, &boolval);
138275c9da8Seschrock 	(void) nvpair_value_uint64(nvp, &intval);
139275c9da8Seschrock 
140275c9da8Seschrock 	if (strcmp(name, SES_PROP_IDENT) == 0)
141275c9da8Seschrock 		tp->seci_rqst_ident = boolval;
142275c9da8Seschrock 	else if (strcmp(name, SES_PROP_WARN_REQ) == 0)
143275c9da8Seschrock 		tp->seci_request_warning = boolval;
144275c9da8Seschrock 	else if (strcmp(name, SES_PROP_FAIL_REQ) == 0)
145275c9da8Seschrock 		tp->seci_request_failure = boolval;
146275c9da8Seschrock 	else if (strcmp(name, SES_EN_PROP_POWER_DELAY) == 0)
147275c9da8Seschrock 		tp->seci_power_cycle_delay = intval;
148275c9da8Seschrock 	else if (strcmp(name, SES_EN_PROP_POWER_REQUEST) == 0)
149275c9da8Seschrock 		tp->seci_power_cycle_request = intval;
150275c9da8Seschrock 	else if (strcmp(name, SES_EN_PROP_POWER_DURATION) == 0)
151275c9da8Seschrock 		tp->seci_power_off_duration = intval;
152275c9da8Seschrock 	else
153275c9da8Seschrock 		ses_panic("bad property %s", name);
154275c9da8Seschrock 
155275c9da8Seschrock 	return (0);
156275c9da8Seschrock }
157275c9da8Seschrock 
158275c9da8Seschrock /*ARGSUSED*/
159275c9da8Seschrock static int
enc_ctl_string(ses_plugin_t * sp,ses_node_t * np,ses2_diag_page_t page,nvpair_t * nvp)160275c9da8Seschrock enc_ctl_string(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
161275c9da8Seschrock     nvpair_t *nvp)
162275c9da8Seschrock {
163275c9da8Seschrock 	ses2_substring_out_page_impl_t *spip;
164275c9da8Seschrock 	ses2_string_out_page_impl_t *pip;
165275c9da8Seschrock 	const uint8_t *data;
166275c9da8Seschrock 	size_t datalen;
167275c9da8Seschrock 	uint_t nvlen;
168275c9da8Seschrock 	nvlist_t *props = ses_node_props(np);
169275c9da8Seschrock 	uint64_t eid;
170275c9da8Seschrock 
171275c9da8Seschrock 	ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_STRING) == 0);
172275c9da8Seschrock 
173275c9da8Seschrock 	VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
174275c9da8Seschrock 
175275c9da8Seschrock 	(void) nvpair_value_byte_array(nvp, (uint8_t **)&data, &nvlen);
176275c9da8Seschrock 	datalen = (size_t)nvlen;
177275c9da8Seschrock 
178275c9da8Seschrock 	if ((spip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
179275c9da8Seschrock 	    SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO, datalen, np,
180275c9da8Seschrock 	    B_FALSE)) != NULL) {
181275c9da8Seschrock 		spip->ssopi_subenclosure_identifier = eid;
182275c9da8Seschrock 		bcopy(data, spip->ssopi_data, datalen);
183275c9da8Seschrock 	} else {
184275c9da8Seschrock 		if (eid != 0)
185275c9da8Seschrock 			return (ses_error(ESES_NOTSUP, "target does not "
186275c9da8Seschrock 			    "support string data for secondary subenclosures"));
187275c9da8Seschrock 
188275c9da8Seschrock 		if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
189275c9da8Seschrock 		    SES2_DIAGPAGE_STRING_IO, datalen, np, B_FALSE)) == NULL)
190275c9da8Seschrock 			return (-1);
191275c9da8Seschrock 
192275c9da8Seschrock 		bcopy(data, pip->ssopi_data, datalen);
193275c9da8Seschrock 	}
194275c9da8Seschrock 
195275c9da8Seschrock 	return (0);
196275c9da8Seschrock }
197275c9da8Seschrock 
198275c9da8Seschrock static int
enc_ctl_nick(ses_plugin_t * sp,ses_node_t * np,ses2_diag_page_t page,nvpair_t * nvp)199275c9da8Seschrock enc_ctl_nick(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
200275c9da8Seschrock     nvpair_t *nvp)
201275c9da8Seschrock {
202275c9da8Seschrock 	/* LINTED - dummy variable for sizeof */
203275c9da8Seschrock 	ses2_subnick_ctl_page_impl_t *pip, dummy;
204275c9da8Seschrock 	const char *nick;
205275c9da8Seschrock 	size_t len, max;
206275c9da8Seschrock 	nvlist_t *props = ses_node_props(np);
207275c9da8Seschrock 	uint64_t eid;
208275c9da8Seschrock 
209275c9da8Seschrock 	ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_NICK) == 0);
210275c9da8Seschrock 	ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS);
211275c9da8Seschrock 
212275c9da8Seschrock 	(void) nvpair_value_string(nvp, (char **)&nick);
213275c9da8Seschrock 	len = strlen(nick);
214275c9da8Seschrock 
215275c9da8Seschrock 	VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
216275c9da8Seschrock 
217275c9da8Seschrock 	max = sizeof (dummy.sspci_subenclosure_nickname);
218275c9da8Seschrock 	if (len > max)
219275c9da8Seschrock 		return (ses_error(ESES_RANGE, "nickname '%s' exceeds "
220275c9da8Seschrock 		    "maximum length %lu", nick, max));
221275c9da8Seschrock 
222275c9da8Seschrock 	if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
223275c9da8Seschrock 	    page, len, np, B_FALSE)) == NULL)
224275c9da8Seschrock 		return (-1);
225275c9da8Seschrock 
226275c9da8Seschrock 	pip->sspci_subenclosure_identifier = eid;
227275c9da8Seschrock 	bcopy(nick, pip->sspci_subenclosure_nickname, len);
228275c9da8Seschrock 
229275c9da8Seschrock 	return (0);
230275c9da8Seschrock }
231275c9da8Seschrock 
232275c9da8Seschrock static const ses2_ctl_prop_t enc_props[] = {
233275c9da8Seschrock 	SES_COMMON_CTL_PROPS,
234275c9da8Seschrock {
235275c9da8Seschrock 	.scp_name = SES_PROP_IDENT,
236275c9da8Seschrock 	.scp_type = DATA_TYPE_BOOLEAN_VALUE,
237275c9da8Seschrock 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
238275c9da8Seschrock 	.scp_setprop = enc_ctl_common
239275c9da8Seschrock },
240275c9da8Seschrock {
241275c9da8Seschrock 	.scp_name = SES_PROP_WARN_REQ,
242275c9da8Seschrock 	.scp_type = DATA_TYPE_BOOLEAN_VALUE,
243275c9da8Seschrock 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
244275c9da8Seschrock 	.scp_setprop = enc_ctl_common
245275c9da8Seschrock },
246275c9da8Seschrock {
247275c9da8Seschrock 	.scp_name = SES_PROP_FAIL_REQ,
248275c9da8Seschrock 	.scp_type = DATA_TYPE_BOOLEAN_VALUE,
249275c9da8Seschrock 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
250275c9da8Seschrock 	.scp_setprop = enc_ctl_common
251275c9da8Seschrock },
252275c9da8Seschrock {
253275c9da8Seschrock 	.scp_name = SES_EN_PROP_POWER_DELAY,
254275c9da8Seschrock 	.scp_type = DATA_TYPE_UINT64,
255275c9da8Seschrock 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
256275c9da8Seschrock 	.scp_setprop = enc_ctl_common
257275c9da8Seschrock },
258275c9da8Seschrock {
259275c9da8Seschrock 	.scp_name = SES_EN_PROP_POWER_DURATION,
260275c9da8Seschrock 	.scp_type = DATA_TYPE_UINT64,
261275c9da8Seschrock 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
262275c9da8Seschrock 	.scp_setprop = enc_ctl_common
263275c9da8Seschrock },
264275c9da8Seschrock {
265275c9da8Seschrock 	.scp_name = SES_EN_PROP_POWER_REQUEST,
266275c9da8Seschrock 	.scp_type = DATA_TYPE_UINT64,
267275c9da8Seschrock 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
268275c9da8Seschrock 	.scp_setprop = enc_ctl_common
269275c9da8Seschrock },
270275c9da8Seschrock {
271275c9da8Seschrock 	.scp_name = SES_EN_PROP_STRING,
272275c9da8Seschrock 	.scp_type = DATA_TYPE_BYTE_ARRAY,
273275c9da8Seschrock 	.scp_num = -1,
274275c9da8Seschrock 	.scp_setprop = enc_ctl_string
275275c9da8Seschrock },
276275c9da8Seschrock {
277275c9da8Seschrock 	.scp_name = SES_EN_PROP_NICK,
278275c9da8Seschrock 	.scp_type = DATA_TYPE_STRING,
279275c9da8Seschrock 	.scp_num = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS,
280275c9da8Seschrock 	.scp_setprop = enc_ctl_nick
281275c9da8Seschrock },
282275c9da8Seschrock {
283275c9da8Seschrock 	NULL
284275c9da8Seschrock }
285275c9da8Seschrock };
286275c9da8Seschrock 
287275c9da8Seschrock static int
enc_setdef_one(ses_node_t * np,ses2_diag_page_t page,void * data)288275c9da8Seschrock enc_setdef_one(ses_node_t *np, ses2_diag_page_t page, void *data)
289275c9da8Seschrock {
290275c9da8Seschrock 	ses2_enclosure_ctl_impl_t *tp = data;
291275c9da8Seschrock 	nvlist_t *props = ses_node_props(np);
292275c9da8Seschrock 
293275c9da8Seschrock 	if (page != SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS)
294275c9da8Seschrock 		return (0);
295275c9da8Seschrock 
296275c9da8Seschrock 	SES_NV_CTLBOOL(props, SES_PROP_IDENT, tp->seci_rqst_ident);
297275c9da8Seschrock 	SES_NV_CTLBOOL(props, SES_PROP_WARN_REQ,
298275c9da8Seschrock 	    tp->seci_request_warning);
299275c9da8Seschrock 	SES_NV_CTLBOOL(props, SES_PROP_FAIL_REQ,
300275c9da8Seschrock 	    tp->seci_request_failure);
301275c9da8Seschrock 
302275c9da8Seschrock 	return (0);
303275c9da8Seschrock }
304275c9da8Seschrock 
305275c9da8Seschrock int
ses2_enclosure_ctl(ses_plugin_t * sp,ses_node_t * np,const char * op,nvlist_t * nvl)306275c9da8Seschrock ses2_enclosure_ctl(ses_plugin_t *sp, ses_node_t *np, const char *op,
307275c9da8Seschrock     nvlist_t *nvl)
308275c9da8Seschrock {
309275c9da8Seschrock 	if (strcmp(op, SES_CTL_OP_SETPROP) == 0)
310275c9da8Seschrock 		return (ses2_setprop(sp, np, enc_props, nvl));
311275c9da8Seschrock 	else if (strcmp(op, SES_CTL_OP_DL_UCODE) == 0)
312275c9da8Seschrock 		return (enc_do_ucode(sp, np, nvl));
313275c9da8Seschrock 
314275c9da8Seschrock 	return (0);
315275c9da8Seschrock }
316275c9da8Seschrock 
317275c9da8Seschrock int
ses2_enclosure_setdef(ses_node_t * np,ses2_diag_page_t page,void * data)318275c9da8Seschrock ses2_enclosure_setdef(ses_node_t *np, ses2_diag_page_t page, void *data)
319275c9da8Seschrock {
320275c9da8Seschrock 	nvlist_t *props = ses_node_props(np);
321275c9da8Seschrock 	uint64_t type;
322275c9da8Seschrock 
323275c9da8Seschrock 	VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0);
324275c9da8Seschrock 
325275c9da8Seschrock 	if (type == SES_ET_ENCLOSURE &&
326275c9da8Seschrock 	    enc_setdef_one(np, page, data) != 0)
327275c9da8Seschrock 		return (-1);
328275c9da8Seschrock 
329275c9da8Seschrock 	return (0);
330275c9da8Seschrock }
331