1c56c1e58Sgirish /* 2c56c1e58Sgirish * CDDL HEADER START 3c56c1e58Sgirish * 4c56c1e58Sgirish * The contents of this file are subject to the terms of the 5c56c1e58Sgirish * Common Development and Distribution License (the "License"). 6c56c1e58Sgirish * You may not use this file except in compliance with the License. 7c56c1e58Sgirish * 8c56c1e58Sgirish * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9c56c1e58Sgirish * or http://www.opensolaris.org/os/licensing. 10c56c1e58Sgirish * See the License for the specific language governing permissions 11c56c1e58Sgirish * and limitations under the License. 12c56c1e58Sgirish * 13c56c1e58Sgirish * When distributing Covered Code, include this CDDL HEADER in each 14c56c1e58Sgirish * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15c56c1e58Sgirish * If applicable, add the following below this CDDL HEADER, with the 16c56c1e58Sgirish * fields enclosed by brackets "[]" replaced with your own identifying 17c56c1e58Sgirish * information: Portions Copyright [yyyy] [name of copyright owner] 18c56c1e58Sgirish * 19c56c1e58Sgirish * CDDL HEADER END 20c56c1e58Sgirish */ 211ae08745Sheppo 22c56c1e58Sgirish /* 23*023e71deSHaik 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 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 * 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 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 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 * 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 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 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 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 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 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 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 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}, 666*023e71deSHaik 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}, 669c56c1e58Sgirish }; 670c56c1e58Sgirish 671c56c1e58Sgirish #define HSVCINFO_UNIX_CNT (sizeof (hsvcinfo_unix) / sizeof (hsvc_info_t)) 672c56c1e58Sgirish static char *hsvcinfo_unix_modname = "unix"; 673c56c1e58Sgirish 674c56c1e58Sgirish /* 675c56c1e58Sgirish * Initialize framework and register hypervisor services to be used 676c56c1e58Sgirish * by the kernel. 677c56c1e58Sgirish */ 678c56c1e58Sgirish void 679c56c1e58Sgirish hsvc_setup() 680c56c1e58Sgirish { 681c56c1e58Sgirish int i, status; 682c56c1e58Sgirish uint64_t sup_minor; 683d66f8315Sjb145095 hsvc_info_unix_t *hsvcinfop; 684c56c1e58Sgirish 685c56c1e58Sgirish /* 686c56c1e58Sgirish * Initialize framework 687c56c1e58Sgirish */ 688c56c1e58Sgirish hsvc_init(); 689c56c1e58Sgirish 690c56c1e58Sgirish /* 691c56c1e58Sgirish * Negotiate versioning for required groups 692c56c1e58Sgirish */ 693c56c1e58Sgirish for (hsvcinfop = &hsvcinfo_unix[0], i = 0; i < HSVCINFO_UNIX_CNT; 694c56c1e58Sgirish i++, hsvcinfop++) { 695d66f8315Sjb145095 hsvcinfop->hsvcinfo.hsvc_private = NULL; 696d66f8315Sjb145095 hsvcinfop->hsvcinfo.hsvc_modname = hsvcinfo_unix_modname; 697d66f8315Sjb145095 status = hsvc_register(&(hsvcinfop->hsvcinfo), &sup_minor); 698c56c1e58Sgirish 699d66f8315Sjb145095 if ((status != 0) && hsvcinfop->required) { 700c56c1e58Sgirish cmn_err(CE_PANIC, "%s: cannot negotiate hypervisor " 701c56c1e58Sgirish "services - group: 0x%lx major: 0x%lx minor: 0x%lx" 702d66f8315Sjb145095 " errno: %d\n", hsvcinfop->hsvcinfo.hsvc_modname, 703d66f8315Sjb145095 hsvcinfop->hsvcinfo.hsvc_group, 704d66f8315Sjb145095 hsvcinfop->hsvcinfo.hsvc_major, 705d66f8315Sjb145095 hsvcinfop->hsvcinfo.hsvc_minor, status); 706c56c1e58Sgirish } 707c56c1e58Sgirish } 708c56c1e58Sgirish HSVC_DUMP(); 709c56c1e58Sgirish } 710