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