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