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 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <limits.h>
28#include <ctype.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <unistd.h>
32#include <strings.h>
33#include <libintl.h>
34#include <libscf.h>
35#include <libnvpair.h>
36
37#include <libstmf.h>
38#include <libsrpt.h>
39
40#include "srpt_common.h"
41
42#define	SRPT_PROV_NAME	"srpt"
43
44/*
45 * Function:  srpt_GetConfig()
46 *
47 * Parameters:
48 *    cfg	Current SRPT configuration in nvlist form
49 *    token	Configuration generation number.  Use this token
50 *		if updating the configuration with srpt_SetConfig.
51 *
52 * Return Values:
53 *    0		Success
54 *    ENOMEM	Could not allocate resources
55 *    EINVAL	Invalid parameter
56 */
57int
58srpt_GetConfig(nvlist_t **cfg, uint64_t *token)
59{
60	int		ret = 0;
61	nvlist_t	*cfg_nv = NULL;
62	uint64_t	stmf_token = 0;
63	nvlist_t	*hcanv = NULL;
64
65	if (!cfg) {
66		return (EINVAL);
67	}
68
69	*cfg = NULL;
70
71	ret = stmfGetProviderDataProt(SRPT_PROV_NAME, &cfg_nv,
72	    STMF_PORT_PROVIDER_TYPE, &stmf_token);
73
74	if (ret == STMF_STATUS_SUCCESS) {
75		ret = 0;
76	} else if (ret == STMF_ERROR_NOT_FOUND) {
77		/* Not initialized yet */
78		ret = nvlist_alloc(&cfg_nv, NV_UNIQUE_NAME, 0);
79		if (ret != 0) {
80			return (ret);
81		}
82		/* create the HCA list */
83		ret = nvlist_alloc(&hcanv, NV_UNIQUE_NAME, 0);
84		if (ret == 0) {
85			ret = nvlist_add_nvlist(cfg_nv, SRPT_PROP_HCALIST,
86			    hcanv);
87			if (ret != 0) {
88				nvlist_free(hcanv);
89			}
90		}
91		if (ret != 0) {
92			nvlist_free(cfg_nv);
93			cfg_nv = NULL;
94		}
95	} else if (ret == STMF_ERROR_NOMEM) {
96		ret = ENOMEM;
97	} else {
98		ret = EINVAL;
99	}
100
101	*cfg = cfg_nv;
102	*token = stmf_token;
103
104	return (ret);
105}
106
107/*
108 * Function:  srpt_SetConfig()
109 *
110 * Parameters:
111 *    cfg	SRPT configuration in nvlist form
112 *    token	Configuration generation number from srpt_GetConfig.
113 *		Use this token to ensure the configuration hasn't been
114 *		updated by another user since the time it was fetched.
115 *
116 * Return Values:
117 *    0		Success
118 *    ENOMEM	Could not allocate resources
119 *    EINVAL	Invalid parameter
120 *    ECANCELED Configuration updated by another user
121 */
122int
123srpt_SetConfig(nvlist_t *cfg, uint64_t token)
124{
125	int		ret = 0;
126
127	ret = stmfSetProviderDataProt(SRPT_PROV_NAME, cfg,
128	    STMF_PORT_PROVIDER_TYPE, &token);
129
130	if (ret == STMF_STATUS_SUCCESS) {
131		ret = 0;
132	} else if (ret == STMF_ERROR_NOMEM) {
133		ret = ENOMEM;
134	} else if (ret == STMF_ERROR_PROV_DATA_STALE) {
135		ret = ECANCELED;  /* could be a better errno */
136	} else {
137		ret = EINVAL;
138	}
139
140	return (ret);
141}
142
143/*
144 * Function:  srpt_GetDefaultState()
145 *
146 * Parameters:
147 *    enabled	If B_TRUE, indicates that targets will be created for all
148 *		discovered HCAs that have not been specifically disabled.
149 *		If B_FALSE, targets will not be created unless the HCA has
150 *		been specifically enabled.  See also srpt_SetDefaultState().
151 *
152 * Return Values:
153 *    0		Success
154 *    ENOMEM	Could not allocate resources
155 *    EINVAL	Invalid parameter
156 */
157int
158srpt_GetDefaultState(boolean_t *enabled)
159{
160	int		ret;
161	nvlist_t	*cfgnv;
162	uint64_t	token;
163	boolean_t	val = B_TRUE;
164
165	if (enabled == NULL) {
166		return (EINVAL);
167	}
168
169	ret = srpt_GetConfig(&cfgnv, &token);
170	if (ret != 0) {
171		return (ret);
172	}
173
174	if (cfgnv != NULL) {
175		ret = nvlist_lookup_boolean_value(cfgnv,
176		    SRPT_PROP_DEFAULT_ENABLED, &val);
177
178		if (ret == ENOENT) {
179			ret = 0;
180		}
181	}
182
183	*enabled = val;
184	return (ret);
185}
186
187/*
188 * Function:  srpt_SetDefaultState()
189 *
190 * Parameters:
191 *    enabled	If B_TRUE, indicates that targets will be created for all
192 *		discovered HCAs that have not been specifically disabled.
193 *		If B_FALSE, targets will not be created unless the HCA has
194 *		been specifically enabled.  See also srpt_SetDefaultState().
195 *
196 * Return Values:
197 *    0		Success
198 *    ENOMEM	Could not allocate resources
199 *    EINVAL	Invalid parameter
200 */
201int
202srpt_SetDefaultState(boolean_t enabled)
203{
204	int		ret;
205	nvlist_t	*cfgnv;
206	uint64_t	token;
207
208	ret = srpt_GetConfig(&cfgnv, &token);
209	if (ret != 0) {
210		return (ret);
211	}
212
213	if (cfgnv == NULL) {
214		ret = nvlist_alloc(&cfgnv, NV_UNIQUE_NAME, 0);
215		if (ret != 0) {
216			return (ret);
217		}
218	}
219
220	ret = nvlist_add_boolean_value(cfgnv, SRPT_PROP_DEFAULT_ENABLED,
221	    enabled);
222
223	if (ret == 0) {
224		ret = srpt_SetConfig(cfgnv, token);
225	}
226
227	nvlist_free(cfgnv);
228
229	return (ret);
230}
231
232/*
233 * Function:  srpt_SetTargetState()
234 *
235 * Parameters:
236 *    hca_guid	HCA GUID.  See description of srpt_NormalizeGuid
237 *    enabled	If B_TRUE, indicates that a target will be created for
238 *		this HCA when the SRPT SMF service is enabled.  If B_FALSE,
239 *		a target will not be created
240 *
241 * Return Values:
242 *    0		Success
243 *    ENOMEM	Could not allocate resources
244 *    EINVAL	Invalid parameter
245 */
246int
247srpt_SetTargetState(char *hca_guid, boolean_t enabled)
248{
249	int		ret;
250	nvlist_t	*cfgnv;
251	uint64_t	token;
252	nvlist_t	*hcalist;
253	nvlist_t	*hcanv;
254	char		guid[32];
255	uint64_t	hcaguid;
256
257	if (hca_guid == NULL) {
258		return (EINVAL);
259	}
260
261	ret = srpt_NormalizeGuid(hca_guid, guid, sizeof (guid), &hcaguid);
262	if (ret != 0) {
263		return (ret);
264	}
265
266	ret = srpt_GetConfig(&cfgnv, &token);
267	if (ret != 0) {
268		return (ret);
269	}
270
271	/* get the list of HCAs */
272	ret = nvlist_lookup_nvlist(cfgnv, SRPT_PROP_HCALIST, &hcalist);
273	if (ret != 0) {
274		nvlist_free(cfgnv);
275		return (ret);
276	}
277
278	ret = nvlist_lookup_nvlist(hcalist, guid, &hcanv);
279	if (ret == ENOENT) {
280		/* no entry yet */
281		ret = nvlist_alloc(&hcanv, NV_UNIQUE_NAME, 0);
282		if (ret == 0) {
283			ret = nvlist_add_uint64(hcanv, SRPT_PROP_GUID, hcaguid);
284		}
285	}
286
287	if (ret == 0) {
288		ret = nvlist_add_boolean_value(hcanv, SRPT_PROP_ENABLED,
289		    enabled);
290	}
291
292	if (ret == 0) {
293		ret = nvlist_add_nvlist(hcalist, guid, hcanv);
294	}
295
296	if (ret == 0) {
297		ret = srpt_SetConfig(cfgnv, token);
298	}
299
300	nvlist_free(cfgnv);
301
302	return (ret);
303}
304
305/*
306 * Function:  srpt_GetTargetState()
307 *
308 * Parameters:
309 *    hca_guid	HCA GUID.  See description of srpt_NormalizeGuid
310 *    enabled	If B_TRUE, indicates that a target will be created for
311 *		this HCA when the SRPT SMF service is enabled.  If B_FALSE,
312 *		a target will not be created
313 *
314 * Return Values:
315 *    0		Success
316 *    ENOMEM	Could not allocate resources
317 *    EINVAL	Invalid parameter
318 */
319int
320srpt_GetTargetState(char *hca_guid, boolean_t *enabled)
321{
322	int		ret;
323	nvlist_t	*cfgnv;
324	uint64_t	token;
325	nvlist_t	*hcalist;
326	nvlist_t	*hcanv;
327	boolean_t	defaultState = B_TRUE;
328	char		guid[32];
329
330	if (hca_guid == NULL) {
331		return (EINVAL);
332	}
333
334	ret = srpt_NormalizeGuid(hca_guid, guid, sizeof (guid), NULL);
335	if (ret != 0) {
336		return (ret);
337	}
338
339	ret = srpt_GetConfig(&cfgnv, &token);
340	if (ret != 0) {
341		return (ret);
342	}
343
344	/* get the list of HCAs */
345	ret = nvlist_lookup_nvlist(cfgnv, SRPT_PROP_HCALIST, &hcalist);
346	if (ret != 0) {
347		nvlist_free(cfgnv);
348		return (ret);
349	}
350
351	/*
352	 * Find the default, for the likely case that this HCA isn't
353	 * explicitly set.
354	 */
355	(void) nvlist_lookup_boolean_value(cfgnv, SRPT_PROP_DEFAULT_ENABLED,
356	    &defaultState);
357
358	ret = nvlist_lookup_nvlist(hcalist, guid, &hcanv);
359	if (ret == 0) {
360		ret = nvlist_lookup_boolean_value(hcanv, SRPT_PROP_ENABLED,
361		    enabled);
362	}
363
364	if (ret == ENOENT) {
365		/* not explicitly set, use the default */
366		*enabled = defaultState;
367		ret = 0;
368	}
369
370	nvlist_free(cfgnv);
371
372	return (ret);
373
374}
375
376/*
377 * Function:  srpt_ResetTarget()
378 *
379 * Clears the HCA-specific configuration.  Target creation will revert to
380 * the default.
381 *
382 * Parameters:
383 *    hca_guid	HCA GUID.  See description of srpt_NormalizeGuid
384 *
385 * Return Values:
386 *    0		Success
387 *    ENOMEM	Could not allocate resources
388 *    EINVAL	Invalid parameter
389 */
390int
391srpt_ResetTarget(char *hca_guid)
392{
393	int		ret;
394	nvlist_t	*cfgnv;
395	nvlist_t	*hcalist;
396	uint64_t	token;
397	char		guid[32];
398
399	if (hca_guid == NULL) {
400		return (EINVAL);
401	}
402
403	ret = srpt_NormalizeGuid(hca_guid, guid, sizeof (guid), NULL);
404	if (ret != 0) {
405		return (ret);
406	}
407
408	ret = srpt_GetConfig(&cfgnv, &token);
409	if (ret != 0) {
410		return (ret);
411	}
412
413	/* get the list of HCAs */
414	ret = nvlist_lookup_nvlist(cfgnv, SRPT_PROP_HCALIST, &hcalist);
415	if (ret != 0) {
416		nvlist_free(cfgnv);
417		return (ret);
418	}
419
420	/* don't set config if we don't actually change anything */
421	if (nvlist_exists(hcalist, guid)) {
422		(void) nvlist_remove_all(hcalist, guid);
423
424		if (ret == 0) {
425			ret = srpt_SetConfig(cfgnv, token);
426		}
427	}
428
429	nvlist_free(cfgnv);
430
431	return (ret);
432}
433
434/*
435 * srpt_NormalizeGuid()
436 *
437 * Parameters:
438 *    in	HCA GUID.  Must be in one of the following forms:
439 *		    3BA000100CD18	- base hex form
440 *		    0003BA000100CD18	- base hex form with leading zeroes
441 *		    hca:3BA000100CD18	- form from cfgadm and/or /dev/cfg
442 *		    eui.0003BA000100CD18 - EUI form
443 *
444 *    buf	Buffer to hold normalized guid string.  Must be at least
445 *		17 chars long.
446 *    buflen	Length of provided buffer
447 *    int_guid	Optional.  If not NULL, the integer form of the GUID will also
448 *		be returned.
449 * Return Values:
450 *    0		Success
451 *    EINVAL	Invalid HCA GUID or invalid parameter.
452 */
453int
454srpt_NormalizeGuid(char *in, char *buf, size_t buflen, uint64_t *int_guid)
455{
456	uint64_t	guid;
457	char		*bufp = in;
458	char		*end = NULL;
459
460	if ((in == NULL) || (buf == NULL)) {
461		return (EINVAL);
462	}
463
464	if (strncasecmp(bufp, "eui.", 4) == 0) {
465		/* EUI form */
466		bufp += 4;
467	} else if (strncasecmp(bufp, "hca:", 4) == 0) {
468		/* cfgadm and /dev/hca form */
469		bufp += 4;
470	}
471
472	/*
473	 * strtoull() does not return EINVAL as documented.  Lucky
474	 * for us, neither 0 nor ULLONG_MAX will be valid.  Trap on
475	 * those and fail.
476	 */
477	guid = strtoull(bufp, &end, 16);
478	if ((guid == 0) || (guid == ULLONG_MAX) ||
479	    ((end != NULL) && (strlen(end) > 0))) {
480		return (EINVAL);
481	}
482
483#if 0
484	(void) snprintf(buf, buflen, "%llX", guid);
485#endif
486	SRPT_FORMAT_HCAKEY(buf, buflen, guid);
487
488	if (int_guid) {
489		*int_guid = guid;
490	}
491
492	return (0);
493}
494