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