xref: /onnv-gate/usr/src/uts/sun4v/os/hsvc.c (revision 11304:3092d1e303d6)
11592Sgirish /*
21592Sgirish  * CDDL HEADER START
31592Sgirish  *
41592Sgirish  * The contents of this file are subject to the terms of the
51592Sgirish  * Common Development and Distribution License (the "License").
61592Sgirish  * You may not use this file except in compliance with the License.
71592Sgirish  *
81592Sgirish  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91592Sgirish  * or http://www.opensolaris.org/os/licensing.
101592Sgirish  * See the License for the specific language governing permissions
111592Sgirish  * and limitations under the License.
121592Sgirish  *
131592Sgirish  * When distributing Covered Code, include this CDDL HEADER in each
141592Sgirish  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151592Sgirish  * If applicable, add the following below this CDDL HEADER, with the
161592Sgirish  * fields enclosed by brackets "[]" replaced with your own identifying
171592Sgirish  * information: Portions Copyright [yyyy] [name of copyright owner]
181592Sgirish  *
191592Sgirish  * CDDL HEADER END
201592Sgirish  */
211991Sheppo 
221592Sgirish /*
2311172SHaik.Aftandilian@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241592Sgirish  * Use is subject to license terms.
251592Sgirish  */
261592Sgirish 
271592Sgirish #include <sys/types.h>
281592Sgirish #include <sys/dditypes.h>
291592Sgirish #include <sys/machsystm.h>
301592Sgirish #include <sys/archsystm.h>
311592Sgirish #include <sys/prom_plat.h>
321592Sgirish #include <sys/promif.h>
331592Sgirish #include <sys/kmem.h>
341592Sgirish #include <sys/hypervisor_api.h>
351592Sgirish #include <sys/hsvc.h>
361592Sgirish 
371592Sgirish #ifdef DEBUG
381592Sgirish 
391592Sgirish int	hsvc_debug = 0;				/* HSVC debug flags */
401592Sgirish 
411592Sgirish /*
421592Sgirish  * Flags to control HSVC debugging
431592Sgirish  */
441592Sgirish #define	DBG_HSVC_REGISTER	0x0001
451592Sgirish #define	DBG_HSVC_UNREGISTER	0x0002
461592Sgirish #define	DBG_HSVC_OBP_CIF	0x0004
471592Sgirish #define	DBG_HSVC_ALLOC		0x0008
481592Sgirish #define	DBG_HSVC_VERSION	0x0010
491592Sgirish #define	DBG_HSVC_REFCNT		0x0020
501592Sgirish #define	DBG_HSVC_SETUP		0x0040
511592Sgirish 
521592Sgirish #define	HSVC_CHK_REFCNT(hsvcp)	\
531592Sgirish 	if (hsvc_debug & DBG_HSVC_REFCNT) hsvc_chk_refcnt(hsvcp)
541592Sgirish 
551592Sgirish #define	HSVC_DEBUG(flag, ARGS)	\
561592Sgirish 	if (hsvc_debug & flag)  prom_printf ARGS
571592Sgirish 
581592Sgirish #define	HSVC_DUMP()	\
591592Sgirish 	if (hsvc_debug & DBG_HSVC_SETUP) hsvc_dump()
601592Sgirish 
611592Sgirish #else /* DEBUG */
621592Sgirish 
631592Sgirish #define	HSVC_CHK_REFCNT(hsvcp)
641592Sgirish #define	HSVC_DEBUG(flag, args)
651592Sgirish #define	HSVC_DUMP()
661592Sgirish 
671592Sgirish #endif /* DEBUG */
681592Sgirish 
691592Sgirish /*
701592Sgirish  * Each hypervisor API group negotiation is tracked via a
711592Sgirish  * hsvc structure. This structure contains the API group,
721592Sgirish  * currently negotiated major/minor number, a singly linked
731592Sgirish  * list of clients currently registered and a reference count.
741592Sgirish  *
751592Sgirish  * Since the number of API groups is fairly small, negotiated
761592Sgirish  * API groups are maintained via a singly linked list. Also,
771592Sgirish  * sufficient free space is reserved to allow for API group
781592Sgirish  * registration before kmem_xxx interface can be used to
791592Sgirish  * allocate memory dynamically.
801592Sgirish  *
811592Sgirish  * Note that all access to the API group lookup and negotiation
821592Sgirish  * is serialized to support strict HV API interface.
831592Sgirish  */
841592Sgirish 
851592Sgirish typedef struct hsvc {
861592Sgirish 	struct hsvc	*next;			/* next group/free entry */
871592Sgirish 	uint64_t	group;			/* hypervisor service group */
881592Sgirish 	uint64_t	major;			/* major number */
891592Sgirish 	uint64_t	minor;			/* minor number */
901592Sgirish 	uint64_t	refcnt;			/* reference count */
911592Sgirish 	hsvc_info_t	*clients;		/* linked list of clients */
921592Sgirish } hsvc_t;
931592Sgirish 
941592Sgirish 
951592Sgirish /*
961592Sgirish  * Global variables
971592Sgirish  */
981592Sgirish hsvc_t		*hsvc_groups;		/* linked list of API groups in use */
991592Sgirish hsvc_t		*hsvc_avail;		/* free reserved buffers */
1001592Sgirish kmutex_t	hsvc_lock;		/* protects linked list and globals */
1011592Sgirish 
1021592Sgirish /*
1031592Sgirish  * Preallocate some space for boot requirements (before kmem_xxx can be
1041592Sgirish  * used)
1051592Sgirish  */
1061592Sgirish #define	HSVC_RESV_BUFS_MAX		16
1071592Sgirish hsvc_t	hsvc_resv_bufs[HSVC_RESV_BUFS_MAX];
1081592Sgirish 
1091592Sgirish /*
1101592Sgirish  * Pre-versioning groups (not negotiated by Ontario/Erie FCS release)
1111592Sgirish  */
1121592Sgirish static uint64_t hsvc_pre_versioning_groups[] = {
1131592Sgirish 	HSVC_GROUP_SUN4V,
1141592Sgirish 	HSVC_GROUP_CORE,
1151592Sgirish 	HSVC_GROUP_VPCI,
1161592Sgirish 	HSVC_GROUP_VSC,
1171592Sgirish 	HSVC_GROUP_NIAGARA_CPU,
1181592Sgirish 	HSVC_GROUP_NCS,
1191592Sgirish 	HSVC_GROUP_DIAG
1201592Sgirish };
1211592Sgirish 
1221592Sgirish #define	HSVC_PRE_VERSIONING_GROUP_CNT	\
1231592Sgirish 	(sizeof (hsvc_pre_versioning_groups) / sizeof (uint64_t))
1241592Sgirish 
1251592Sgirish static boolean_t
pre_versioning_group(uint64_t api_group)1261592Sgirish pre_versioning_group(uint64_t api_group)
1271592Sgirish {
1281592Sgirish 	int	i;
1291592Sgirish 
1301592Sgirish 	for (i = 0; i < HSVC_PRE_VERSIONING_GROUP_CNT; i++)
1311592Sgirish 		if (hsvc_pre_versioning_groups[i] == api_group)
1321592Sgirish 			return (B_TRUE);
1331592Sgirish 	return (B_FALSE);
1341592Sgirish }
1351592Sgirish 
1361592Sgirish static hsvc_t *
hsvc_lookup(hsvc_info_t * hsvcinfop)1371592Sgirish hsvc_lookup(hsvc_info_t *hsvcinfop)
1381592Sgirish {
1391592Sgirish 	hsvc_t		*hsvcp;
1401592Sgirish 	hsvc_info_t	*p;
1411592Sgirish 
1421592Sgirish 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) {
1431592Sgirish 		for (p = hsvcp->clients; p != NULL;
1441592Sgirish 		    p = (hsvc_info_t *)p->hsvc_private)
1451592Sgirish 			if (p == hsvcinfop)
1461592Sgirish 				break;
1471592Sgirish 		if (p)
1481592Sgirish 			break;
1491592Sgirish 	}
1501592Sgirish 
1511592Sgirish 	return (hsvcp);
1521592Sgirish }
1531592Sgirish 
1541592Sgirish #ifdef DEBUG
1551592Sgirish 
1561592Sgirish /*
1571592Sgirish  * Check client reference count
1581592Sgirish  */
1591592Sgirish static void
hsvc_chk_refcnt(hsvc_t * hsvcp)1601592Sgirish hsvc_chk_refcnt(hsvc_t *hsvcp)
1611592Sgirish {
1621592Sgirish 	int		refcnt;
1631592Sgirish 	hsvc_info_t	*p;
1641592Sgirish 
1651592Sgirish 	for (refcnt = 0, p = hsvcp->clients; p != NULL;
1661592Sgirish 	    p = (hsvc_info_t *)p->hsvc_private)
1671592Sgirish 		refcnt++;
1681592Sgirish 
1691592Sgirish 	ASSERT(hsvcp->refcnt == refcnt);
1701592Sgirish }
1711592Sgirish 
1721592Sgirish /*
1731592Sgirish  * Dump registered clients information
1741592Sgirish  */
1751592Sgirish static void
hsvc_dump(void)1761592Sgirish hsvc_dump(void)
1771592Sgirish {
1781592Sgirish 	hsvc_t		*hsvcp;
1791592Sgirish 	hsvc_info_t	*p;
1801592Sgirish 
1811592Sgirish 	mutex_enter(&hsvc_lock);
1821592Sgirish 
1831592Sgirish 	prom_printf("hsvc_dump: hsvc_groups: %p  hsvc_avail: %p\n",
1847240Srh87107 	    (void *)hsvc_groups, (void *)hsvc_avail);
1851592Sgirish 
1861592Sgirish 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) {
1871717Swesolows 		prom_printf(" hsvcp: %p (0x%lx 0x%lx 0x%lx) ref: %ld clients: "
1887240Srh87107 		    "%p\n", (void *)hsvcp, hsvcp->group, hsvcp->major,
1897240Srh87107 		    hsvcp->minor, hsvcp->refcnt, (void *)hsvcp->clients);
1901592Sgirish 
1911592Sgirish 		for (p = hsvcp->clients; p != NULL;
1921592Sgirish 		    p = (hsvc_info_t *)p->hsvc_private) {
1931592Sgirish 			prom_printf("  client %p (0x%lx 0x%lx 0x%lx '%s') "
1947240Srh87107 			    "private: %p\n", (void *)p, p->hsvc_group,
1957240Srh87107 			    p->hsvc_major, p->hsvc_minor, p->hsvc_modname,
1967240Srh87107 			    p->hsvc_private);
1971592Sgirish 		}
1981592Sgirish 	}
1991592Sgirish 
2001592Sgirish 	mutex_exit(&hsvc_lock);
2011592Sgirish }
2021592Sgirish 
2031592Sgirish #endif /* DEBUG */
2041592Sgirish 
2051592Sgirish /*
2061592Sgirish  * Allocate a buffer to cache API group information. Note that we
2071592Sgirish  * allocate a buffer from reserved pool early on, before kmem_xxx
2081592Sgirish  * interface becomes available.
2091592Sgirish  */
2101592Sgirish static hsvc_t *
hsvc_alloc(void)2111592Sgirish hsvc_alloc(void)
2121592Sgirish {
2131592Sgirish 	hsvc_t	*hsvcp;
2141592Sgirish 
2151592Sgirish 	ASSERT(MUTEX_HELD(&hsvc_lock));
2161592Sgirish 
2171592Sgirish 	if (hsvc_avail != NULL) {
2181592Sgirish 		hsvcp = hsvc_avail;
2191592Sgirish 		hsvc_avail = hsvcp->next;
2201592Sgirish 	} else if (kmem_ready) {
2211592Sgirish 		hsvcp = kmem_zalloc(sizeof (hsvc_t), KM_SLEEP);
2221592Sgirish 		HSVC_DEBUG(DBG_HSVC_ALLOC,
2231592Sgirish 		    ("hsvc_alloc: hsvc_avail: %p  kmem_zalloc hsvcp: %p\n",
2247240Srh87107 		    (void *)hsvc_avail, (void *)hsvcp));
2251592Sgirish 	} else
2261592Sgirish 		hsvcp = NULL;
2271592Sgirish 	return (hsvcp);
2281592Sgirish }
2291592Sgirish 
2301592Sgirish static void
hsvc_free(hsvc_t * hsvcp)2311592Sgirish hsvc_free(hsvc_t *hsvcp)
2321592Sgirish {
2331592Sgirish 	ASSERT(hsvcp != NULL);
2341592Sgirish 	ASSERT(MUTEX_HELD(&hsvc_lock));
2351592Sgirish 
2361592Sgirish 	if (hsvcp >= hsvc_resv_bufs &&
2371592Sgirish 	    hsvcp < &hsvc_resv_bufs[HSVC_RESV_BUFS_MAX]) {
2381592Sgirish 		hsvcp->next = hsvc_avail;
2391592Sgirish 		hsvc_avail =  hsvcp;
2401592Sgirish 	} else {
2411592Sgirish 		HSVC_DEBUG(DBG_HSVC_ALLOC,
2421592Sgirish 		    ("hsvc_free: hsvc_avail: %p  kmem_free hsvcp: %p\n",
2437240Srh87107 		    (void *)hsvc_avail, (void *)hsvcp));
2441592Sgirish 		(void) kmem_free(hsvcp, sizeof (hsvc_t));
2451592Sgirish 	}
2461592Sgirish }
2471592Sgirish 
2481592Sgirish /*
2491592Sgirish  * Link client on the specified hsvc's client list and
2501592Sgirish  * bump the reference count.
2511592Sgirish  */
2521592Sgirish static void
hsvc_link_client(hsvc_t * hsvcp,hsvc_info_t * hsvcinfop)2531592Sgirish hsvc_link_client(hsvc_t *hsvcp, hsvc_info_t *hsvcinfop)
2541592Sgirish {
2551592Sgirish 	ASSERT(MUTEX_HELD(&hsvc_lock));
2561592Sgirish 	HSVC_CHK_REFCNT(hsvcp);
2571592Sgirish 
2581592Sgirish 	hsvcinfop->hsvc_private = hsvcp->clients;
2591592Sgirish 	hsvcp->clients = hsvcinfop;
2601592Sgirish 	hsvcp->refcnt++;
2611592Sgirish }
2621592Sgirish 
2631592Sgirish /*
2641592Sgirish  * Unlink a client from the specified hsvc's client list and
2651592Sgirish  * decrement the reference count, if found.
2661592Sgirish  *
2671592Sgirish  * Return 0 if client unlinked. Otherwise return -1.
2681592Sgirish  */
2691592Sgirish static int
hsvc_unlink_client(hsvc_t * hsvcp,hsvc_info_t * hsvcinfop)2701592Sgirish hsvc_unlink_client(hsvc_t *hsvcp, hsvc_info_t *hsvcinfop)
2711592Sgirish {
2721592Sgirish 	hsvc_info_t	*p, **pp;
2731592Sgirish 	int	status = 0;
2741592Sgirish 
2751592Sgirish 	ASSERT(MUTEX_HELD(&hsvc_lock));
2761592Sgirish 	HSVC_CHK_REFCNT(hsvcp);
2771592Sgirish 
2781592Sgirish 	for (pp = &hsvcp->clients; (p = *pp) != NULL;
2791592Sgirish 	    pp = (hsvc_info_t **)&p->hsvc_private) {
2801592Sgirish 		if (p != hsvcinfop)
2811592Sgirish 			continue;
2821592Sgirish 
2831592Sgirish 		ASSERT(hsvcp->refcnt > 0);
2841592Sgirish 		hsvcp->refcnt--;
2851592Sgirish 		*pp = (hsvc_info_t *)p->hsvc_private;
2861592Sgirish 		p->hsvc_private = NULL;
2871592Sgirish 		break;
2881592Sgirish 	}
2891592Sgirish 
2901592Sgirish 	if (p == NULL)
2911592Sgirish 		status = -1;
2921592Sgirish 
2931592Sgirish 	return (status);
2941592Sgirish }
2951592Sgirish 
2961592Sgirish /*
2971592Sgirish  * Negotiate/register an API group usage
2981592Sgirish  */
2991592Sgirish int
hsvc_register(hsvc_info_t * hsvcinfop,uint64_t * supported_minor)3001592Sgirish hsvc_register(hsvc_info_t *hsvcinfop, uint64_t *supported_minor)
3011592Sgirish {
3021592Sgirish 	hsvc_t *hsvcp;
3031592Sgirish 	uint64_t api_group = hsvcinfop->hsvc_group;
3041592Sgirish 	uint64_t major = hsvcinfop->hsvc_major;
3051592Sgirish 	uint64_t minor = hsvcinfop->hsvc_minor;
3061592Sgirish 	int status = 0;
3071592Sgirish 
3081592Sgirish 	HSVC_DEBUG(DBG_HSVC_REGISTER,
3097240Srh87107 	    ("hsvc_register %p (0x%lx 0x%lx 0x%lx ID %s)\n", (void *)hsvcinfop,
3101592Sgirish 	    api_group, major, minor, hsvcinfop->hsvc_modname));
3111592Sgirish 
3121592Sgirish 	if (hsvcinfop->hsvc_rev != HSVC_REV_1)
3131592Sgirish 		return (EINVAL);
3141592Sgirish 
3151592Sgirish 	mutex_enter(&hsvc_lock);
3161592Sgirish 
3171592Sgirish 	/*
3181592Sgirish 	 * Make sure that the hsvcinfop is new (i.e. not already registered).
3191592Sgirish 	 */
3201592Sgirish 	if (hsvc_lookup(hsvcinfop) != NULL) {
3211592Sgirish 		mutex_exit(&hsvc_lock);
3221592Sgirish 		return (EINVAL);
3231592Sgirish 	}
3241592Sgirish 
3251592Sgirish 	/*
3261592Sgirish 	 * Search for the specified api_group
3271592Sgirish 	 */
3281592Sgirish 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next)
3291592Sgirish 		if (hsvcp->group == api_group)
3301592Sgirish 			break;
3311592Sgirish 
3321592Sgirish 	if (hsvcp) {
3331592Sgirish 		/*
3341592Sgirish 		 * If major number mismatch, then return ENOTSUP.
3351592Sgirish 		 * Otherwise return currently negotiated minor
3361592Sgirish 		 * and the following status:
3371592Sgirish 		 *	ENOTSUP		requested minor < current minor
3381592Sgirish 		 *	OK		requested minor >= current minor
3391592Sgirish 		 */
3401592Sgirish 
3411592Sgirish 		if (hsvcp->major != major) {
3421592Sgirish 			status = ENOTSUP;
3431592Sgirish 		} else if (hsvcp->minor > minor) {
3441592Sgirish 			/*
3451592Sgirish 			 * Client requested a lower minor number than
3461592Sgirish 			 * currently in use.
3471592Sgirish 			 */
3481592Sgirish 			status = ENOTSUP;
3491592Sgirish 			*supported_minor = hsvcp->minor;
3501592Sgirish 		} else {
3511592Sgirish 			/*
3521592Sgirish 			 * Client requested a minor number same or higher
3531592Sgirish 			 * than the one in use.  Set supported minor number
3541592Sgirish 			 * and link the client on hsvc client linked list.
3551592Sgirish 			 */
3561592Sgirish 			*supported_minor = hsvcp->minor;
3571592Sgirish 			hsvc_link_client(hsvcp, hsvcinfop);
3581592Sgirish 		}
3591592Sgirish 	} else {
3601592Sgirish 		/*
3611592Sgirish 		 * This service group has not been negotiated yet.
3621592Sgirish 		 * Call OBP CIF interface to negotiate a major/minor
3631592Sgirish 		 * number.
3641592Sgirish 		 *
3651592Sgirish 		 * If not enough memory to cache this information, then
3661592Sgirish 		 * return EAGAIN so that the caller can try again later.
3671592Sgirish 		 * Otherwise, process OBP CIF results as follows:
3681592Sgirish 		 *
3691592Sgirish 		 *	H_BADTRAP	OBP CIF interface is not supported.
3701592Sgirish 		 *			If not a pre-versioning group, then
3711592Sgirish 		 *			return EINVAL, indicating unsupported
3721592Sgirish 		 *			API group. Otherwise, mimic default
3731592Sgirish 		 *			behavior (i.e. support only major=1).
3741592Sgirish 		 *
3751592Sgirish 		 *	H_EOK		Negotiation was successful. Cache
3761592Sgirish 		 *			and return supported major/minor,
3771592Sgirish 		 *			limiting the minor number to the
3781592Sgirish 		 *			requested value.
3791592Sgirish 		 *
3801592Sgirish 		 *	H_EINVAL	Invalid group. Return EINVAL
3811592Sgirish 		 *
3821592Sgirish 		 *	H_ENOTSUPPORTED	Unsupported major number. Return
3831592Sgirish 		 *			ENOTSUP.
3841592Sgirish 		 *
3851592Sgirish 		 *	H_EBUSY		Return EAGAIN.
3861592Sgirish 		 *
3871592Sgirish 		 *	H_EWOULDBLOCK	Return EAGAIN.
3881592Sgirish 		 */
3891592Sgirish 		hsvcp = hsvc_alloc();
3901592Sgirish 		if (hsvcp == NULL) {
3911592Sgirish 			status = EAGAIN;
3921592Sgirish 		} else {
3931592Sgirish 			uint64_t hvstat;
3941592Sgirish 
3951592Sgirish 			hvstat = prom_set_sun4v_api_version(api_group,
3961592Sgirish 			    major, minor, supported_minor);
3971592Sgirish 
3981592Sgirish 			HSVC_DEBUG(DBG_HSVC_OBP_CIF,
3991592Sgirish 			    ("prom_set_sun4v_api_ver: 0x%lx 0x%lx, 0x%lx "
4001592Sgirish 			    " hvstat: 0x%lx sup_minor: 0x%lx\n", api_group,
4011592Sgirish 			    major, minor, hvstat, *supported_minor));
4021592Sgirish 
4031592Sgirish 			switch (hvstat) {
4041592Sgirish 			case H_EBADTRAP:
4051592Sgirish 				/*
4061592Sgirish 				 * Older firmware does not support OBP CIF
4071592Sgirish 				 * interface. If it's a pre-versioning group,
4081592Sgirish 				 * then assume that the firmware supports
4091592Sgirish 				 * only major=1 and minor=0.
4101592Sgirish 				 */
4111592Sgirish 				if (!pre_versioning_group(api_group)) {
4121592Sgirish 					status = EINVAL;
4131592Sgirish 					break;
4141592Sgirish 				} else if (major != 1) {
4151592Sgirish 					status = ENOTSUP;
4161592Sgirish 					break;
4171592Sgirish 				}
4181592Sgirish 
4191592Sgirish 				/*
4201592Sgirish 				 * It's a preversioning group. Default minor
4211592Sgirish 				 * value to 0.
4221592Sgirish 				 */
4231592Sgirish 				*supported_minor = 0;
4241592Sgirish 
4251592Sgirish 				/*FALLTHROUGH*/
4261592Sgirish 			case H_EOK:
4271592Sgirish 				/*
4281592Sgirish 				 * Limit supported minor number to the
4291592Sgirish 				 * requested value and cache the new
4301592Sgirish 				 * API group information.
4311592Sgirish 				 */
4321592Sgirish 				if (*supported_minor > minor)
4331592Sgirish 					*supported_minor = minor;
4341592Sgirish 				hsvcp->group = api_group;
4351592Sgirish 				hsvcp->major = major;
4361592Sgirish 				hsvcp->minor = *supported_minor;
4371592Sgirish 				hsvcp->refcnt = 0;
4381592Sgirish 				hsvcp->clients = NULL;
4391592Sgirish 				hsvcp->next = hsvc_groups;
4401592Sgirish 				hsvc_groups = hsvcp;
4411592Sgirish 
4421592Sgirish 				/*
4431592Sgirish 				 * Link the caller on the client linked list.
4441592Sgirish 				 */
4451592Sgirish 				hsvc_link_client(hsvcp, hsvcinfop);
4461592Sgirish 				break;
4471592Sgirish 
4481592Sgirish 			case H_ENOTSUPPORTED:
4491592Sgirish 				status = ENOTSUP;
4501592Sgirish 				break;
4511592Sgirish 
4521592Sgirish 			case H_EBUSY:
4531592Sgirish 			case H_EWOULDBLOCK:
4541592Sgirish 				status = EAGAIN;
4551592Sgirish 				break;
4561592Sgirish 
4571592Sgirish 			case H_EINVAL:
4581592Sgirish 			default:
4591592Sgirish 				status = EINVAL;
4601592Sgirish 				break;
4611592Sgirish 			}
4621592Sgirish 		}
4631592Sgirish 		/*
4641592Sgirish 		 * Deallocate entry if not used
4651592Sgirish 		 */
4661592Sgirish 		if (status != 0)
4671592Sgirish 			hsvc_free(hsvcp);
4681592Sgirish 	}
4691592Sgirish 	mutex_exit(&hsvc_lock);
4701592Sgirish 
4711592Sgirish 	HSVC_DEBUG(DBG_HSVC_REGISTER,
4727240Srh87107 	    ("hsvc_register(%p) status; %d sup_minor: 0x%lx\n",
4737240Srh87107 	    (void *)hsvcinfop, status, *supported_minor));
4741592Sgirish 
4751592Sgirish 	return (status);
4761592Sgirish }
4771592Sgirish 
4781592Sgirish /*
4791592Sgirish  * Unregister an API group usage
4801592Sgirish  */
4811592Sgirish int
hsvc_unregister(hsvc_info_t * hsvcinfop)4821592Sgirish hsvc_unregister(hsvc_info_t *hsvcinfop)
4831592Sgirish {
4841592Sgirish 	hsvc_t		**hsvcpp, *hsvcp;
4851592Sgirish 	uint64_t	api_group;
4861592Sgirish 	uint64_t	major, supported_minor;
4871592Sgirish 	int		status = 0;
4881592Sgirish 
4891592Sgirish 	if (hsvcinfop->hsvc_rev != HSVC_REV_1)
4901592Sgirish 		return (EINVAL);
4911592Sgirish 
4921592Sgirish 	major = hsvcinfop->hsvc_major;
4931592Sgirish 	api_group = hsvcinfop->hsvc_group;
4941592Sgirish 
4951592Sgirish 	HSVC_DEBUG(DBG_HSVC_UNREGISTER,
4961592Sgirish 	    ("hsvc_unregister %p (0x%lx 0x%lx 0x%lx ID %s)\n",
4977240Srh87107 	    (void *)hsvcinfop, api_group, major, hsvcinfop->hsvc_minor,
4981592Sgirish 	    hsvcinfop->hsvc_modname));
4991592Sgirish 
5001592Sgirish 	/*
5011592Sgirish 	 * Search for the matching entry and return EINVAL if no match found.
5021592Sgirish 	 * Otherwise, remove it from our list and unregister the API
5031592Sgirish 	 * group if this was the last reference to that API group.
5041592Sgirish 	 */
5051592Sgirish 	mutex_enter(&hsvc_lock);
5061592Sgirish 
5071592Sgirish 	for (hsvcpp = &hsvc_groups; (hsvcp = *hsvcpp) != NULL;
5081592Sgirish 	    hsvcpp = &hsvcp->next) {
5091592Sgirish 		if (hsvcp->group != api_group || hsvcp->major != major)
5101592Sgirish 			continue;
5111592Sgirish 
5121592Sgirish 		/*
5131592Sgirish 		 * Search client list for a matching hsvcinfop entry
5141592Sgirish 		 * and unlink it and decrement refcnt, if found.
5151592Sgirish 		 */
5161592Sgirish 		if (hsvc_unlink_client(hsvcp, hsvcinfop) < 0) {
5171592Sgirish 			/* client not registered */
5181592Sgirish 			status = EINVAL;
5191592Sgirish 			break;
5201592Sgirish 		}
5211592Sgirish 
5221592Sgirish 		/*
5231592Sgirish 		 * Client has been unlinked. If this was the last
5241592Sgirish 		 * reference, unregister API group via OBP CIF
5251592Sgirish 		 * interface.
5261592Sgirish 		 */
5271592Sgirish 		if (hsvcp->refcnt == 0) {
5281592Sgirish 			uint64_t	hvstat;
5291592Sgirish 
5301592Sgirish 			ASSERT(hsvcp->clients == NULL);
5311592Sgirish 			hvstat = prom_set_sun4v_api_version(api_group, 0, 0,
5321592Sgirish 			    &supported_minor);
5331592Sgirish 
5341592Sgirish 			HSVC_DEBUG(DBG_HSVC_OBP_CIF,
5351592Sgirish 			    (" prom unreg group: 0x%lx hvstat: 0x%lx\n",
5361592Sgirish 			    api_group, hvstat));
5371592Sgirish 
5381592Sgirish 			/*
5391592Sgirish 			 * Note that the call to unnegotiate an API group
5401592Sgirish 			 * may fail if anyone, including OBP, is using
5411592Sgirish 			 * those services. However, the caller is done
5421592Sgirish 			 * with this API group and should be allowed to
5431592Sgirish 			 * unregister regardless of the outcome.
5441592Sgirish 			 */
5451592Sgirish 			*hsvcpp = hsvcp->next;
5461592Sgirish 			hsvc_free(hsvcp);
5471592Sgirish 		}
5481592Sgirish 		break;
5491592Sgirish 	}
5501592Sgirish 
5511592Sgirish 	if (hsvcp == NULL)
5521592Sgirish 		status = EINVAL;
5531592Sgirish 
5541592Sgirish 	mutex_exit(&hsvc_lock);
5551592Sgirish 
5561592Sgirish 	HSVC_DEBUG(DBG_HSVC_UNREGISTER,
5577240Srh87107 	    ("hsvc_unregister %p status: %d\n", (void *)hsvcinfop, status));
5581592Sgirish 
5591592Sgirish 	return (status);
5601592Sgirish }
5611592Sgirish 
5621592Sgirish 
5631592Sgirish /*
5641592Sgirish  * Get negotiated major/minor version number for an API group
5651592Sgirish  */
5661592Sgirish int
hsvc_version(uint64_t api_group,uint64_t * majorp,uint64_t * minorp)5671592Sgirish hsvc_version(uint64_t api_group, uint64_t *majorp, uint64_t *minorp)
5681592Sgirish {
5691592Sgirish 	int status = 0;
5701592Sgirish 	uint64_t hvstat;
5711592Sgirish 	hsvc_t	*hsvcp;
5721592Sgirish 
5731592Sgirish 	/*
5741592Sgirish 	 * Check if the specified api_group is already in use.
5751592Sgirish 	 * If so, return currently negotiated major/minor number.
5761592Sgirish 	 * Otherwise, call OBP CIF interface to get the currently
5771592Sgirish 	 * negotiated major/minor number.
5781592Sgirish 	 */
5791592Sgirish 	mutex_enter(&hsvc_lock);
5801592Sgirish 	for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next)
5811592Sgirish 		if (hsvcp->group == api_group)
5821592Sgirish 			break;
5831592Sgirish 
5841592Sgirish 	if (hsvcp) {
5851592Sgirish 		*majorp = hsvcp->major;
5861592Sgirish 		*minorp = hsvcp->minor;
5871592Sgirish 	} else {
5881592Sgirish 		hvstat = prom_get_sun4v_api_version(api_group, majorp, minorp);
5891592Sgirish 
5901592Sgirish 		switch (hvstat) {
5911592Sgirish 		case H_EBADTRAP:
5921592Sgirish 			/*
5931592Sgirish 			 * Older firmware does not support OBP CIF
5941592Sgirish 			 * interface. If it's a pre-versioning group,
5951592Sgirish 			 * then return default major/minor (i.e. 1/0).
5961592Sgirish 			 * Otherwise, return EINVAL.
5971592Sgirish 			 */
5981592Sgirish 			if (pre_versioning_group(api_group)) {
5991592Sgirish 				*majorp = 1;
6001592Sgirish 				*minorp = 0;
6011592Sgirish 			} else
6021592Sgirish 				status = EINVAL;
6031592Sgirish 			break;
6041592Sgirish 
6051592Sgirish 		case H_EINVAL:
6061592Sgirish 		default:
6071592Sgirish 			status = EINVAL;
6081592Sgirish 			break;
6091592Sgirish 
6101592Sgirish 		}
6111592Sgirish 	}
6121592Sgirish 	mutex_exit(&hsvc_lock);
6131592Sgirish 
6141592Sgirish 	HSVC_DEBUG(DBG_HSVC_VERSION,
6151592Sgirish 	    ("hsvc_version(0x%lx) status: %d major: 0x%lx minor: 0x%lx\n",
6161592Sgirish 	    api_group, status, *majorp, *minorp));
6171592Sgirish 
6181592Sgirish 	return (status);
6191592Sgirish }
6201592Sgirish 
6211592Sgirish /*
6221592Sgirish  * Initialize framework data structures
6231592Sgirish  */
6241592Sgirish void
hsvc_init(void)6251592Sgirish hsvc_init(void)
6261592Sgirish {
6271592Sgirish 	int		i;
6281592Sgirish 	hsvc_t		*hsvcp;
6291592Sgirish 
6301592Sgirish 	/*
6311592Sgirish 	 * Initialize global data structures
6321592Sgirish 	 */
6331592Sgirish 	mutex_init(&hsvc_lock, NULL, MUTEX_DEFAULT, NULL);
6341592Sgirish 	hsvc_groups = NULL;
6351592Sgirish 	hsvc_avail = NULL;
6361592Sgirish 
6371592Sgirish 	/*
6381592Sgirish 	 * Setup initial free list
6391592Sgirish 	 */
6401592Sgirish 	mutex_enter(&hsvc_lock);
6411592Sgirish 	for (i = 0, hsvcp = &hsvc_resv_bufs[0];
6421592Sgirish 	    i < HSVC_RESV_BUFS_MAX; i++, hsvcp++)
6431592Sgirish 		hsvc_free(hsvcp);
6441592Sgirish 	mutex_exit(&hsvc_lock);
6451592Sgirish }
6461592Sgirish 
6471592Sgirish 
6481592Sgirish /*
6491592Sgirish  * Hypervisor services to be negotiated at boot time.
6501592Sgirish  *
6511592Sgirish  * Note that the kernel needs to negotiate the HSVC_GROUP_SUN4V
6521592Sgirish  * API group first, before doing any other negotiation. Also, it
6531592Sgirish  * uses hypervisor services belonging to the HSVC_GROUP_CORE API
6541592Sgirish  * group only for itself.
6551592Sgirish  *
6562109Slm66018  * Note that the HSVC_GROUP_DIAG is negotiated on behalf of
6572109Slm66018  * any driver/module using DIAG services.
6581592Sgirish  */
6594423Sjb145095 typedef struct hsvc_info_unix_s {
6604423Sjb145095 	hsvc_info_t	hsvcinfo;
6614423Sjb145095 	int		required;
6624423Sjb145095 } hsvc_info_unix_t;
6634423Sjb145095 
6644423Sjb145095 static hsvc_info_unix_t  hsvcinfo_unix[] = {
6654423Sjb145095 	{{HSVC_REV_1, NULL,	HSVC_GROUP_SUN4V,	1,	0, NULL}, 1},
66611172SHaik.Aftandilian@Sun.COM 	{{HSVC_REV_1, NULL,	HSVC_GROUP_CORE,	1,	2, NULL}, 1},
6674423Sjb145095 	{{HSVC_REV_1, NULL,	HSVC_GROUP_DIAG,	1,	0, NULL}, 1},
6684423Sjb145095 	{{HSVC_REV_1, NULL,	HSVC_GROUP_INTR,	1,	0, NULL}, 0},
669*11304SJanie.Lu@Sun.COM 	{{HSVC_REV_1, NULL,	HSVC_GROUP_REBOOT_DATA,	1,	0, NULL}, 0},
6701592Sgirish };
6711592Sgirish 
6721592Sgirish #define	HSVCINFO_UNIX_CNT	(sizeof (hsvcinfo_unix) / sizeof (hsvc_info_t))
6731592Sgirish static char	*hsvcinfo_unix_modname = "unix";
6741592Sgirish 
6751592Sgirish /*
6761592Sgirish  * Initialize framework and register hypervisor services to be used
6771592Sgirish  * by the kernel.
6781592Sgirish  */
6791592Sgirish void
hsvc_setup()6801592Sgirish hsvc_setup()
6811592Sgirish {
6824423Sjb145095 	int			i, status;
6834423Sjb145095 	uint64_t		sup_minor;
6844423Sjb145095 	hsvc_info_unix_t	*hsvcinfop;
6851592Sgirish 
6861592Sgirish 	/*
6871592Sgirish 	 * Initialize framework
6881592Sgirish 	 */
6891592Sgirish 	hsvc_init();
6901592Sgirish 
6911592Sgirish 	/*
6921592Sgirish 	 * Negotiate versioning for required groups
6931592Sgirish 	 */
6941592Sgirish 	for (hsvcinfop = &hsvcinfo_unix[0], i = 0; i < HSVCINFO_UNIX_CNT;
6951592Sgirish 	    i++, hsvcinfop++) {
6964423Sjb145095 		hsvcinfop->hsvcinfo.hsvc_private = NULL;
6974423Sjb145095 		hsvcinfop->hsvcinfo.hsvc_modname = hsvcinfo_unix_modname;
6984423Sjb145095 		status = hsvc_register(&(hsvcinfop->hsvcinfo), &sup_minor);
6991592Sgirish 
7004423Sjb145095 		if ((status != 0) && hsvcinfop->required) {
7011592Sgirish 			cmn_err(CE_PANIC, "%s: cannot negotiate hypervisor "
7021592Sgirish 			    "services - group: 0x%lx major: 0x%lx minor: 0x%lx"
7034423Sjb145095 			    " errno: %d\n", hsvcinfop->hsvcinfo.hsvc_modname,
7044423Sjb145095 			    hsvcinfop->hsvcinfo.hsvc_group,
7054423Sjb145095 			    hsvcinfop->hsvcinfo.hsvc_major,
7064423Sjb145095 			    hsvcinfop->hsvcinfo.hsvc_minor, status);
7071592Sgirish 		}
7081592Sgirish 	}
7091592Sgirish 	HSVC_DUMP();
7101592Sgirish }
711