xref: /illumos-gate/usr/src/lib/libv12n/sparc/libv12n.c (revision 598f4ceed9327d2d6c2325dd67cae3aa06f7fea6)
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 <dlfcn.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include <synch.h>
32 #include <thread.h>
33 #include <unistd.h>
34 #include <utility.h>
35 #include <sys/mdesc.h>
36 #include <sys/mdesc_impl.h>
37 #include <sys/debug.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/utsname.h>
41 
42 #include "ldma.h"
43 #include "libds.h"
44 #include "libv12n.h"
45 
46 /*
47  * sun4 support for libv12n.
48  *
49  * Non-sun4v support is minimal.  The v12n_capabilities() function will
50  * only return 0 (not supported, not enabled, no implementation).
51  *
52  * For sun4v the support for v12n_capabilities(), v12n_domain_roles(),
53  * v12n_domain_name() and v12n_domain_uuid() are supported by scanning the
54  * MD from /dev/mdesc for specific properties.  For v12n_ctrl_domain() and
55  * v12n_chassis_serialno(), the ldoms agent daemon (ldmad) on the control
56  * domain supplies the required information via the "agent-system" domain
57  * service.
58  */
59 
60 /* libds statics */
61 static void *v12n_ds_dlhdl = NULL;
62 static int (*v12n_ds_send_msg)(ds_hdl_t, void *, size_t) = NULL;
63 static int (*v12n_ds_clnt_reg)(ds_capability_t *, ds_ops_t *);
64 static int (*v12n_ds_unreg_svc)(char *, boolean_t);
65 
66 /*
67  * Defines to support the 'agent-system' domain service.
68  */
69 
70 #define	LDMA_SYSTEM_NVERS		\
71 			(sizeof (v12n_ldma_system_vers) / sizeof (ds_ver_t))
72 static ds_ver_t v12n_ldma_system_vers[] = { { 1, 0} };
73 
74 static ds_capability_t v12n_ldma_cap = {
75 	LDMA_NAME_SYSTEM,	/* svc_id */
76 	v12n_ldma_system_vers,	/* vers */
77 	LDMA_SYSTEM_NVERS	/* nvers */
78 };
79 
80 static void v12n_ldma_register_handler(ds_hdl_t hdl, ds_cb_arg_t arg,
81     ds_ver_t *ver, ds_domain_hdl_t dhdl);
82 static void v12n_ldma_data_handler(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf,
83     size_t buflen);
84 
85 static ds_ops_t v12n_ldma_ops = {
86 	v12n_ldma_register_handler,	/* ds_reg_cb */
87 	NULL,				/* ds_unreg_cb */
88 	v12n_ldma_data_handler,		/* ds_data_cb */
89 	NULL				/* ds_cb_arg */
90 };
91 
92 /* v12n_ldma_cv_state values */
93 #define	V12N_LDMA_CVINVALID	-1	/* invalid value for cv_state */
94 #define	V12N_LDMA_REGWAITING	0	/* waiting for ctrl domain reg */
95 #define	V12N_LDMA_REGRECEIVED	1	/* received ctrl domain reg */
96 #define	V12N_LDMA_MSGWAITING	2	/* waiting for message response */
97 #define	V12N_LDMA_MSGRECEIVED	3	/* received message response */
98 #define	V12N_LDMA_MSGERROR	4	/* received a bad message */
99 
100 /* 'agent-system' data used in async registration/data message handlers */
101 static ds_hdl_t v12n_ldma_ctrl_hdl = DS_INVALID_HDL;
102 static int v12n_ldma_msgtype;
103 static char *v12n_ldma_msgstr;
104 static mutex_t v12n_ldma_lock = DEFAULTMUTEX;
105 static cond_t v12n_ldma_cv = DEFAULTCV;
106 static int v12n_ldma_cv_state = V12N_LDMA_CVINVALID;
107 static mutex_t v12n_ldma_cv_lock = DEFAULTMUTEX;
108 
109 /* 'agent-system' timeout values in seconds */
110 static int v12n_ldma_timeout = 15;
111 static int v12n_ldma_sleeptime = 1;
112 
113 
114 #define	V12N_LDOMS_SUPPORTED	(V12N_CAP_SUPPORTED | V12N_CAP_ENABLED | \
115 				    V12N_CAP_IMPL_LDOMS)
116 
117 #define	MD_DEVICE		"/dev/mdesc"
118 
119 /*
120  * libv12n routines to support /dev/mdesc.
121  */
122 
123 /*
124  * Wrapper for MD free: need unused size argument.
125  */
126 /* ARGSUSED */
127 static void
128 v12n_md_free(void *buf, size_t n)
129 {
130 	free(buf);
131 }
132 
133 /*
134  * Wrapper for MD init: read MD and invoke md_init_intern.
135  */
136 static md_t *
137 v12n_md_init()
138 {
139 	md_t *mdp;
140 	char *buf = NULL;
141 	md_header_t mdh;
142 	int md_size;
143 	int fd;
144 
145 	/*
146 	 * Open the Machine Description (MD)
147 	 */
148 	fd = open(MD_DEVICE, O_RDONLY);
149 	if (fd == -1) {
150 		return (NULL);
151 	}
152 
153 	if (read(fd, &mdh, sizeof (md_header_t)) != sizeof (md_header_t))
154 		goto errdone;
155 
156 	md_size = sizeof (md_header_t) + mdh.node_blk_sz + mdh.name_blk_sz +
157 	    mdh.data_blk_sz;
158 
159 	if ((buf = malloc(md_size)) == NULL)
160 		goto errdone;
161 
162 	(void) memcpy(buf, &mdh, sizeof (md_header_t));
163 	if (read(fd, buf + sizeof (md_header_t),
164 	    md_size - sizeof (md_header_t)) != md_size - sizeof (md_header_t)) {
165 		goto errdone;
166 	}
167 
168 	mdp = md_init_intern((uint64_t *)((void *)buf), malloc, v12n_md_free);
169 
170 	(void) close(fd);
171 
172 	return (mdp);
173 
174 errdone:
175 	(void) close(fd);
176 	free(buf);
177 
178 	return (NULL);
179 }
180 
181 /*
182  * Wrapper for md_fini.  Allow NULL md ptr and free MD buffer.
183  */
184 static void
185 v12n_md_fini(void *md)
186 {
187 	md_impl_t *mdp = (md_impl_t *)md;
188 
189 	if (mdp) {
190 		free(mdp->caddr);
191 		(void) md_fini(md);
192 	}
193 }
194 
195 /*
196  * See if LDoms domaining is enabled, returns 1 if enabled.
197  * Get the value of the 'domaining-enabled' property under the
198  * 'platform' node.  Value of 1 => domaining is enabled.
199  */
200 static int
201 v12n_domaining_enabled()
202 {
203 	mde_cookie_t *nodes, rootnode;
204 	int nnodes;
205 	uint64_t prop_val = 0;
206 	md_t *mdp;
207 
208 	if ((mdp = v12n_md_init()) == NULL) {
209 		return (0);
210 	}
211 
212 	nnodes = md_node_count(mdp);
213 	nodes = malloc(nnodes * sizeof (mde_cookie_t));
214 	if (nodes == NULL) {
215 		v12n_md_fini(mdp);
216 		return (0);
217 	}
218 
219 	rootnode = md_root_node(mdp);
220 
221 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "platform"),
222 	    md_find_name(mdp, "fwd"), nodes);
223 
224 	if (nnodes >= 1) {
225 		(void) md_get_prop_val(mdp, nodes[0], "domaining-enabled",
226 		    &prop_val);
227 	}
228 
229 	v12n_md_fini(mdp);
230 	free(nodes);
231 	return (prop_val == 1);
232 }
233 
234 int
235 v12n_capabilities()
236 {
237 	struct utsname uinfo;
238 	struct stat st;
239 	int cap;
240 
241 	/*
242 	 * Check if this is an LDoms system. When using LDoms each
243 	 * domain should have a /dev/mdesc device providing access to
244 	 * the Machine Description (MD) of the domain. If this device
245 	 * does not exist then this is not an LDoms system.
246 	 */
247 	if (uname(&uinfo) == -1 || strcmp(uinfo.machine, "sun4v")) {
248 		/*
249 		 * Not sun4v -> LDoms not supported
250 		 */
251 		cap = 0;
252 	} else if (stat(MD_DEVICE, &st) == 0) {
253 		/*
254 		 * sun4v + /dev/mdesc exists -> Check if LDoms enabled
255 		 * via the 'domaining-enabled' property.
256 		 */
257 		cap = (V12N_CAP_SUPPORTED | V12N_CAP_IMPL_LDOMS |
258 		    (v12n_domaining_enabled() ? V12N_CAP_ENABLED : 0));
259 	} else if (errno == ENOENT) {
260 		/*
261 		 * sun4v + /dev/mdesc does not exist -> LDoms supported
262 		 * but not enabled.
263 		 */
264 		cap = (V12N_CAP_SUPPORTED | V12N_CAP_IMPL_LDOMS);
265 	}
266 
267 	return (cap);
268 }
269 
270 /*
271  * Routines to support v12n_domain_roles.
272  */
273 static int
274 v12n_scan_md_nodes(md_t *mdp, char *node_name, char *node_str_prop,
275     char **props)
276 {
277 	mde_cookie_t *nodes, rootnode;
278 	int nnodes, i, j;
279 	char *prop_str;
280 
281 	nnodes = md_node_count(mdp);
282 	nodes = malloc(nnodes * sizeof (mde_cookie_t));
283 	if (nodes == NULL) {
284 		return (0);
285 	}
286 
287 	rootnode = md_root_node(mdp);
288 
289 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, node_name),
290 	    md_find_name(mdp, "fwd"), nodes);
291 
292 	if (node_str_prop == NULL)
293 		return (nnodes > 0);
294 
295 	for (i = 0; i < nnodes; i++) {
296 		if (md_get_prop_str(mdp, nodes[i], node_str_prop, &prop_str))
297 			continue;
298 		for (j = 0; props[j] != NULL; j++) {
299 			if (strcmp(prop_str, props[j]) == 0) {
300 				free(nodes);
301 				return (1);
302 			}
303 		}
304 	}
305 	free(nodes);
306 	return (0);
307 }
308 
309 /*
310  * Check if MD has a hypervisor access point, returns 1 if true.
311  * Check the MD for a 'virtual-device-port' node whose 'vldc-svc-name' is
312  * 'hvctl'.
313  */
314 static int
315 v12n_check_hv_access(md_t *mdp)
316 {
317 	static char *hvctl_str[] = {
318 		"hvctl",
319 		NULL
320 	};
321 
322 	return (v12n_scan_md_nodes(mdp, "virtual-device-port", "vldc-svc-name",
323 	    hvctl_str));
324 }
325 
326 /*
327  * Check if MD has a virtual device service (vcc, vsw, vds), returns 1 if true.
328  * Need to check all the MD 'virtual-device' nodes for a 'device-type' property
329  * of 'vcc', 'vsw' or 'vds'.
330  */
331 static int
332 v12n_check_virtual_service(md_t *mdp)
333 {
334 	static char *vdevs[] = {
335 		"vcc",
336 		"vsw",
337 		"vds",
338 		NULL
339 	};
340 
341 	return (v12n_scan_md_nodes(mdp, "virtual-device", "device-type",
342 	    vdevs));
343 }
344 
345 /*
346  * Check if MD has an physical I/O device node, returns 1 if true.
347  */
348 static int
349 v12n_check_io_service(md_t *mdp)
350 {
351 	return (v12n_scan_md_nodes(mdp, "iodevice", NULL, NULL));
352 }
353 
354 /*
355  * Check if a MD node is root PCI device, returns 1 if true.
356  * Need to check all the MD 'iodevice' nodes for a 'device-type' property
357  * of 'pciex'.
358  */
359 static int
360 v12n_check_root(md_t *mdp)
361 {
362 	static char *pciex[] = {
363 		"pciex",
364 		NULL
365 	};
366 
367 	return (v12n_scan_md_nodes(mdp, "iodevice", "device-type", pciex));
368 }
369 
370 /*
371  * Get the domain roles for the domain.
372  */
373 int
374 v12n_domain_roles()
375 {
376 	md_t *mdp;
377 	int roles = 0;
378 
379 	if (v12n_capabilities() != V12N_LDOMS_SUPPORTED) {
380 		errno = ENOTSUP;
381 		return (-1);
382 	}
383 
384 	if ((mdp = v12n_md_init()) == NULL) {
385 		errno = EACCES;
386 		return (-1);
387 	}
388 
389 	if (v12n_check_hv_access(mdp))
390 		roles |= V12N_ROLE_CONTROL;
391 
392 	if (v12n_check_virtual_service(mdp))
393 		roles |= V12N_ROLE_SERVICE;
394 
395 	if (v12n_check_io_service(mdp))
396 		roles |= V12N_ROLE_IO;
397 
398 	if (v12n_check_root(mdp))
399 		roles |= V12N_ROLE_ROOT;
400 
401 	v12n_md_fini(mdp);
402 
403 	return (roles);
404 }
405 
406 /*
407  * Get domain name from MD's virtual domain service node, returns 1 on success.
408  * The domain name is a string property 'vlds-domain-name' under the
409  * 'virtual-device' device node whose name is 'virtual-domain-service'.
410  */
411 static int
412 v12n_get_md_domain_name(md_t *mdp, char **vds_dnamep)
413 {
414 	mde_cookie_t *vdev_nodes, rootnode;
415 	int list_size, nvdevs, num_nodes, i, rv;
416 	char *vldc_name;
417 
418 	num_nodes = md_node_count(mdp);
419 	list_size = num_nodes * sizeof (mde_cookie_t);
420 	vdev_nodes = malloc(list_size);
421 	if (vdev_nodes == NULL) {
422 		return (0);
423 	}
424 
425 	rootnode = md_root_node(mdp);
426 
427 	nvdevs = md_scan_dag(mdp, rootnode, md_find_name(mdp, "virtual-device"),
428 	    md_find_name(mdp, "fwd"), vdev_nodes);
429 
430 	rv = 0;
431 	for (i = 0; i < nvdevs; i++) {
432 		if (md_get_prop_str(mdp, vdev_nodes[i], "name", &vldc_name))
433 			continue;
434 		if (strcmp(vldc_name, "virtual-domain-service") == 0) {
435 			rv = (md_get_prop_str(mdp, vdev_nodes[i],
436 			    "vlds-domain-name", vds_dnamep) == 0);
437 			break;
438 		}
439 	}
440 	free(vdev_nodes);
441 	return (rv);
442 }
443 
444 /*
445  * String copyout utility.
446  */
447 static size_t
448 v12n_string_copyout(char *sout, char *sfrom, size_t count)
449 {
450 	size_t ret = strlen(sfrom) + 1;
451 
452 	if (sout != NULL && count > 0) {
453 		count = MIN(ret, count);
454 		(void) memcpy(sout, sfrom, count);
455 	}
456 	return (ret);
457 }
458 
459 /*
460  * Get the domain name of this domain.
461  */
462 size_t
463 v12n_domain_name(char *buf, size_t count)
464 {
465 	md_t *mdp = NULL;
466 	char *ldmname;
467 	int rv = -1;
468 
469 	if (v12n_capabilities() != V12N_LDOMS_SUPPORTED) {
470 		errno = ENOTSUP;
471 	} else if ((mdp = v12n_md_init()) == NULL) {
472 		errno = EACCES;
473 	} else if (!v12n_get_md_domain_name(mdp, &ldmname)) {
474 		errno = ESRCH;
475 	} else {
476 		rv = v12n_string_copyout(buf, ldmname, count);
477 	}
478 
479 	v12n_md_fini(mdp);
480 	return (rv);
481 }
482 
483 /*
484  * Get UUID string from MD, returns 1 on success.
485  * The UUID is a string property 'uuid' under the 'platform' node of the MD.
486  */
487 static int
488 v12n_get_md_uuid_str(md_t *mdp, char **uuid_strp)
489 {
490 	mde_cookie_t *plat_nodes, rootnode;
491 	int list_size, npnodes, num_nodes, rv;
492 
493 	num_nodes = md_node_count(mdp);
494 	list_size = num_nodes * sizeof (mde_cookie_t);
495 	plat_nodes = malloc(list_size);
496 	if (plat_nodes == NULL) {
497 		return (0);
498 	}
499 
500 	rootnode = md_root_node(mdp);
501 
502 	npnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "platform"),
503 	    md_find_name(mdp, "fwd"), plat_nodes);
504 
505 	if (npnodes >= 1)
506 		rv = !md_get_prop_str(mdp, plat_nodes[0], "uuid", uuid_strp);
507 	else
508 		rv = 0;
509 
510 	free(plat_nodes);
511 	return (rv);
512 }
513 
514 /*
515  * Get the domain UUID.
516  */
517 int
518 v12n_domain_uuid(uuid_t uuid)
519 {
520 	md_t *mdp = NULL;
521 	char *uuid_str;
522 	int rv = -1;
523 
524 	if (v12n_capabilities() != V12N_LDOMS_SUPPORTED) {
525 		errno = ENOTSUP;
526 	} else if ((mdp = v12n_md_init()) == NULL) {
527 		errno = EACCES;
528 	} else if (!v12n_get_md_uuid_str(mdp, &uuid_str)) {
529 		errno = ESRCH;
530 	} else {
531 		rv = uuid_parse(uuid_str, uuid);
532 	}
533 
534 	v12n_md_fini(mdp);
535 
536 	return (rv);
537 }
538 
539 /*
540  * Send 'agent-sytem' request message.
541  */
542 static int
543 v12n_ldma_send_request()
544 {
545 	ldma_message_header_t ldmamsg;
546 
547 	if (v12n_ds_send_msg == NULL || v12n_ldma_ctrl_hdl == DS_INVALID_HDL)
548 		return (ENOENT);
549 
550 	ldmamsg.msg_num = 0;
551 	ldmamsg.msg_type = v12n_ldma_msgtype;
552 	ldmamsg.msg_info = 0;
553 	return (v12n_ds_send_msg(v12n_ldma_ctrl_hdl, (char *)&ldmamsg,
554 	    sizeof (ldmamsg)));
555 }
556 
557 /*
558  * 'agent-system' registration handler.
559  * If we get a registration from the control domain (domain 0), then send
560  * the requested message.  Otherwise, ignore the registration.
561  */
562 /* ARGSUSED */
563 static void
564 v12n_ldma_register_handler(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
565     ds_domain_hdl_t dhdl)
566 {
567 
568 	/* got registration from control domain */
569 	if (dhdl == 0) {
570 		(void) mutex_lock(&v12n_ldma_cv_lock);
571 		if (v12n_ldma_cv_state == V12N_LDMA_REGWAITING) {
572 			v12n_ldma_ctrl_hdl = hdl;
573 			v12n_ldma_cv_state = V12N_LDMA_REGRECEIVED;
574 			(void) cond_signal(&v12n_ldma_cv);
575 		}
576 		(void) mutex_unlock(&v12n_ldma_cv_lock);
577 	}
578 }
579 
580 /*
581  * 'agent-system' data handler.
582  */
583 /* ARGSUSED */
584 static void
585 v12n_ldma_data_handler(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf,
586     size_t buflen)
587 {
588 	char *data;
589 	ldma_message_header_t *ldmp;
590 	int n;
591 	int cv_state = V12N_LDMA_MSGERROR;
592 
593 	/*
594 	 * Ignore any message not from the control domain.
595 	 */
596 	if (v12n_ldma_ctrl_hdl != hdl)
597 		return;
598 
599 	/*
600 	 * Ignore any unexpected message.
601 	 */
602 	if (buflen < LDMA_MESSAGE_HEADER_SIZE)
603 		return;
604 
605 	/*
606 	 * Ignore message with unexpected msgnum.
607 	 */
608 	ldmp = (ldma_message_header_t *)buf;
609 	if (ldmp->msg_num != 0)
610 		return;
611 
612 	switch (ldmp->msg_type) {
613 
614 	case LDMA_MSG_RESULT:
615 		if (ldmp->msg_info == 0 ||
616 		    ldmp->msg_info > LDMA_MESSAGE_DLEN(buflen)) {
617 			cv_state = V12N_LDMA_MSGERROR;
618 			break;
619 		}
620 		data = LDMA_HDR2DATA(buf);
621 
622 		/* ensure that data ends with a '\0' */
623 		data[ldmp->msg_info - 1] = '\0';
624 		switch (v12n_ldma_msgtype) {
625 
626 		case LDMA_MSGSYS_GET_SYSINFO:
627 			/*
628 			 * Control domain nodename is second string in the
629 			 * message.  Make sure there is enough data in the msg
630 			 * to have a second string.
631 			 */
632 			n = strlen(data);
633 			if (LDMA_MESSAGE_DLEN(buflen) <= n + 3) {
634 				cv_state = V12N_LDMA_MSGERROR;
635 				break;
636 			}
637 			data += n + 1;
638 			if ((v12n_ldma_msgstr = strdup(data)) == NULL)
639 				cv_state = V12N_LDMA_MSGERROR;
640 			else
641 				cv_state = V12N_LDMA_MSGRECEIVED;
642 			break;
643 
644 		case LDMA_MSGSYS_GET_CHASSISNO:
645 			if ((v12n_ldma_msgstr = strdup(data)) == NULL)
646 				cv_state = V12N_LDMA_MSGERROR;
647 			else
648 				cv_state = V12N_LDMA_MSGRECEIVED;
649 			break;
650 
651 		default:
652 			/* v12n_ldma_msgtype must be valid */
653 			ASSERT(0);
654 		}
655 		break;
656 
657 	case LDMA_MSG_ERROR:
658 		cv_state = V12N_LDMA_MSGERROR;
659 		break;
660 
661 	default:
662 		/* unexpected message, ignored */
663 		return;
664 	}
665 
666 	(void) mutex_lock(&v12n_ldma_cv_lock);
667 	v12n_ldma_cv_state = cv_state;
668 	(void) cond_signal(&v12n_ldma_cv);
669 	(void) mutex_unlock(&v12n_ldma_cv_lock);
670 }
671 
672 
673 /*
674  * libds doesn't exist on non-sun4v, dynamically load it and get the
675  * function pointers to the needed lib functions.
676  */
677 static int
678 v12n_libds_init(void)
679 {
680 	if (v12n_ds_dlhdl != NULL) {
681 		if (v12n_ds_clnt_reg == NULL || v12n_ds_send_msg == NULL ||
682 		    v12n_ds_unreg_svc == NULL)
683 			return (ENOENT);
684 		return (0);
685 	}
686 
687 	if ((v12n_ds_dlhdl = dlopen("libds.so.1",
688 	    RTLD_NOW | RTLD_GLOBAL)) == NULL)
689 		return (ENOENT);
690 
691 	if ((v12n_ds_clnt_reg = (int (*)(ds_capability_t *, ds_ops_t *))
692 	    dlsym(v12n_ds_dlhdl, "ds_clnt_reg")) == NULL)
693 		return (ENOENT);
694 
695 	if ((v12n_ds_send_msg = (int (*)(ds_hdl_t, void *, size_t))
696 	    dlsym(v12n_ds_dlhdl, "ds_send_msg")) == NULL)
697 		return (ENOENT);
698 
699 	if ((v12n_ds_unreg_svc = (int (*)(char *, boolean_t))
700 	    dlsym(v12n_ds_dlhdl, "ds_unreg_svc")) == NULL)
701 		return (ENOENT);
702 
703 	return (0);
704 }
705 
706 /*
707  * Initiate and wait for an ldmad 'agent-system' domain service.
708  * Dynamically load libds, register the client 'agent-system' service
709  * and wait for a specified amount of time for the 'agent-system'
710  * service on the control domain to respond to the request.
711  */
712 static int
713 v12n_get_ldma_system_msg(int msgtype, char **strp)
714 {
715 	int tout;
716 	int err = 0;
717 	timestruc_t timeout;
718 
719 	/*
720 	 * Ensure that there's only one thread trying to do a
721 	 * 'agent-system' client registration/message at a time.
722 	 */
723 	(void) mutex_lock(&v12n_ldma_lock);
724 	if ((err = v12n_libds_init()) != 0) {
725 		(void) mutex_unlock(&v12n_ldma_lock);
726 		return (err);
727 	}
728 
729 	v12n_ldma_msgtype = msgtype;
730 	v12n_ldma_msgstr = NULL;
731 
732 	/* initialize v12n_ldma_cv_state variable before registering service */
733 	(void) mutex_lock(&v12n_ldma_cv_lock);
734 	v12n_ldma_cv_state = V12N_LDMA_REGWAITING;
735 	(void) mutex_unlock(&v12n_ldma_cv_lock);
736 
737 	/*
738 	 * Other instances may be trying to load the "agent-system" service.
739 	 * If a collision happens (EBUSY error), wait and try again.
740 	 */
741 	for (tout = 0; tout < v12n_ldma_timeout; tout += v12n_ldma_sleeptime) {
742 		if ((err = v12n_ds_clnt_reg(&v12n_ldma_cap,
743 		    &v12n_ldma_ops)) == 0)
744 			break;
745 		if (err != EALREADY) {
746 			goto done;
747 		}
748 		(void) sleep(v12n_ldma_sleeptime);
749 	}
750 
751 	if (tout >= v12n_ldma_timeout) {
752 		err = EBUSY;
753 		goto done;
754 	}
755 
756 	/*
757 	 * Wait for control domain registration.
758 	 */
759 	timeout.tv_sec = v12n_ldma_timeout;
760 	timeout.tv_nsec = 0;
761 
762 	(void) mutex_lock(&v12n_ldma_cv_lock);
763 	while (v12n_ldma_cv_state == V12N_LDMA_REGWAITING) {
764 		if ((err = cond_reltimedwait(&v12n_ldma_cv,
765 		    &v12n_ldma_cv_lock, &timeout)) != EINTR)
766 			break;
767 	}
768 
769 	/*
770 	 * Check for timeout or an error.
771 	 */
772 	if (v12n_ldma_cv_state != V12N_LDMA_REGRECEIVED) {
773 		if (err == 0)
774 			err = EPROTO;
775 		(void) mutex_unlock(&v12n_ldma_cv_lock);
776 		goto done;
777 	}
778 
779 	/*
780 	 * Received a registration request, send the request message.
781 	 */
782 	v12n_ldma_cv_state = V12N_LDMA_MSGWAITING;
783 	if ((err = v12n_ldma_send_request()) != 0) {
784 		(void) mutex_unlock(&v12n_ldma_cv_lock);
785 		goto done;
786 	}
787 
788 	while (v12n_ldma_cv_state == V12N_LDMA_MSGWAITING) {
789 		if ((err = cond_reltimedwait(&v12n_ldma_cv,
790 		    &v12n_ldma_cv_lock, &timeout)) != EINTR)
791 			break;
792 	}
793 
794 	if (v12n_ldma_cv_state != V12N_LDMA_MSGRECEIVED) {
795 		if (err == 0)
796 			err = EPROTO;
797 		(void) mutex_unlock(&v12n_ldma_cv_lock);
798 		goto done;
799 	}
800 
801 	v12n_ldma_cv_state = V12N_LDMA_CVINVALID;
802 	(void) mutex_unlock(&v12n_ldma_cv_lock);
803 
804 	/*
805 	 * If v12n_ldma_msgstr is set, a valid data response was seen.
806 	 */
807 	if (v12n_ldma_msgstr == NULL)
808 		err = ENODATA;
809 	else {
810 		if (*v12n_ldma_msgstr == '\0' ||
811 		    (*strp = strdup(v12n_ldma_msgstr)) == NULL)
812 			err = ENODATA;
813 		free(v12n_ldma_msgstr);
814 		v12n_ldma_msgstr = NULL;
815 	}
816 
817 done:
818 	v12n_ds_unreg_svc(LDMA_NAME_SYSTEM, B_TRUE);
819 	v12n_ldma_msgtype = -1;
820 	v12n_ldma_ctrl_hdl = DS_INVALID_HDL;
821 	(void) mutex_unlock(&v12n_ldma_lock);
822 
823 	return (err);
824 }
825 
826 /*
827  * Get the nodename of the control domain.  Returns the equivalent
828  * of 'uname -n' on the control domain.
829  *   This is obtained via the 'agent-system' domain service provided
830  *   by ldmad.
831  */
832 size_t
833 v12n_ctrl_domain(char *buf, size_t count)
834 {
835 	char *str;
836 	int err;
837 	size_t rv = (size_t)(-1);
838 
839 	if (v12n_capabilities() != V12N_LDOMS_SUPPORTED) {
840 		errno = ENOTSUP;
841 	} else if ((err = v12n_get_ldma_system_msg(LDMA_MSGSYS_GET_SYSINFO,
842 	    &str)) != 0) {
843 		errno = err;
844 	} else {
845 		rv = v12n_string_copyout(buf, str, count);
846 	}
847 	return (rv);
848 }
849 
850 /*
851  * Get the Chassis serial number from the Control Domain.
852  *   This is obtained via the 'agent-system' domain service provided
853  *   by ldmad.
854  */
855 size_t
856 v12n_chassis_serialno(char *buf, size_t count)
857 {
858 	char *str;
859 	int err;
860 	size_t rv = (size_t)(-1);
861 
862 	if (v12n_capabilities() != V12N_LDOMS_SUPPORTED) {
863 		errno = ENOTSUP;
864 	} else if ((err = v12n_get_ldma_system_msg(LDMA_MSGSYS_GET_CHASSISNO,
865 	    &str)) != 0) {
866 		errno = err;
867 	} else {
868 		rv = v12n_string_copyout(buf, str, count);
869 	}
870 	return (rv);
871 }
872