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