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