xref: /titanic_52/usr/src/lib/fm/libldom/sparc/ldom.c (revision 1bf5e2ae883107708d4e1651562a348519be0e6a)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <pthread.h>
32 #include <errno.h>
33 #include <libnvpair.h>
34 #include <dlfcn.h>
35 #include <link.h>
36 #include <assert.h>
37 
38 #include <sys/processor.h>
39 #include <sys/stat.h>
40 #include <sys/mdesc.h>
41 #include <sys/param.h>
42 #include <sys/systeminfo.h>
43 #include <sys/mem.h>
44 #include <sys/bl.h>
45 #include <sys/fm/protocol.h>
46 #include <fm/fmd_fmri.h>
47 #include <fm/fmd_agent.h>
48 #include <sys/pri.h>
49 
50 #include "ldom.h"
51 #include "ldmsvcs_utils.h"
52 
53 #define	MD_STR_PLATFORM		"platform"
54 #define	MD_STR_DOM_CAPABLE	"domaining-enabled"
55 
56 static int ldom_ldmd_is_up = 0; /* assume stays up if ever seen up */
57 
58 static void *ldom_dl_hp = (void *)NULL;
59 static const char *ldom_dl_path = "libpri.so.1";
60 static int ldom_dl_mode = (RTLD_NOW | RTLD_LOCAL);
61 
62 static pthread_mutex_t ldom_pri_lock = PTHREAD_MUTEX_INITIALIZER;
63 static int ldom_pri_ref_cnt = 0; /* num of outstanding ldom_pri_init()s */
64 static int ldom_pri_init_done = 0; /* bool for real pri_init() done */
65 static int (*ldom_pri_fp_init)(void) = (int (*)(void))NULL;
66 static void (*ldom_pri_fp_fini)(void) = (void (*)(void))NULL;
67 static ssize_t (*ldom_pri_fp_get)(uint8_t wait, uint64_t *token, uint64_t **buf,
68 	void *(*allocp)(size_t), void (*freep)(void *, size_t)) =
69 	(ssize_t (*)(uint8_t wait, uint64_t *token, uint64_t **buf,
70 	void *(*allocp)(size_t), void (*freep)(void *, size_t)))NULL;
71 
72 static void
73 ldom_pri_config(void)
74 {
75 	char isa[MAXNAMELEN];	/* used to see if machine is sun4v */
76 
77 	if (sysinfo(SI_MACHINE, isa, MAXNAMELEN) < 0)
78 		return;
79 	if (strcmp(isa, "sun4v") != 0)
80 		return;
81 	if ((ldom_dl_hp = dlopen(ldom_dl_path, ldom_dl_mode)) == NULL)
82 		return;
83 
84 	ldom_pri_fp_init = (int (*)(void))dlsym(ldom_dl_hp, "pri_init");
85 	ldom_pri_fp_fini = (void (*)(void))dlsym(ldom_dl_hp, "pri_fini");
86 	ldom_pri_fp_get = (ssize_t (*)(uint8_t wait, uint64_t *token,
87 	    uint64_t **buf, void *(*allocp)(size_t),
88 	    void (*freep)(void *, size_t)))dlsym(ldom_dl_hp, "pri_get");
89 }
90 
91 static void
92 ldom_pri_unconfig(void)
93 {
94 	if (ldom_dl_hp == NULL)
95 		return;
96 
97 	ldom_pri_fp_init = (int (*)(void))NULL;
98 	ldom_pri_fp_fini = (void (*)(void))NULL;
99 	ldom_pri_fp_get = (ssize_t (*)(uint8_t wait, uint64_t *token,
100 	    uint64_t **buf, void *(*allocp)(size_t),
101 	    void (*freep)(void *, size_t)))NULL;
102 	(void) dlclose(ldom_dl_hp);
103 	ldom_dl_hp = (void *)NULL;
104 }
105 
106 /*
107  * ldom_pri_lock is assumed already held by anyone accessing ldom_pri_ref_cnt
108  */
109 
110 static int
111 ldom_pri_init(void)
112 {
113 	if (ldom_pri_ref_cnt == 0) {
114 		ldom_pri_config();
115 		/*
116 		 * ldom_pri_init() is called before we know whether we
117 		 * have LDOMS FW or not; defer calling pri_init() via
118 		 * ldom_pri_fp_init until the first time we try to
119 		 * actually get a PRI
120 		 */
121 	}
122 	ldom_pri_ref_cnt++;
123 
124 	assert(ldom_pri_ref_cnt > 0);
125 
126 	return (0);
127 }
128 
129 static void
130 ldom_pri_fini(void)
131 {
132 	assert(ldom_pri_ref_cnt > 0);
133 
134 	ldom_pri_ref_cnt--;
135 	if (ldom_pri_ref_cnt == 0) {
136 		if (ldom_pri_init_done && (ldom_pri_fp_fini != NULL)) {
137 			(*ldom_pri_fp_fini)();
138 			ldom_pri_init_done = 0;
139 		}
140 		ldom_pri_unconfig();
141 	}
142 }
143 
144 static ssize_t
145 ldom_pri_get(uint8_t wait, uint64_t *token, uint64_t **buf,
146 		void *(*allocp)(size_t), void (*freep)(void *, size_t))
147 {
148 	assert(ldom_pri_ref_cnt > 0);
149 
150 	if ((!ldom_pri_init_done) && (ldom_pri_fp_init != NULL)) {
151 		if ((*ldom_pri_fp_init)() < 0)
152 			return (-1);
153 		ldom_pri_init_done = 1;
154 	}
155 
156 	if (ldom_pri_fp_get != NULL)
157 		return ((*ldom_pri_fp_get)(wait, token, buf, allocp, freep));
158 	else
159 		return (-1);
160 }
161 
162 static ssize_t
163 get_local_core_md(ldom_hdl_t *lhp, uint64_t **buf)
164 {
165 	int fh;
166 	size_t size;
167 	uint64_t *bufp;
168 
169 	if ((fh = open("/devices/pseudo/mdesc@0:mdesc", O_RDONLY, 0)) < 0)
170 		return (-1);
171 
172 	if (ioctl(fh, MDESCIOCGSZ, &size) < 0) {
173 		(void) close(fh);
174 		return (-1);
175 	}
176 
177 	bufp = (uint64_t *)lhp->allocp(size);
178 
179 	if (read(fh, bufp, size) < 0) {
180 		lhp->freep(bufp, size);
181 		(void) close(fh);
182 		return (-1);
183 	}
184 	(void) close(fh);
185 
186 	*buf = bufp;
187 
188 	return ((ssize_t)size);
189 }
190 
191 
192 static int
193 get_local_md_prop_value(ldom_hdl_t *lhp, char *node, char *prop, uint64_t *val)
194 {
195 	int rc = 1;
196 	uint64_t *bufp;
197 	ssize_t bufsiz;
198 
199 	if ((bufsiz = get_local_core_md(lhp, &bufp)) > 0) {
200 		md_t *mdp;
201 
202 		if (mdp = md_init_intern(bufp, lhp->allocp, lhp->freep)) {
203 			int num_nodes;
204 			mde_cookie_t *listp;
205 
206 			num_nodes = md_node_count(mdp);
207 			listp = lhp->allocp(sizeof (mde_cookie_t) * num_nodes);
208 
209 			if (md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE,
210 			    md_find_name(mdp, node),
211 			    md_find_name(mdp, "fwd"), listp) > 0 &&
212 			    md_get_prop_val(mdp, listp[0], prop, val) >= 0) {
213 				/* found the property */
214 				rc = 0;
215 			}
216 
217 			lhp->freep(listp, sizeof (mde_cookie_t) * num_nodes);
218 			(void) md_fini(mdp);
219 		}
220 		lhp->freep(bufp, bufsiz);
221 	}
222 	return (rc);
223 }
224 
225 static int
226 ldom_getinfo(struct ldom_hdl *lhp)
227 {
228 	static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
229 	static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
230 	static int major_version = -1;
231 	static int service_ldom = -1;
232 	static int busy_init = 0;
233 
234 	int ier, rc = 0;
235 	uint64_t domain_capable;
236 
237 	(void) pthread_mutex_lock(&mt);
238 
239 	while (busy_init == 1)
240 		(void) pthread_cond_wait(&cv, &mt);
241 
242 	if (major_version != -1 && service_ldom != -1) {
243 		lhp->major_version = major_version;
244 		lhp->service_ldom = service_ldom;
245 		(void) pthread_mutex_unlock(&mt);
246 		return (0);
247 	}
248 
249 	/*
250 	 * get to this point if major_version and service_ldom have not yet
251 	 * been determined
252 	 */
253 	busy_init = 1;
254 	(void) pthread_mutex_unlock(&mt);
255 
256 	/*
257 	 * set defaults which correspond to the case of "LDOMS not
258 	 * available".	note that these can (and will) also apply to
259 	 * non-sun4v machines.
260 	 */
261 	major_version = 0;
262 	service_ldom = 0;
263 
264 	if (get_local_md_prop_value(lhp, MD_STR_PLATFORM, MD_STR_DOM_CAPABLE,
265 	    &domain_capable) == 0) {
266 
267 		/*
268 		 * LDOMS capable FW is installed; it should be ok to
269 		 * try to communicate with ldmd and if that fails/timesout
270 		 * then use libpri
271 		 */
272 		major_version = 1;
273 
274 		if ((ier = ldmsvcs_check_channel()) == 0) {
275 			/*
276 			 * control ldom
277 			 * ldmfma channel between FMA and ldmd only exists
278 			 * on the control domain.
279 			 */
280 			service_ldom = 1;
281 		} else if (ier == 1) {
282 			/*
283 			 * guest ldom
284 			 * non-control ldom such as guest and io service ldom
285 			 */
286 			service_ldom = 0;
287 		}
288 	}
289 
290 	(void) pthread_mutex_lock(&mt);
291 	lhp->major_version = major_version;
292 	lhp->service_ldom = service_ldom;
293 	busy_init = 0;
294 	(void) pthread_mutex_unlock(&mt);
295 
296 	(void) pthread_cond_broadcast(&cv);
297 
298 	return (rc);
299 }
300 
301 
302 /*
303  * search the machine description for a "pid" entry (physical cpuid) and
304  * return the corresponding "id" entry (virtual cpuid).
305  * return -1 if not found.
306  * if the pid property does not exist in a cpu node, assume pid = id.
307  */
308 static processorid_t
309 cpu_phys2virt(ldom_hdl_t *lhp, uint32_t cpuid)
310 {
311 	char isa[MAXNAMELEN];
312 	md_t *mdp;
313 	mde_cookie_t *listp;
314 	ssize_t bufsize;
315 	processorid_t vid;
316 	uint64_t *bufp;
317 	uint64_t pval, pid, id;
318 	int num_nodes, ncpus, i;
319 
320 	(void) sysinfo(SI_MACHINE, isa, MAXNAMELEN);
321 
322 	if (strcmp(isa, "sun4v") != 0)
323 		return ((processorid_t)cpuid);
324 
325 	/*
326 	 * convert the physical cpuid to a virtual cpuid
327 	 */
328 	if ((bufsize = get_local_core_md(lhp, &bufp)) < 1)
329 		return (-1);
330 
331 	if ((mdp = md_init_intern(bufp, lhp->allocp, lhp->freep)) == NULL ||
332 	    (num_nodes = md_node_count(mdp)) < 1) {
333 		lhp->freep(bufp, bufsize);
334 		return (-1);
335 	}
336 
337 	listp = (mde_cookie_t *)lhp->allocp(sizeof (mde_cookie_t) * num_nodes);
338 	ncpus = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE,
339 	    md_find_name(mdp, "cpu"), md_find_name(mdp, "fwd"), listp);
340 
341 	vid = -1;
342 	for (i = 0; i < ncpus; i++) {
343 		if (md_get_prop_val(mdp, listp[i], "id", &pval) < 0)
344 			pval = (uint64_t)-1;
345 		id = pval;
346 
347 		/* if pid does not exist, assume pid=id */
348 		if (md_get_prop_val(mdp, listp[i], "pid", &pval) < 0)
349 			pval = id;
350 		pid = pval;
351 
352 		if (pid == (uint64_t)cpuid) {
353 			/* Found the entry */
354 			vid = (processorid_t)id;
355 			break;
356 		}
357 	}
358 
359 	lhp->freep(listp, sizeof (mde_cookie_t) * num_nodes);
360 	(void) md_fini(mdp);
361 	lhp->freep(bufp, bufsize);
362 
363 	return (vid);
364 }
365 
366 int
367 ldom_fmri_status(ldom_hdl_t *lhp, nvlist_t *nvl)
368 {
369 	char *name;
370 	int ret = ENOTSUP;
371 
372 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0)
373 		return (EINVAL);
374 
375 	/*
376 	 * ldom_ldmd_is_up can only be true if ldom_major_version()
377 	 * returned 1 earlier; the major version is constant for the
378 	 * life of the client process
379 	 */
380 
381 	if (!ldom_ldmd_is_up) {
382 		/* Zeus is unavail; use local routines for status/retire */
383 
384 		if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) {
385 			processorid_t vid;
386 			uint32_t cpuid;
387 
388 			if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid)
389 			    == 0 && (vid = cpu_phys2virt(lhp, cpuid)) != -1)
390 				return (p_online(vid, P_STATUS));
391 		} else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) {
392 			fmd_agent_hdl_t *hdl;
393 			int err;
394 			if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) {
395 				err = errno;
396 			} else {
397 				err = fmd_agent_page_isretired(hdl, nvl);
398 				if (err == FMD_AGENT_RETIRE_DONE)
399 					err = 0;
400 				else
401 					err = fmd_agent_errno(hdl);
402 				fmd_agent_close(hdl);
403 			}
404 			return (err);
405 		}
406 
407 		return (EINVAL);
408 	} else {
409 		/* Zeus is avail; use Zeus for status/retire */
410 
411 		if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) {
412 			uint32_t cpuid;
413 
414 			if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID,
415 			    &cpuid) == 0)
416 				ret = ldmsvcs_cpu_req_status(lhp, cpuid);
417 		} else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) {
418 			uint64_t pa;
419 
420 			if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR,
421 			    &pa) == 0)
422 				ret = ldmsvcs_mem_req_status(lhp, pa);
423 			else
424 				ret = EINVAL;
425 		}
426 		return (ret);
427 	}
428 }
429 
430 
431 int
432 ldom_fmri_retire(ldom_hdl_t *lhp, nvlist_t *nvl)
433 {
434 	char *name;
435 	int ret = ENOTSUP;
436 
437 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0)
438 		return (EINVAL);
439 
440 	/*
441 	 * ldom_ldmd_is_up can only be true if ldom_major_version()
442 	 * returned 1 earlier; the major version is constant for the
443 	 * life of the client process
444 	 */
445 
446 	if (!ldom_ldmd_is_up) {
447 		/* Zeus is unavail; use local routines for status/retire */
448 
449 		if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) {
450 			processorid_t vid;
451 			uint32_t cpuid;
452 
453 			if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid)
454 			    == 0 && (vid = cpu_phys2virt(lhp, cpuid)) != -1)
455 				return (p_online(vid, P_FAULTED));
456 		} else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) {
457 			fmd_agent_hdl_t *hdl;
458 			int err;
459 			if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) {
460 				err = errno;
461 			} else {
462 				err = fmd_agent_page_retire(hdl, nvl);
463 				if (err == FMD_AGENT_RETIRE_DONE)
464 					err = 0;
465 				else
466 					err = fmd_agent_errno(hdl);
467 				fmd_agent_close(hdl);
468 			}
469 			return (err);
470 		}
471 
472 		return (EINVAL);
473 	} else {
474 		/* Zeus is avail; use Zeus for status/retire */
475 
476 		if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) {
477 			uint32_t cpuid;
478 
479 			if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID,
480 			    &cpuid) == 0)
481 				ret = ldmsvcs_cpu_req_offline(lhp, cpuid);
482 		} else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) {
483 			uint64_t pa;
484 
485 			if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR,
486 			    &pa) == 0)
487 				ret = ldmsvcs_mem_req_retire(lhp, pa);
488 			else
489 				ret = EINVAL;
490 		}
491 		return (ret);
492 	}
493 }
494 
495 int
496 ldom_fmri_unretire(ldom_hdl_t *lhp, nvlist_t *nvl)
497 {
498 	char *name;
499 	int ret = ENOTSUP;
500 
501 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0)
502 		return (EINVAL);
503 
504 	/*
505 	 * ldom_ldmd_is_up can only be true if ldom_major_version()
506 	 * returned 1 earlier; the major version is constant for the
507 	 * life of the client process
508 	 */
509 
510 	if (!ldom_ldmd_is_up) {
511 		/* Zeus is unavail; use local routines for status/retire */
512 
513 		if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) {
514 			processorid_t vid;
515 			uint32_t cpuid;
516 
517 			if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid)
518 			    == 0 && (vid = cpu_phys2virt(lhp, cpuid)) != -1)
519 				return (p_online(vid, P_ONLINE));
520 		} else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) {
521 			fmd_agent_hdl_t *hdl;
522 			int err;
523 			if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) {
524 				err = errno;
525 			} else {
526 				err = fmd_agent_page_unretire(hdl, nvl);
527 				if (err == FMD_AGENT_RETIRE_DONE)
528 					err = 0;
529 				else
530 					err = fmd_agent_errno(hdl);
531 				fmd_agent_close(hdl);
532 			}
533 			return (err);
534 		}
535 
536 		return (EINVAL);
537 	} else {
538 		/* Zeus is avail; use Zeus for status/retire */
539 
540 		if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) {
541 			uint32_t cpuid;
542 
543 			if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID,
544 			    &cpuid) == 0)
545 				ret = ldmsvcs_cpu_req_online(lhp, cpuid);
546 		} else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) {
547 			uint64_t pa;
548 
549 			if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR,
550 			    &pa) == 0)
551 				ret = ldmsvcs_mem_req_unretire(lhp, pa);
552 			else
553 				ret = EINVAL;
554 		}
555 		return (ret);
556 	}
557 }
558 
559 static int
560 fmri_blacklist(ldom_hdl_t *lhp, nvlist_t *nvl, int cmd)
561 {
562 	char *name;
563 
564 	if (ldom_major_version(lhp) != 0)
565 		return (0);
566 
567 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0)
568 		return (EINVAL);
569 
570 	if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) {
571 		bl_req_t blr;
572 		char *class;
573 		int fd, rc, err;
574 
575 		if ((nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) ||
576 		    (class == NULL) || (*class == '\0'))
577 			return (EINVAL);
578 
579 		if ((fd = open("/dev/bl", O_RDONLY)) < 0)
580 			return (EIO);
581 
582 		if (nvlist_size(nvl, &blr.bl_fmrisz, NV_ENCODE_NATIVE) != 0 ||
583 		    blr.bl_fmrisz == 0 ||
584 		    (blr.bl_fmri = (caddr_t)lhp->allocp(blr.bl_fmrisz)) ==
585 		    NULL) {
586 			(void) close(fd);
587 			return (EINVAL);
588 		}
589 
590 		blr.bl_class = class;
591 
592 		rc = ioctl(fd, cmd, &blr);
593 		err = errno;
594 
595 		lhp->freep((void *)&blr.bl_fmri, blr.bl_fmrisz);
596 		(void) close(fd);
597 
598 		if (rc < 0 && err != ENOTSUP) {
599 			errno = err;
600 			return (-1);
601 		}
602 	}
603 
604 	return (0);
605 }
606 
607 /*
608  * blacklist cpus in a non-LDOMS environment
609  */
610 int
611 ldom_fmri_blacklist(ldom_hdl_t *lhp, nvlist_t *nvl)
612 {
613 	return (fmri_blacklist(lhp, nvl, BLIOC_INSERT));
614 }
615 
616 /*
617  * unblacklist cpus
618  */
619 int
620 ldom_fmri_unblacklist(ldom_hdl_t *lhp, nvlist_t *nvl)
621 {
622 	return (fmri_blacklist(lhp, nvl, BLIOC_DELETE));
623 }
624 
625 
626 ssize_t
627 ldom_get_core_md(ldom_hdl_t *lhp, uint64_t **buf)
628 {
629 	ssize_t		rv;	/* return value */
630 	uint64_t	tok;	/* opaque PRI token */
631 
632 	switch (ldom_major_version(lhp)) {
633 	case 0:
634 		/* pre LDOMS */
635 		rv = get_local_core_md(lhp, buf);
636 		break;
637 	case 1:
638 		/* LDOMS 1.0 - Zeus and libpri usable only on service dom */
639 		if (ldom_on_service(lhp) == 1) {
640 			if ((rv = ldmsvcs_get_core_md(lhp, buf)) < 1) {
641 				(void) pthread_mutex_lock(&ldom_pri_lock);
642 				rv = ldom_pri_get(PRI_GET, &tok,
643 				    buf, lhp->allocp, lhp->freep);
644 				(void) pthread_mutex_unlock(&ldom_pri_lock);
645 			} else {
646 				ldom_ldmd_is_up = 1;
647 			}
648 		} else {
649 			rv = get_local_core_md(lhp, buf);
650 		}
651 		break;
652 	default:
653 		rv = -1;
654 		break;
655 	}
656 
657 	return (rv);
658 }
659 
660 /*
661  * version 0 means no LDOMS
662  */
663 int
664 ldom_major_version(ldom_hdl_t *lhp)
665 {
666 	if (lhp == NULL)
667 		return (-1);
668 
669 	if (ldom_getinfo(lhp) == 0)
670 		return (lhp->major_version);
671 	else
672 		return (0);
673 }
674 
675 /*
676  * in the absence of ldoms we are on a single OS instance which is the
677  * equivalent of the service ldom
678  */
679 int
680 ldom_on_service(ldom_hdl_t *lhp)
681 {
682 	if (lhp == NULL)
683 		return (-1);
684 
685 	if (ldom_getinfo(lhp) == 0)
686 		return (lhp->service_ldom);
687 	else
688 		return (1);
689 }
690 
691 
692 ldom_hdl_t *
693 ldom_init(void *(*allocp)(size_t size),
694 	void (*freep)(void *addr, size_t size))
695 {
696 	struct ldom_hdl *lhp;
697 
698 	(void) pthread_mutex_lock(&ldom_pri_lock);
699 
700 	if (ldom_pri_init() < 0) {
701 		(void) pthread_mutex_unlock(&ldom_pri_lock);
702 		return (NULL);
703 	}
704 
705 	if ((lhp = allocp(sizeof (struct ldom_hdl))) == NULL) {
706 		ldom_pri_fini();
707 		(void) pthread_mutex_unlock(&ldom_pri_lock);
708 		return (NULL);
709 	}
710 
711 	(void) pthread_mutex_unlock(&ldom_pri_lock);
712 
713 	lhp->major_version = -1;	/* version not yet determined */
714 	lhp->allocp = allocp;
715 	lhp->freep = freep;
716 
717 	ldmsvcs_init(lhp);
718 
719 	return (lhp);
720 }
721 
722 
723 void
724 ldom_fini(ldom_hdl_t *lhp)
725 {
726 	if (lhp == NULL)
727 		return;
728 
729 	ldmsvcs_fini(lhp);
730 	lhp->freep(lhp, sizeof (struct ldom_hdl));
731 
732 	(void) pthread_mutex_lock(&ldom_pri_lock);
733 
734 	ldom_pri_fini();
735 
736 	(void) pthread_mutex_unlock(&ldom_pri_lock);
737 }
738 
739 /* end file */
740