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