1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/types.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <libnvpair.h>
32 
33 #include <scsi/libses.h>
34 #include "ses2_impl.h"
35 
36 #define	SES_UCODE_DEF_CHUNK	(32 * 1024)
37 
38 /*ARGSUSED*/
39 static int
enc_do_ucode(ses_plugin_t * sp,ses_node_t * np,nvlist_t * nvl)40 enc_do_ucode(ses_plugin_t *sp, ses_node_t *np, nvlist_t *nvl)
41 {
42 	nvlist_t *props = ses_node_props(np);
43 	uint64_t maxlen, bufid = 0;
44 	uint8_t *data;
45 	ses2_ucode_ctl_page_impl_t *uip;
46 	size_t offset, len, pagelen;
47 	uint_t datalen;
48 	uint64_t mode;
49 	uint64_t chunksz = SES_UCODE_DEF_CHUNK;
50 
51 	/*
52 	 * Get the data and check the length.
53 	 */
54 	if (nvlist_lookup_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
55 	    &data, &datalen) != 0)
56 		return (ses_error(ESES_INVALID_PROP,
57 		    "missing or invalid %s property", SES_CTL_PROP_UCODE_DATA));
58 
59 	if (nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
60 	    &mode) != 0)
61 		return (ses_error(ESES_INVALID_PROP,
62 		    "missing or invalid %s property", SES_CTL_PROP_UCODE_MODE));
63 
64 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_SZ,
65 	    &maxlen) != 0 || datalen > maxlen)
66 		return (ses_error(ESES_RANGE,
67 		    "microcode image length (%u) exceeds maximum length (%llu)",
68 		    datalen, maxlen));
69 
70 	/*
71 	 * Get the expected buffer ID, but allow the user to override it.
72 	 */
73 	(void) nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_BUF,
74 	    &bufid);
75 
76 	if (bufid == 0xFF)
77 		bufid = 0;
78 
79 	(void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_BUFID, &bufid);
80 	(void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_DATA_LEN, &chunksz);
81 
82 	if (chunksz & 3)
83 		return (ses_error(ESES_RANGE,
84 		    "upload chunk size %llu is not divisible by 4", chunksz));
85 
86 	for (offset = 0; offset < datalen; offset += chunksz)  {
87 
88 		len = MIN(datalen - offset, chunksz);
89 		if (len & 0x3)
90 			pagelen = (len + 4) & ~0x3;
91 		else
92 			pagelen = len;
93 
94 		if ((uip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
95 		    SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS, pagelen,
96 		    np, B_TRUE)) == NULL)
97 			return (-1);
98 
99 		uip->sucpi_buffer_id = (uint8_t)bufid;
100 		uip->sucpi_dl_ucode_mode = mode;
101 		SCSI_WRITE32(&uip->sucpi_buffer_offset, offset);
102 		SCSI_WRITE32(&uip->sucpi_ucode_image_length, datalen);
103 		SCSI_WRITE32(&uip->sucpi_ucode_data_length, len);
104 
105 		bcopy(data + offset, &uip->sucpi_ucode_data[0],
106 		    len);
107 
108 		if (len != pagelen)
109 			bzero(&uip->sucpi_ucode_data[0] + len,
110 			    pagelen - len);
111 	}
112 
113 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA);
114 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_MODE);
115 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_BUFID);
116 	(void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA_LEN);
117 
118 	return (0);
119 }
120 
121 static int
enc_ctl_common(ses_plugin_t * sp,ses_node_t * np,ses2_diag_page_t page,nvpair_t * nvp)122 enc_ctl_common(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
123     nvpair_t *nvp)
124 {
125 	ses2_enclosure_ctl_impl_t *tp;
126 	const char *name;
127 	boolean_t boolval;
128 	uint64_t intval;
129 
130 	ASSERT(page == SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS);
131 
132 	if ((tp = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
133 	    page, 0, np, B_FALSE)) == NULL)
134 		return (-1);
135 
136 	name = nvpair_name(nvp);
137 	(void) nvpair_value_boolean_value(nvp, &boolval);
138 	(void) nvpair_value_uint64(nvp, &intval);
139 
140 	if (strcmp(name, SES_PROP_IDENT) == 0)
141 		tp->seci_rqst_ident = boolval;
142 	else if (strcmp(name, SES_PROP_WARN_REQ) == 0)
143 		tp->seci_request_warning = boolval;
144 	else if (strcmp(name, SES_PROP_FAIL_REQ) == 0)
145 		tp->seci_request_failure = boolval;
146 	else if (strcmp(name, SES_EN_PROP_POWER_DELAY) == 0)
147 		tp->seci_power_cycle_delay = intval;
148 	else if (strcmp(name, SES_EN_PROP_POWER_REQUEST) == 0)
149 		tp->seci_power_cycle_request = intval;
150 	else if (strcmp(name, SES_EN_PROP_POWER_DURATION) == 0)
151 		tp->seci_power_off_duration = intval;
152 	else
153 		ses_panic("bad property %s", name);
154 
155 	return (0);
156 }
157 
158 /*ARGSUSED*/
159 static int
enc_ctl_string(ses_plugin_t * sp,ses_node_t * np,ses2_diag_page_t page,nvpair_t * nvp)160 enc_ctl_string(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
161     nvpair_t *nvp)
162 {
163 	ses2_substring_out_page_impl_t *spip;
164 	ses2_string_out_page_impl_t *pip;
165 	const uint8_t *data;
166 	size_t datalen;
167 	uint_t nvlen;
168 	nvlist_t *props = ses_node_props(np);
169 	uint64_t eid;
170 
171 	ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_STRING) == 0);
172 
173 	VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
174 
175 	(void) nvpair_value_byte_array(nvp, (uint8_t **)&data, &nvlen);
176 	datalen = (size_t)nvlen;
177 
178 	if ((spip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
179 	    SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO, datalen, np,
180 	    B_FALSE)) != NULL) {
181 		spip->ssopi_subenclosure_identifier = eid;
182 		bcopy(data, spip->ssopi_data, datalen);
183 	} else {
184 		if (eid != 0)
185 			return (ses_error(ESES_NOTSUP, "target does not "
186 			    "support string data for secondary subenclosures"));
187 
188 		if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
189 		    SES2_DIAGPAGE_STRING_IO, datalen, np, B_FALSE)) == NULL)
190 			return (-1);
191 
192 		bcopy(data, pip->ssopi_data, datalen);
193 	}
194 
195 	return (0);
196 }
197 
198 static int
enc_ctl_nick(ses_plugin_t * sp,ses_node_t * np,ses2_diag_page_t page,nvpair_t * nvp)199 enc_ctl_nick(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page,
200     nvpair_t *nvp)
201 {
202 	/* LINTED - dummy variable for sizeof */
203 	ses2_subnick_ctl_page_impl_t *pip, dummy;
204 	const char *nick;
205 	size_t len, max;
206 	nvlist_t *props = ses_node_props(np);
207 	uint64_t eid;
208 
209 	ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_NICK) == 0);
210 	ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS);
211 
212 	(void) nvpair_value_string(nvp, (char **)&nick);
213 	len = strlen(nick);
214 
215 	VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
216 
217 	max = sizeof (dummy.sspci_subenclosure_nickname);
218 	if (len > max)
219 		return (ses_error(ESES_RANGE, "nickname '%s' exceeds "
220 		    "maximum length %lu", nick, max));
221 
222 	if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np),
223 	    page, len, np, B_FALSE)) == NULL)
224 		return (-1);
225 
226 	pip->sspci_subenclosure_identifier = eid;
227 	bcopy(nick, pip->sspci_subenclosure_nickname, len);
228 
229 	return (0);
230 }
231 
232 static const ses2_ctl_prop_t enc_props[] = {
233 	SES_COMMON_CTL_PROPS,
234 {
235 	.scp_name = SES_PROP_IDENT,
236 	.scp_type = DATA_TYPE_BOOLEAN_VALUE,
237 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
238 	.scp_setprop = enc_ctl_common
239 },
240 {
241 	.scp_name = SES_PROP_WARN_REQ,
242 	.scp_type = DATA_TYPE_BOOLEAN_VALUE,
243 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
244 	.scp_setprop = enc_ctl_common
245 },
246 {
247 	.scp_name = SES_PROP_FAIL_REQ,
248 	.scp_type = DATA_TYPE_BOOLEAN_VALUE,
249 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
250 	.scp_setprop = enc_ctl_common
251 },
252 {
253 	.scp_name = SES_EN_PROP_POWER_DELAY,
254 	.scp_type = DATA_TYPE_UINT64,
255 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
256 	.scp_setprop = enc_ctl_common
257 },
258 {
259 	.scp_name = SES_EN_PROP_POWER_DURATION,
260 	.scp_type = DATA_TYPE_UINT64,
261 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
262 	.scp_setprop = enc_ctl_common
263 },
264 {
265 	.scp_name = SES_EN_PROP_POWER_REQUEST,
266 	.scp_type = DATA_TYPE_UINT64,
267 	.scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
268 	.scp_setprop = enc_ctl_common
269 },
270 {
271 	.scp_name = SES_EN_PROP_STRING,
272 	.scp_type = DATA_TYPE_BYTE_ARRAY,
273 	.scp_num = -1,
274 	.scp_setprop = enc_ctl_string
275 },
276 {
277 	.scp_name = SES_EN_PROP_NICK,
278 	.scp_type = DATA_TYPE_STRING,
279 	.scp_num = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS,
280 	.scp_setprop = enc_ctl_nick
281 },
282 {
283 	NULL
284 }
285 };
286 
287 static int
enc_setdef_one(ses_node_t * np,ses2_diag_page_t page,void * data)288 enc_setdef_one(ses_node_t *np, ses2_diag_page_t page, void *data)
289 {
290 	ses2_enclosure_ctl_impl_t *tp = data;
291 	nvlist_t *props = ses_node_props(np);
292 
293 	if (page != SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS)
294 		return (0);
295 
296 	SES_NV_CTLBOOL(props, SES_PROP_IDENT, tp->seci_rqst_ident);
297 	SES_NV_CTLBOOL(props, SES_PROP_WARN_REQ,
298 	    tp->seci_request_warning);
299 	SES_NV_CTLBOOL(props, SES_PROP_FAIL_REQ,
300 	    tp->seci_request_failure);
301 
302 	return (0);
303 }
304 
305 int
ses2_enclosure_ctl(ses_plugin_t * sp,ses_node_t * np,const char * op,nvlist_t * nvl)306 ses2_enclosure_ctl(ses_plugin_t *sp, ses_node_t *np, const char *op,
307     nvlist_t *nvl)
308 {
309 	if (strcmp(op, SES_CTL_OP_SETPROP) == 0)
310 		return (ses2_setprop(sp, np, enc_props, nvl));
311 	else if (strcmp(op, SES_CTL_OP_DL_UCODE) == 0)
312 		return (enc_do_ucode(sp, np, nvl));
313 
314 	return (0);
315 }
316 
317 int
ses2_enclosure_setdef(ses_node_t * np,ses2_diag_page_t page,void * data)318 ses2_enclosure_setdef(ses_node_t *np, ses2_diag_page_t page, void *data)
319 {
320 	nvlist_t *props = ses_node_props(np);
321 	uint64_t type;
322 
323 	VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0);
324 
325 	if (type == SES_ET_ENCLOSURE &&
326 	    enc_setdef_one(np, page, data) != 0)
327 		return (-1);
328 
329 	return (0);
330 }
331