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