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