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 */ 211592Sgirish /* 221592Sgirish * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 231592Sgirish * Use is subject to license terms. 241592Sgirish */ 251592Sgirish 261592Sgirish #pragma ident "%Z%%M% %I% %E% SMI" 271592Sgirish 281592Sgirish #include <sys/types.h> 291592Sgirish #include <sys/dditypes.h> 301592Sgirish #include <sys/machsystm.h> 311592Sgirish #include <sys/archsystm.h> 321592Sgirish #include <sys/prom_plat.h> 331592Sgirish #include <sys/promif.h> 341592Sgirish #include <sys/kmem.h> 351592Sgirish #include <sys/hypervisor_api.h> 361592Sgirish #include <sys/hsvc.h> 371592Sgirish 381592Sgirish #ifdef DEBUG 391592Sgirish 401592Sgirish int hsvc_debug = 0; /* HSVC debug flags */ 411592Sgirish 421592Sgirish /* 431592Sgirish * Flags to control HSVC debugging 441592Sgirish */ 451592Sgirish #define DBG_HSVC_REGISTER 0x0001 461592Sgirish #define DBG_HSVC_UNREGISTER 0x0002 471592Sgirish #define DBG_HSVC_OBP_CIF 0x0004 481592Sgirish #define DBG_HSVC_ALLOC 0x0008 491592Sgirish #define DBG_HSVC_VERSION 0x0010 501592Sgirish #define DBG_HSVC_REFCNT 0x0020 511592Sgirish #define DBG_HSVC_SETUP 0x0040 521592Sgirish 531592Sgirish #define HSVC_CHK_REFCNT(hsvcp) \ 541592Sgirish if (hsvc_debug & DBG_HSVC_REFCNT) hsvc_chk_refcnt(hsvcp) 551592Sgirish 561592Sgirish #define HSVC_DEBUG(flag, ARGS) \ 571592Sgirish if (hsvc_debug & flag) prom_printf ARGS 581592Sgirish 591592Sgirish #define HSVC_DUMP() \ 601592Sgirish if (hsvc_debug & DBG_HSVC_SETUP) hsvc_dump() 611592Sgirish 621592Sgirish #else /* DEBUG */ 631592Sgirish 641592Sgirish #define HSVC_CHK_REFCNT(hsvcp) 651592Sgirish #define HSVC_DEBUG(flag, args) 661592Sgirish #define HSVC_DUMP() 671592Sgirish 681592Sgirish #endif /* DEBUG */ 691592Sgirish 701592Sgirish /* 711592Sgirish * Each hypervisor API group negotiation is tracked via a 721592Sgirish * hsvc structure. This structure contains the API group, 731592Sgirish * currently negotiated major/minor number, a singly linked 741592Sgirish * list of clients currently registered and a reference count. 751592Sgirish * 761592Sgirish * Since the number of API groups is fairly small, negotiated 771592Sgirish * API groups are maintained via a singly linked list. Also, 781592Sgirish * sufficient free space is reserved to allow for API group 791592Sgirish * registration before kmem_xxx interface can be used to 801592Sgirish * allocate memory dynamically. 811592Sgirish * 821592Sgirish * Note that all access to the API group lookup and negotiation 831592Sgirish * is serialized to support strict HV API interface. 841592Sgirish */ 851592Sgirish 861592Sgirish typedef struct hsvc { 871592Sgirish struct hsvc *next; /* next group/free entry */ 881592Sgirish uint64_t group; /* hypervisor service group */ 891592Sgirish uint64_t major; /* major number */ 901592Sgirish uint64_t minor; /* minor number */ 911592Sgirish uint64_t refcnt; /* reference count */ 921592Sgirish hsvc_info_t *clients; /* linked list of clients */ 931592Sgirish } hsvc_t; 941592Sgirish 951592Sgirish 961592Sgirish /* 971592Sgirish * Global variables 981592Sgirish */ 991592Sgirish hsvc_t *hsvc_groups; /* linked list of API groups in use */ 1001592Sgirish hsvc_t *hsvc_avail; /* free reserved buffers */ 1011592Sgirish kmutex_t hsvc_lock; /* protects linked list and globals */ 1021592Sgirish 1031592Sgirish /* 1041592Sgirish * Preallocate some space for boot requirements (before kmem_xxx can be 1051592Sgirish * used) 1061592Sgirish */ 1071592Sgirish #define HSVC_RESV_BUFS_MAX 16 1081592Sgirish hsvc_t hsvc_resv_bufs[HSVC_RESV_BUFS_MAX]; 1091592Sgirish 1101592Sgirish /* 1111592Sgirish * Pre-versioning groups (not negotiated by Ontario/Erie FCS release) 1121592Sgirish */ 1131592Sgirish static uint64_t hsvc_pre_versioning_groups[] = { 1141592Sgirish HSVC_GROUP_SUN4V, 1151592Sgirish HSVC_GROUP_CORE, 1161592Sgirish HSVC_GROUP_VPCI, 1171592Sgirish HSVC_GROUP_VSC, 1181592Sgirish HSVC_GROUP_NIAGARA_CPU, 1191592Sgirish HSVC_GROUP_NCS, 1201592Sgirish HSVC_GROUP_DIAG 1211592Sgirish }; 1221592Sgirish 1231592Sgirish #define HSVC_PRE_VERSIONING_GROUP_CNT \ 1241592Sgirish (sizeof (hsvc_pre_versioning_groups) / sizeof (uint64_t)) 1251592Sgirish 1261592Sgirish static boolean_t 1271592Sgirish pre_versioning_group(uint64_t api_group) 1281592Sgirish { 1291592Sgirish int i; 1301592Sgirish 1311592Sgirish for (i = 0; i < HSVC_PRE_VERSIONING_GROUP_CNT; i++) 1321592Sgirish if (hsvc_pre_versioning_groups[i] == api_group) 1331592Sgirish return (B_TRUE); 1341592Sgirish return (B_FALSE); 1351592Sgirish } 1361592Sgirish 1371592Sgirish static hsvc_t * 1381592Sgirish hsvc_lookup(hsvc_info_t *hsvcinfop) 1391592Sgirish { 1401592Sgirish hsvc_t *hsvcp; 1411592Sgirish hsvc_info_t *p; 1421592Sgirish 1431592Sgirish for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) { 1441592Sgirish for (p = hsvcp->clients; p != NULL; 1451592Sgirish p = (hsvc_info_t *)p->hsvc_private) 1461592Sgirish if (p == hsvcinfop) 1471592Sgirish break; 1481592Sgirish if (p) 1491592Sgirish break; 1501592Sgirish } 1511592Sgirish 1521592Sgirish return (hsvcp); 1531592Sgirish } 1541592Sgirish 1551592Sgirish #ifdef DEBUG 1561592Sgirish 1571592Sgirish /* 1581592Sgirish * Check client reference count 1591592Sgirish */ 1601592Sgirish static void 1611592Sgirish hsvc_chk_refcnt(hsvc_t *hsvcp) 1621592Sgirish { 1631592Sgirish int refcnt; 1641592Sgirish hsvc_info_t *p; 1651592Sgirish 1661592Sgirish for (refcnt = 0, p = hsvcp->clients; p != NULL; 1671592Sgirish p = (hsvc_info_t *)p->hsvc_private) 1681592Sgirish refcnt++; 1691592Sgirish 1701592Sgirish ASSERT(hsvcp->refcnt == refcnt); 1711592Sgirish } 1721592Sgirish 1731592Sgirish /* 1741592Sgirish * Dump registered clients information 1751592Sgirish */ 1761592Sgirish static void 1771592Sgirish hsvc_dump(void) 1781592Sgirish { 1791592Sgirish hsvc_t *hsvcp; 1801592Sgirish hsvc_info_t *p; 1811592Sgirish 1821592Sgirish mutex_enter(&hsvc_lock); 1831592Sgirish 1841592Sgirish prom_printf("hsvc_dump: hsvc_groups: %p hsvc_avail: %p\n", 1851592Sgirish hsvc_groups, hsvc_avail); 1861592Sgirish 1871592Sgirish for (hsvcp = hsvc_groups; hsvcp != NULL; hsvcp = hsvcp->next) { 188*1717Swesolows prom_printf(" hsvcp: %p (0x%lx 0x%lx 0x%lx) ref: %ld clients: " 1891592Sgirish "%p\n", hsvcp, hsvcp->group, hsvcp->major, hsvcp->minor, 1901592Sgirish hsvcp->refcnt, hsvcp->clients); 1911592Sgirish 1921592Sgirish for (p = hsvcp->clients; p != NULL; 1931592Sgirish p = (hsvc_info_t *)p->hsvc_private) { 1941592Sgirish prom_printf(" client %p (0x%lx 0x%lx 0x%lx '%s') " 1951592Sgirish "private: %p\n", p, p->hsvc_group, p->hsvc_major, 1961592Sgirish p->hsvc_minor, p->hsvc_modname, 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 * 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", 2241592Sgirish hsvc_avail, hsvcp)); 2251592Sgirish } else 2261592Sgirish hsvcp = NULL; 2271592Sgirish return (hsvcp); 2281592Sgirish } 2291592Sgirish 2301592Sgirish static void 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", 2431592Sgirish hsvc_avail, 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 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 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 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, 3091592Sgirish ("hsvc_register %p (0x%lx 0x%lx 0x%lx ID %s)\n", 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, 4721592Sgirish ("hsvc_register(%p) status; %d sup_minor: 0x%lx\n", hsvcinfop, 4731592Sgirish status, *supported_minor)); 4741592Sgirish 4751592Sgirish return (status); 4761592Sgirish } 4771592Sgirish 4781592Sgirish /* 4791592Sgirish * Unregister an API group usage 4801592Sgirish */ 4811592Sgirish int 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", 4971592Sgirish 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, 5571592Sgirish ("hsvc_unregister %p status: %d\n", hsvcinfop, status)); 5581592Sgirish 5591592Sgirish return (status); 5601592Sgirish } 5611592Sgirish 5621592Sgirish 5631592Sgirish /* 5641592Sgirish * Get negotiated major/minor version number for an API group 5651592Sgirish */ 5661592Sgirish int 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 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 * 6561592Sgirish * Rest of the API groups are currently negotiated on behalf 6571617Sgovinda * of the pcitool, glvc and Niagara crypto support. In 6581592Sgirish * future, when these drivers are modified to do the negotiation 6591592Sgirish * themselves, corresponding entry should be removed from the 6601592Sgirish * table below. 6611592Sgirish */ 6621592Sgirish static hsvc_info_t hsvcinfo_unix[] = { 6631592Sgirish {HSVC_REV_1, NULL, HSVC_GROUP_SUN4V, 1, 0, NULL}, 6641592Sgirish {HSVC_REV_1, NULL, HSVC_GROUP_CORE, 1, 0, NULL}, 6651592Sgirish {HSVC_REV_1, NULL, HSVC_GROUP_VSC, 1, 0, NULL}, 6661592Sgirish {HSVC_REV_1, NULL, HSVC_GROUP_DIAG, 1, 0, NULL}, 6671592Sgirish {HSVC_REV_1, NULL, HSVC_GROUP_NCS, 1, 0, NULL} 6681592Sgirish }; 6691592Sgirish 6701592Sgirish #define HSVCINFO_UNIX_CNT (sizeof (hsvcinfo_unix) / sizeof (hsvc_info_t)) 6711592Sgirish static char *hsvcinfo_unix_modname = "unix"; 6721592Sgirish 6731592Sgirish /* 6741592Sgirish * Initialize framework and register hypervisor services to be used 6751592Sgirish * by the kernel. 6761592Sgirish */ 6771592Sgirish void 6781592Sgirish hsvc_setup() 6791592Sgirish { 6801592Sgirish int i, status; 6811592Sgirish uint64_t sup_minor; 6821592Sgirish hsvc_info_t *hsvcinfop; 6831592Sgirish 6841592Sgirish /* 6851592Sgirish * Initialize framework 6861592Sgirish */ 6871592Sgirish hsvc_init(); 6881592Sgirish 6891592Sgirish /* 6901592Sgirish * Negotiate versioning for required groups 6911592Sgirish */ 6921592Sgirish for (hsvcinfop = &hsvcinfo_unix[0], i = 0; i < HSVCINFO_UNIX_CNT; 6931592Sgirish i++, hsvcinfop++) { 6941592Sgirish hsvcinfop->hsvc_private = NULL; 6951592Sgirish hsvcinfop->hsvc_modname = hsvcinfo_unix_modname; 6961592Sgirish status = hsvc_register(hsvcinfop, &sup_minor); 6971592Sgirish 6981592Sgirish if (status != 0) { 6991592Sgirish cmn_err(CE_PANIC, "%s: cannot negotiate hypervisor " 7001592Sgirish "services - group: 0x%lx major: 0x%lx minor: 0x%lx" 7011592Sgirish " errno: %d\n", hsvcinfop->hsvc_modname, 7021592Sgirish hsvcinfop->hsvc_group, hsvcinfop->hsvc_major, 7031592Sgirish hsvcinfop->hsvc_minor, status); 7041592Sgirish } 7051592Sgirish } 7061592Sgirish HSVC_DUMP(); 7071592Sgirish } 708