xref: /illumos-gate/usr/src/uts/sun4v/os/hsvc.c (revision 2a8d6eba033e4713ab12b61178f0513f1f075482)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/dditypes.h>
31 #include <sys/machsystm.h>
32 #include <sys/archsystm.h>
33 #include <sys/prom_plat.h>
34 #include <sys/promif.h>
35 #include <sys/kmem.h>
36 #include <sys/hypervisor_api.h>
37 #include <sys/hsvc.h>
38 
39 #ifdef DEBUG
40 
41 int	hsvc_debug = 0;				/* HSVC debug flags */
42 
43 /*
44  * Flags to control HSVC debugging
45  */
46 #define	DBG_HSVC_REGISTER	0x0001
47 #define	DBG_HSVC_UNREGISTER	0x0002
48 #define	DBG_HSVC_OBP_CIF	0x0004
49 #define	DBG_HSVC_ALLOC		0x0008
50 #define	DBG_HSVC_VERSION	0x0010
51 #define	DBG_HSVC_REFCNT		0x0020
52 #define	DBG_HSVC_SETUP		0x0040
53 
54 #define	HSVC_CHK_REFCNT(hsvcp)	\
55 	if (hsvc_debug & DBG_HSVC_REFCNT) hsvc_chk_refcnt(hsvcp)
56 
57 #define	HSVC_DEBUG(flag, ARGS)	\
58 	if (hsvc_debug & flag)  prom_printf ARGS
59 
60 #define	HSVC_DUMP()	\
61 	if (hsvc_debug & DBG_HSVC_SETUP) hsvc_dump()
62 
63 #else /* DEBUG */
64 
65 #define	HSVC_CHK_REFCNT(hsvcp)
66 #define	HSVC_DEBUG(flag, args)
67 #define	HSVC_DUMP()
68 
69 #endif /* DEBUG */
70 
71 /*
72  * Each hypervisor API group negotiation is tracked via a
73  * hsvc structure. This structure contains the API group,
74  * currently negotiated major/minor number, a singly linked
75  * list of clients currently registered and a reference count.
76  *
77  * Since the number of API groups is fairly small, negotiated
78  * API groups are maintained via a singly linked list. Also,
79  * sufficient free space is reserved to allow for API group
80  * registration before kmem_xxx interface can be used to
81  * allocate memory dynamically.
82  *
83  * Note that all access to the API group lookup and negotiation
84  * is serialized to support strict HV API interface.
85  */
86 
87 typedef struct hsvc {
88 	struct hsvc	*next;			/* next group/free entry */
89 	uint64_t	group;			/* hypervisor service group */
90 	uint64_t	major;			/* major number */
91 	uint64_t	minor;			/* minor number */
92 	uint64_t	refcnt;			/* reference count */
93 	hsvc_info_t	*clients;		/* linked list of clients */
94 } hsvc_t;
95 
96 
97 /*
98  * Global variables
99  */
100 hsvc_t		*hsvc_groups;		/* linked list of API groups in use */
101 hsvc_t		*hsvc_avail;		/* free reserved buffers */
102 kmutex_t	hsvc_lock;		/* protects linked list and globals */
103 
104 /*
105  * Preallocate some space for boot requirements (before kmem_xxx can be
106  * used)
107  */
108 #define	HSVC_RESV_BUFS_MAX		16
109 hsvc_t	hsvc_resv_bufs[HSVC_RESV_BUFS_MAX];
110 
111 /*
112  * Pre-versioning groups (not negotiated by Ontario/Erie FCS release)
113  */
114 static uint64_t hsvc_pre_versioning_groups[] = {
115 	HSVC_GROUP_SUN4V,
116 	HSVC_GROUP_CORE,
117 	HSVC_GROUP_VPCI,
118 	HSVC_GROUP_VSC,
119 	HSVC_GROUP_NIAGARA_CPU,
120 	HSVC_GROUP_NCS,
121 	HSVC_GROUP_DIAG
122 };
123 
124 #define	HSVC_PRE_VERSIONING_GROUP_CNT	\
125 	(sizeof (hsvc_pre_versioning_groups) / sizeof (uint64_t))
126 
127 static boolean_t
128 pre_versioning_group(uint64_t api_group)
129 {
130 	int	i;
131 
132 	for (i = 0; i < HSVC_PRE_VERSIONING_GROUP_CNT; i++)
133 		if (hsvc_pre_versioning_groups[i] == api_group)
134 			return (B_TRUE);
135 	return (B_FALSE);
136 }
137 
138 static hsvc_t *
139 hsvc_lookup(hsvc_info_t *hsvcinfop)
140 {
141 	hsvc_t		*hsvcp;
142 	hsvc_info_t	*p;
143 
144 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) {
145 		for (p = hsvcp->clients; p != NULL;
146 		    p = (hsvc_info_t *)p->hsvc_private)
147 			if (p == hsvcinfop)
148 				break;
149 		if (p)
150 			break;
151 	}
152 
153 	return (hsvcp);
154 }
155 
156 #ifdef DEBUG
157 
158 /*
159  * Check client reference count
160  */
161 static void
162 hsvc_chk_refcnt(hsvc_t *hsvcp)
163 {
164 	int		refcnt;
165 	hsvc_info_t	*p;
166 
167 	for (refcnt = 0, p = hsvcp->clients; p != NULL;
168 	    p = (hsvc_info_t *)p->hsvc_private)
169 		refcnt++;
170 
171 	ASSERT(hsvcp->refcnt == refcnt);
172 }
173 
174 /*
175  * Dump registered clients information
176  */
177 static void
178 hsvc_dump(void)
179 {
180 	hsvc_t		*hsvcp;
181 	hsvc_info_t	*p;
182 
183 	mutex_enter(&hsvc_lock);
184 
185 	prom_printf("hsvc_dump: hsvc_groups: %p  hsvc_avail: %p\n",
186 	    (void *)hsvc_groups, (void *)hsvc_avail);
187 
188 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) {
189 		prom_printf(" hsvcp: %p (0x%lx 0x%lx 0x%lx) ref: %ld clients: "
190 		    "%p\n", (void *)hsvcp, hsvcp->group, hsvcp->major,
191 		    hsvcp->minor, hsvcp->refcnt, (void *)hsvcp->clients);
192 
193 		for (p = hsvcp->clients; p != NULL;
194 		    p = (hsvc_info_t *)p->hsvc_private) {
195 			prom_printf("  client %p (0x%lx 0x%lx 0x%lx '%s') "
196 			    "private: %p\n", (void *)p, p->hsvc_group,
197 			    p->hsvc_major, p->hsvc_minor, p->hsvc_modname,
198 			    p->hsvc_private);
199 		}
200 	}
201 
202 	mutex_exit(&hsvc_lock);
203 }
204 
205 #endif /* DEBUG */
206 
207 /*
208  * Allocate a buffer to cache API group information. Note that we
209  * allocate a buffer from reserved pool early on, before kmem_xxx
210  * interface becomes available.
211  */
212 static hsvc_t *
213 hsvc_alloc(void)
214 {
215 	hsvc_t	*hsvcp;
216 
217 	ASSERT(MUTEX_HELD(&hsvc_lock));
218 
219 	if (hsvc_avail != NULL) {
220 		hsvcp = hsvc_avail;
221 		hsvc_avail = hsvcp->next;
222 	} else if (kmem_ready) {
223 		hsvcp = kmem_zalloc(sizeof (hsvc_t), KM_SLEEP);
224 		HSVC_DEBUG(DBG_HSVC_ALLOC,
225 		    ("hsvc_alloc: hsvc_avail: %p  kmem_zalloc hsvcp: %p\n",
226 		    (void *)hsvc_avail, (void *)hsvcp));
227 	} else
228 		hsvcp = NULL;
229 	return (hsvcp);
230 }
231 
232 static void
233 hsvc_free(hsvc_t *hsvcp)
234 {
235 	ASSERT(hsvcp != NULL);
236 	ASSERT(MUTEX_HELD(&hsvc_lock));
237 
238 	if (hsvcp >= hsvc_resv_bufs &&
239 	    hsvcp < &hsvc_resv_bufs[HSVC_RESV_BUFS_MAX]) {
240 		hsvcp->next = hsvc_avail;
241 		hsvc_avail =  hsvcp;
242 	} else {
243 		HSVC_DEBUG(DBG_HSVC_ALLOC,
244 		    ("hsvc_free: hsvc_avail: %p  kmem_free hsvcp: %p\n",
245 		    (void *)hsvc_avail, (void *)hsvcp));
246 		(void) kmem_free(hsvcp, sizeof (hsvc_t));
247 	}
248 }
249 
250 /*
251  * Link client on the specified hsvc's client list and
252  * bump the reference count.
253  */
254 static void
255 hsvc_link_client(hsvc_t *hsvcp, hsvc_info_t *hsvcinfop)
256 {
257 	ASSERT(MUTEX_HELD(&hsvc_lock));
258 	HSVC_CHK_REFCNT(hsvcp);
259 
260 	hsvcinfop->hsvc_private = hsvcp->clients;
261 	hsvcp->clients = hsvcinfop;
262 	hsvcp->refcnt++;
263 }
264 
265 /*
266  * Unlink a client from the specified hsvc's client list and
267  * decrement the reference count, if found.
268  *
269  * Return 0 if client unlinked. Otherwise return -1.
270  */
271 static int
272 hsvc_unlink_client(hsvc_t *hsvcp, hsvc_info_t *hsvcinfop)
273 {
274 	hsvc_info_t	*p, **pp;
275 	int	status = 0;
276 
277 	ASSERT(MUTEX_HELD(&hsvc_lock));
278 	HSVC_CHK_REFCNT(hsvcp);
279 
280 	for (pp = &hsvcp->clients; (p = *pp) != NULL;
281 	    pp = (hsvc_info_t **)&p->hsvc_private) {
282 		if (p != hsvcinfop)
283 			continue;
284 
285 		ASSERT(hsvcp->refcnt > 0);
286 		hsvcp->refcnt--;
287 		*pp = (hsvc_info_t *)p->hsvc_private;
288 		p->hsvc_private = NULL;
289 		break;
290 	}
291 
292 	if (p == NULL)
293 		status = -1;
294 
295 	return (status);
296 }
297 
298 /*
299  * Negotiate/register an API group usage
300  */
301 int
302 hsvc_register(hsvc_info_t *hsvcinfop, uint64_t *supported_minor)
303 {
304 	hsvc_t *hsvcp;
305 	uint64_t api_group = hsvcinfop->hsvc_group;
306 	uint64_t major = hsvcinfop->hsvc_major;
307 	uint64_t minor = hsvcinfop->hsvc_minor;
308 	int status = 0;
309 
310 	HSVC_DEBUG(DBG_HSVC_REGISTER,
311 	    ("hsvc_register %p (0x%lx 0x%lx 0x%lx ID %s)\n", (void *)hsvcinfop,
312 	    api_group, major, minor, hsvcinfop->hsvc_modname));
313 
314 	if (hsvcinfop->hsvc_rev != HSVC_REV_1)
315 		return (EINVAL);
316 
317 	mutex_enter(&hsvc_lock);
318 
319 	/*
320 	 * Make sure that the hsvcinfop is new (i.e. not already registered).
321 	 */
322 	if (hsvc_lookup(hsvcinfop) != NULL) {
323 		mutex_exit(&hsvc_lock);
324 		return (EINVAL);
325 	}
326 
327 	/*
328 	 * Search for the specified api_group
329 	 */
330 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next)
331 		if (hsvcp->group == api_group)
332 			break;
333 
334 	if (hsvcp) {
335 		/*
336 		 * If major number mismatch, then return ENOTSUP.
337 		 * Otherwise return currently negotiated minor
338 		 * and the following status:
339 		 *	ENOTSUP		requested minor < current minor
340 		 *	OK		requested minor >= current minor
341 		 */
342 
343 		if (hsvcp->major != major) {
344 			status = ENOTSUP;
345 		} else if (hsvcp->minor > minor) {
346 			/*
347 			 * Client requested a lower minor number than
348 			 * currently in use.
349 			 */
350 			status = ENOTSUP;
351 			*supported_minor = hsvcp->minor;
352 		} else {
353 			/*
354 			 * Client requested a minor number same or higher
355 			 * than the one in use.  Set supported minor number
356 			 * and link the client on hsvc client linked list.
357 			 */
358 			*supported_minor = hsvcp->minor;
359 			hsvc_link_client(hsvcp, hsvcinfop);
360 		}
361 	} else {
362 		/*
363 		 * This service group has not been negotiated yet.
364 		 * Call OBP CIF interface to negotiate a major/minor
365 		 * number.
366 		 *
367 		 * If not enough memory to cache this information, then
368 		 * return EAGAIN so that the caller can try again later.
369 		 * Otherwise, process OBP CIF results as follows:
370 		 *
371 		 *	H_BADTRAP	OBP CIF interface is not supported.
372 		 *			If not a pre-versioning group, then
373 		 *			return EINVAL, indicating unsupported
374 		 *			API group. Otherwise, mimic default
375 		 *			behavior (i.e. support only major=1).
376 		 *
377 		 *	H_EOK		Negotiation was successful. Cache
378 		 *			and return supported major/minor,
379 		 *			limiting the minor number to the
380 		 *			requested value.
381 		 *
382 		 *	H_EINVAL	Invalid group. Return EINVAL
383 		 *
384 		 *	H_ENOTSUPPORTED	Unsupported major number. Return
385 		 *			ENOTSUP.
386 		 *
387 		 *	H_EBUSY		Return EAGAIN.
388 		 *
389 		 *	H_EWOULDBLOCK	Return EAGAIN.
390 		 */
391 		hsvcp = hsvc_alloc();
392 		if (hsvcp == NULL) {
393 			status = EAGAIN;
394 		} else {
395 			uint64_t hvstat;
396 
397 			hvstat = prom_set_sun4v_api_version(api_group,
398 			    major, minor, supported_minor);
399 
400 			HSVC_DEBUG(DBG_HSVC_OBP_CIF,
401 			    ("prom_set_sun4v_api_ver: 0x%lx 0x%lx, 0x%lx "
402 			    " hvstat: 0x%lx sup_minor: 0x%lx\n", api_group,
403 			    major, minor, hvstat, *supported_minor));
404 
405 			switch (hvstat) {
406 			case H_EBADTRAP:
407 				/*
408 				 * Older firmware does not support OBP CIF
409 				 * interface. If it's a pre-versioning group,
410 				 * then assume that the firmware supports
411 				 * only major=1 and minor=0.
412 				 */
413 				if (!pre_versioning_group(api_group)) {
414 					status = EINVAL;
415 					break;
416 				} else if (major != 1) {
417 					status = ENOTSUP;
418 					break;
419 				}
420 
421 				/*
422 				 * It's a preversioning group. Default minor
423 				 * value to 0.
424 				 */
425 				*supported_minor = 0;
426 
427 				/*FALLTHROUGH*/
428 			case H_EOK:
429 				/*
430 				 * Limit supported minor number to the
431 				 * requested value and cache the new
432 				 * API group information.
433 				 */
434 				if (*supported_minor > minor)
435 					*supported_minor = minor;
436 				hsvcp->group = api_group;
437 				hsvcp->major = major;
438 				hsvcp->minor = *supported_minor;
439 				hsvcp->refcnt = 0;
440 				hsvcp->clients = NULL;
441 				hsvcp->next = hsvc_groups;
442 				hsvc_groups = hsvcp;
443 
444 				/*
445 				 * Link the caller on the client linked list.
446 				 */
447 				hsvc_link_client(hsvcp, hsvcinfop);
448 				break;
449 
450 			case H_ENOTSUPPORTED:
451 				status = ENOTSUP;
452 				break;
453 
454 			case H_EBUSY:
455 			case H_EWOULDBLOCK:
456 				status = EAGAIN;
457 				break;
458 
459 			case H_EINVAL:
460 			default:
461 				status = EINVAL;
462 				break;
463 			}
464 		}
465 		/*
466 		 * Deallocate entry if not used
467 		 */
468 		if (status != 0)
469 			hsvc_free(hsvcp);
470 	}
471 	mutex_exit(&hsvc_lock);
472 
473 	HSVC_DEBUG(DBG_HSVC_REGISTER,
474 	    ("hsvc_register(%p) status; %d sup_minor: 0x%lx\n",
475 	    (void *)hsvcinfop, status, *supported_minor));
476 
477 	return (status);
478 }
479 
480 /*
481  * Unregister an API group usage
482  */
483 int
484 hsvc_unregister(hsvc_info_t *hsvcinfop)
485 {
486 	hsvc_t		**hsvcpp, *hsvcp;
487 	uint64_t	api_group;
488 	uint64_t	major, supported_minor;
489 	int		status = 0;
490 
491 	if (hsvcinfop->hsvc_rev != HSVC_REV_1)
492 		return (EINVAL);
493 
494 	major = hsvcinfop->hsvc_major;
495 	api_group = hsvcinfop->hsvc_group;
496 
497 	HSVC_DEBUG(DBG_HSVC_UNREGISTER,
498 	    ("hsvc_unregister %p (0x%lx 0x%lx 0x%lx ID %s)\n",
499 	    (void *)hsvcinfop, api_group, major, hsvcinfop->hsvc_minor,
500 	    hsvcinfop->hsvc_modname));
501 
502 	/*
503 	 * Search for the matching entry and return EINVAL if no match found.
504 	 * Otherwise, remove it from our list and unregister the API
505 	 * group if this was the last reference to that API group.
506 	 */
507 	mutex_enter(&hsvc_lock);
508 
509 	for (hsvcpp = &hsvc_groups; (hsvcp = *hsvcpp) != NULL;
510 	    hsvcpp = &hsvcp->next) {
511 		if (hsvcp->group != api_group || hsvcp->major != major)
512 			continue;
513 
514 		/*
515 		 * Search client list for a matching hsvcinfop entry
516 		 * and unlink it and decrement refcnt, if found.
517 		 */
518 		if (hsvc_unlink_client(hsvcp, hsvcinfop) < 0) {
519 			/* client not registered */
520 			status = EINVAL;
521 			break;
522 		}
523 
524 		/*
525 		 * Client has been unlinked. If this was the last
526 		 * reference, unregister API group via OBP CIF
527 		 * interface.
528 		 */
529 		if (hsvcp->refcnt == 0) {
530 			uint64_t	hvstat;
531 
532 			ASSERT(hsvcp->clients == NULL);
533 			hvstat = prom_set_sun4v_api_version(api_group, 0, 0,
534 			    &supported_minor);
535 
536 			HSVC_DEBUG(DBG_HSVC_OBP_CIF,
537 			    (" prom unreg group: 0x%lx hvstat: 0x%lx\n",
538 			    api_group, hvstat));
539 
540 			/*
541 			 * Note that the call to unnegotiate an API group
542 			 * may fail if anyone, including OBP, is using
543 			 * those services. However, the caller is done
544 			 * with this API group and should be allowed to
545 			 * unregister regardless of the outcome.
546 			 */
547 			*hsvcpp = hsvcp->next;
548 			hsvc_free(hsvcp);
549 		}
550 		break;
551 	}
552 
553 	if (hsvcp == NULL)
554 		status = EINVAL;
555 
556 	mutex_exit(&hsvc_lock);
557 
558 	HSVC_DEBUG(DBG_HSVC_UNREGISTER,
559 	    ("hsvc_unregister %p status: %d\n", (void *)hsvcinfop, status));
560 
561 	return (status);
562 }
563 
564 
565 /*
566  * Get negotiated major/minor version number for an API group
567  */
568 int
569 hsvc_version(uint64_t api_group, uint64_t *majorp, uint64_t *minorp)
570 {
571 	int status = 0;
572 	uint64_t hvstat;
573 	hsvc_t	*hsvcp;
574 
575 	/*
576 	 * Check if the specified api_group is already in use.
577 	 * If so, return currently negotiated major/minor number.
578 	 * Otherwise, call OBP CIF interface to get the currently
579 	 * negotiated major/minor number.
580 	 */
581 	mutex_enter(&hsvc_lock);
582 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next)
583 		if (hsvcp->group == api_group)
584 			break;
585 
586 	if (hsvcp) {
587 		*majorp = hsvcp->major;
588 		*minorp = hsvcp->minor;
589 	} else {
590 		hvstat = prom_get_sun4v_api_version(api_group, majorp, minorp);
591 
592 		switch (hvstat) {
593 		case H_EBADTRAP:
594 			/*
595 			 * Older firmware does not support OBP CIF
596 			 * interface. If it's a pre-versioning group,
597 			 * then return default major/minor (i.e. 1/0).
598 			 * Otherwise, return EINVAL.
599 			 */
600 			if (pre_versioning_group(api_group)) {
601 				*majorp = 1;
602 				*minorp = 0;
603 			} else
604 				status = EINVAL;
605 			break;
606 
607 		case H_EINVAL:
608 		default:
609 			status = EINVAL;
610 			break;
611 
612 		}
613 	}
614 	mutex_exit(&hsvc_lock);
615 
616 	HSVC_DEBUG(DBG_HSVC_VERSION,
617 	    ("hsvc_version(0x%lx) status: %d major: 0x%lx minor: 0x%lx\n",
618 	    api_group, status, *majorp, *minorp));
619 
620 	return (status);
621 }
622 
623 /*
624  * Initialize framework data structures
625  */
626 void
627 hsvc_init(void)
628 {
629 	int		i;
630 	hsvc_t		*hsvcp;
631 
632 	/*
633 	 * Initialize global data structures
634 	 */
635 	mutex_init(&hsvc_lock, NULL, MUTEX_DEFAULT, NULL);
636 	hsvc_groups = NULL;
637 	hsvc_avail = NULL;
638 
639 	/*
640 	 * Setup initial free list
641 	 */
642 	mutex_enter(&hsvc_lock);
643 	for (i = 0, hsvcp = &hsvc_resv_bufs[0];
644 	    i < HSVC_RESV_BUFS_MAX; i++, hsvcp++)
645 		hsvc_free(hsvcp);
646 	mutex_exit(&hsvc_lock);
647 }
648 
649 
650 /*
651  * Hypervisor services to be negotiated at boot time.
652  *
653  * Note that the kernel needs to negotiate the HSVC_GROUP_SUN4V
654  * API group first, before doing any other negotiation. Also, it
655  * uses hypervisor services belonging to the HSVC_GROUP_CORE API
656  * group only for itself.
657  *
658  * Note that the HSVC_GROUP_DIAG is negotiated on behalf of
659  * any driver/module using DIAG services.
660  */
661 typedef struct hsvc_info_unix_s {
662 	hsvc_info_t	hsvcinfo;
663 	int		required;
664 } hsvc_info_unix_t;
665 
666 static hsvc_info_unix_t  hsvcinfo_unix[] = {
667 	{{HSVC_REV_1, NULL,	HSVC_GROUP_SUN4V,	1,	0, NULL}, 1},
668 	{{HSVC_REV_1, NULL,	HSVC_GROUP_CORE,	1,	1, NULL}, 1},
669 	{{HSVC_REV_1, NULL,	HSVC_GROUP_DIAG,	1,	0, NULL}, 1},
670 	{{HSVC_REV_1, NULL,	HSVC_GROUP_INTR,	1,	0, NULL}, 0},
671 };
672 
673 #define	HSVCINFO_UNIX_CNT	(sizeof (hsvcinfo_unix) / sizeof (hsvc_info_t))
674 static char	*hsvcinfo_unix_modname = "unix";
675 
676 /*
677  * Initialize framework and register hypervisor services to be used
678  * by the kernel.
679  */
680 void
681 hsvc_setup()
682 {
683 	int			i, status;
684 	uint64_t		sup_minor;
685 	hsvc_info_unix_t	*hsvcinfop;
686 
687 	/*
688 	 * Initialize framework
689 	 */
690 	hsvc_init();
691 
692 	/*
693 	 * Negotiate versioning for required groups
694 	 */
695 	for (hsvcinfop = &hsvcinfo_unix[0], i = 0; i < HSVCINFO_UNIX_CNT;
696 	    i++, hsvcinfop++) {
697 		hsvcinfop->hsvcinfo.hsvc_private = NULL;
698 		hsvcinfop->hsvcinfo.hsvc_modname = hsvcinfo_unix_modname;
699 		status = hsvc_register(&(hsvcinfop->hsvcinfo), &sup_minor);
700 
701 		if ((status != 0) && hsvcinfop->required) {
702 			cmn_err(CE_PANIC, "%s: cannot negotiate hypervisor "
703 			    "services - group: 0x%lx major: 0x%lx minor: 0x%lx"
704 			    " errno: %d\n", hsvcinfop->hsvcinfo.hsvc_modname,
705 			    hsvcinfop->hsvcinfo.hsvc_group,
706 			    hsvcinfop->hsvcinfo.hsvc_major,
707 			    hsvcinfop->hsvcinfo.hsvc_minor, status);
708 		}
709 	}
710 	HSVC_DUMP();
711 }
712