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