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*/
39static int
40enc_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
121static int
122enc_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*/
159static int
160enc_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
198static int
199enc_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
232static 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
287static int
288enc_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
305int
306ses2_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
317int
318ses2_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