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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*
  26  * Copyright (c) 2010, Intel Corporation.
  27  * All rights reserved.
  28  */
  29 
  30 #include <sys/types.h>
  31 #include <sys/cmn_err.h>
  32 #include <sys/conf.h>
  33 #include <sys/debug.h>
  34 #include <sys/errno.h>
  35 #include <sys/note.h>
  36 #include <sys/dditypes.h>
  37 #include <sys/ddi.h>
  38 #include <sys/sunddi.h>
  39 #include <sys/sunndi.h>
  40 #include <sys/ddi_impldefs.h>
  41 #include <sys/ndi_impldefs.h>
  42 #include <sys/varargs.h>
  43 #include <sys/modctl.h>
  44 #include <sys/kmem.h>
  45 #include <sys/cpuvar.h>
  46 #include <sys/cpupart.h>
  47 #include <sys/mem_config.h>
  48 #include <sys/mem_cage.h>
  49 #include <sys/memnode.h>
  50 #include <sys/callb.h>
  51 #include <sys/ontrap.h>
  52 #include <sys/obpdefs.h>
  53 #include <sys/promif.h>
  54 #include <sys/synch.h>
  55 #include <sys/systm.h>
  56 #include <sys/sysmacros.h>
  57 #include <sys/archsystm.h>
  58 #include <sys/machsystm.h>
  59 #include <sys/x_call.h>
  60 #include <sys/x86_archext.h>
  61 #include <sys/fastboot_impl.h>
  62 #include <sys/sysevent.h>
  63 #include <sys/sysevent/dr.h>
  64 #include <sys/sysevent/eventdefs.h>
  65 #include <sys/acpi/acpi.h>
  66 #include <sys/acpica.h>
  67 #include <sys/acpidev.h>
  68 #include <sys/acpidev_rsc.h>
  69 #include <sys/acpidev_dr.h>
  70 #include <sys/dr.h>
  71 #include <sys/dr_util.h>
  72 #include <sys/drmach.h>
  73 #include "drmach_acpi.h"
  74 
  75 /* utility */
  76 #define MBYTE           (1048576ull)
  77 #define _ptob64(p)      ((uint64_t)(p) << PAGESHIFT)
  78 #define _b64top(b)      ((pgcnt_t)((b) >> PAGESHIFT))
  79 
  80 static int              drmach_init(void);
  81 static void             drmach_fini(void);
  82 static int              drmach_name2type_idx(char *);
  83 static sbd_error_t      *drmach_mem_update_lgrp(drmachid_t);
  84 
  85 static void drmach_board_dispose(drmachid_t id);
  86 static sbd_error_t *drmach_board_release(drmachid_t);
  87 static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
  88 
  89 static void drmach_io_dispose(drmachid_t);
  90 static sbd_error_t *drmach_io_release(drmachid_t);
  91 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
  92 
  93 static void drmach_cpu_dispose(drmachid_t);
  94 static sbd_error_t *drmach_cpu_release(drmachid_t);
  95 static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
  96 
  97 static void drmach_mem_dispose(drmachid_t);
  98 static sbd_error_t *drmach_mem_release(drmachid_t);
  99 static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
 100 
 101 #ifdef DEBUG
 102 int drmach_debug = 1;            /* set to non-zero to enable debug messages */
 103 #endif /* DEBUG */
 104 
 105 drmach_domain_info_t     drmach_domain;
 106 
 107 static char             *drmach_ie_fmt = "drmach_acpi.c %d";
 108 static drmach_array_t   *drmach_boards;
 109 
 110 /* rwlock to protect drmach_boards. */
 111 static krwlock_t         drmach_boards_rwlock;
 112 
 113 /* rwlock to block out CPR thread. */
 114 static krwlock_t         drmach_cpr_rwlock;
 115 
 116 /* CPR callb id. */
 117 static callb_id_t        drmach_cpr_cid;
 118 
 119 static struct {
 120         const char      *name;
 121         const char      *type;
 122         sbd_error_t     *(*new)(drmach_device_t *, drmachid_t *);
 123 } drmach_name2type[] = {
 124         { ACPIDEV_NODE_NAME_CPU,        DRMACH_DEVTYPE_CPU, drmach_cpu_new },
 125         { ACPIDEV_NODE_NAME_MEMORY,     DRMACH_DEVTYPE_MEM, drmach_mem_new },
 126         { ACPIDEV_NODE_NAME_PCI,        DRMACH_DEVTYPE_PCI, drmach_io_new  },
 127 };
 128 
 129 /*
 130  * drmach autoconfiguration data structures and interfaces
 131  */
 132 static struct modlmisc modlmisc = {
 133         &mod_miscops,
 134         "ACPI based DR v1.0"
 135 };
 136 
 137 static struct modlinkage modlinkage = {
 138         MODREV_1,
 139         (void *)&modlmisc,
 140         NULL
 141 };
 142 
 143 int
 144 _init(void)
 145 {
 146         int err;
 147 
 148         if ((err = drmach_init()) != 0) {
 149                 return (err);
 150         }
 151 
 152         if ((err = mod_install(&modlinkage)) != 0) {
 153                 drmach_fini();
 154         }
 155 
 156         return (err);
 157 }
 158 
 159 int
 160 _fini(void)
 161 {
 162         int     err;
 163 
 164         if ((err = mod_remove(&modlinkage)) == 0) {
 165                 drmach_fini();
 166         }
 167 
 168         return (err);
 169 }
 170 
 171 int
 172 _info(struct modinfo *modinfop)
 173 {
 174         return (mod_info(&modlinkage, modinfop));
 175 }
 176 
 177 /*
 178  * Internal support functions.
 179  */
 180 static DRMACH_HANDLE
 181 drmach_node_acpi_get_dnode(drmach_node_t *np)
 182 {
 183         return ((DRMACH_HANDLE)(uintptr_t)np->here);
 184 }
 185 
 186 static dev_info_t *
 187 drmach_node_acpi_get_dip(drmach_node_t *np)
 188 {
 189         dev_info_t *dip = NULL;
 190 
 191         if (ACPI_FAILURE(acpica_get_devinfo((DRMACH_HANDLE)(np->here), &dip))) {
 192                 return (NULL);
 193         }
 194 
 195         return (dip);
 196 }
 197 
 198 static int
 199 drmach_node_acpi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
 200 {
 201         int             rv = 0;
 202         DRMACH_HANDLE   hdl;
 203 
 204         hdl = np->get_dnode(np);
 205         if (hdl == NULL) {
 206                 DRMACH_PR("!drmach_node_acpi_get_prop: NULL handle");
 207                 rv = -1;
 208         } else {
 209                 rv = acpidev_dr_device_getprop(hdl, name, buf, len);
 210                 if (rv >= 0) {
 211                         ASSERT(rv <= len);
 212                         rv = 0;
 213                 }
 214         }
 215 
 216         return (rv);
 217 }
 218 
 219 static int
 220 drmach_node_acpi_get_proplen(drmach_node_t *np, char *name, int *len)
 221 {
 222         int             rv = 0;
 223         DRMACH_HANDLE   hdl;
 224 
 225         hdl = np->get_dnode(np);
 226         if (hdl == NULL) {
 227                 DRMACH_PR("!drmach_node_acpi_get_proplen: NULL handle");
 228                 rv = -1;
 229         } else {
 230                 rv = acpidev_dr_device_getprop(hdl, name, NULL, 0);
 231                 if (rv >= 0) {
 232                         *len = rv;
 233                         return (0);
 234                 }
 235         }
 236 
 237         return (-1);
 238 }
 239 
 240 static ACPI_STATUS
 241 drmach_node_acpi_callback(ACPI_HANDLE hdl, uint_t lvl, void *ctx, void **retval)
 242 {
 243         _NOTE(ARGUNUSED(lvl));
 244 
 245         int rv;
 246         dev_info_t *dip;
 247         drmach_node_walk_args_t *argp = ctx;
 248         int (*cb)(drmach_node_walk_args_t *args);
 249         acpidev_class_id_t clsid;
 250 
 251         ASSERT(hdl != NULL);
 252         ASSERT(ctx != NULL);
 253         ASSERT(retval != NULL);
 254 
 255         /* Skip subtree if the device is not powered. */
 256         if (!acpidev_dr_device_is_powered(hdl)) {
 257                 return (AE_CTRL_DEPTH);
 258         }
 259 
 260         /*
 261          * Keep scanning subtree if it fails to lookup device node.
 262          * There may be some ACPI objects without device nodes created.
 263          */
 264         if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) {
 265                 return (AE_OK);
 266         }
 267 
 268         argp->node->here = hdl;
 269         cb = (int (*)(drmach_node_walk_args_t *args))argp->func;
 270         rv = (*cb)(argp);
 271         argp->node->here = NULL;
 272         if (rv) {
 273                 *(int *)retval = rv;
 274                 return (AE_CTRL_TERMINATE);
 275         }
 276 
 277         /*
 278          * Skip descendants of PCI/PCIex host bridges.
 279          * PCI/PCIex devices will be handled by pcihp.
 280          */
 281         clsid = acpidev_dr_device_get_class(hdl);
 282         if (clsid == ACPIDEV_CLASS_ID_PCI || clsid == ACPIDEV_CLASS_ID_PCIEX) {
 283                 return (AE_CTRL_DEPTH);
 284         }
 285 
 286         return (AE_OK);
 287 }
 288 
 289 static int
 290 drmach_node_acpi_walk(drmach_node_t *np, void *data,
 291     int (*cb)(drmach_node_walk_args_t *args))
 292 {
 293         DRMACH_HANDLE           hdl;
 294         int                     rv = 0;
 295         drmach_node_walk_args_t args;
 296 
 297         /* initialize the args structure for callback */
 298         args.node = np;
 299         args.data = data;
 300         args.func = (void *)cb;
 301 
 302         /* save the handle, it will be modified when walking the tree. */
 303         hdl = np->get_dnode(np);
 304         if (hdl == NULL) {
 305                 DRMACH_PR("!drmach_node_acpi_walk: failed to get device node.");
 306                 return (EX86_INAPPROP);
 307         }
 308 
 309         if (ACPI_FAILURE(acpidev_dr_device_walk_device(hdl,
 310             ACPIDEV_MAX_ENUM_LEVELS, drmach_node_acpi_callback,
 311             &args, (void *)&rv))) {
 312                 /*
 313                  * If acpidev_dr_device_walk_device() itself fails, rv won't
 314                  * be set to suitable error code. Set it here.
 315                  */
 316                 if (rv == 0) {
 317                         cmn_err(CE_WARN, "!drmach_node_acpi_walk: failed to "
 318                             "walk ACPI namespace.");
 319                         rv = EX86_ACPIWALK;
 320                 }
 321         }
 322 
 323         /* restore the handle to original value after walking the tree. */
 324         np->here = (void *)hdl;
 325 
 326         return ((int)rv);
 327 }
 328 
 329 static drmach_node_t *
 330 drmach_node_new(void)
 331 {
 332         drmach_node_t *np;
 333 
 334         np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
 335 
 336         np->get_dnode = drmach_node_acpi_get_dnode;
 337         np->getdip = drmach_node_acpi_get_dip;
 338         np->getproplen = drmach_node_acpi_get_proplen;
 339         np->getprop = drmach_node_acpi_get_prop;
 340         np->walk = drmach_node_acpi_walk;
 341 
 342         return (np);
 343 }
 344 
 345 static drmachid_t
 346 drmach_node_dup(drmach_node_t *np)
 347 {
 348         drmach_node_t *dup;
 349 
 350         dup = drmach_node_new();
 351         dup->here = np->here;
 352         dup->get_dnode = np->get_dnode;
 353         dup->getdip = np->getdip;
 354         dup->getproplen = np->getproplen;
 355         dup->getprop = np->getprop;
 356         dup->walk = np->walk;
 357 
 358         return (dup);
 359 }
 360 
 361 static void
 362 drmach_node_dispose(drmach_node_t *np)
 363 {
 364         kmem_free(np, sizeof (*np));
 365 }
 366 
 367 static int
 368 drmach_node_walk(drmach_node_t *np, void *param,
 369         int (*cb)(drmach_node_walk_args_t *args))
 370 {
 371         return (np->walk(np, param, cb));
 372 }
 373 
 374 static DRMACH_HANDLE
 375 drmach_node_get_dnode(drmach_node_t *np)
 376 {
 377         return (np->get_dnode(np));
 378 }
 379 
 380 /*
 381  * drmach_array provides convenient array construction, access,
 382  * bounds checking and array destruction logic.
 383  */
 384 static drmach_array_t *
 385 drmach_array_new(uint_t min_index, uint_t max_index)
 386 {
 387         drmach_array_t *arr;
 388 
 389         arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
 390 
 391         arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
 392         if (arr->arr_sz > 0) {
 393                 arr->min_index = min_index;
 394                 arr->max_index = max_index;
 395 
 396                 arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
 397                 return (arr);
 398         } else {
 399                 kmem_free(arr, sizeof (*arr));
 400                 return (0);
 401         }
 402 }
 403 
 404 static int
 405 drmach_array_set(drmach_array_t *arr, uint_t idx, drmachid_t val)
 406 {
 407         if (idx < arr->min_index || idx > arr->max_index)
 408                 return (-1);
 409         arr->arr[idx - arr->min_index] = val;
 410         return (0);
 411 }
 412 
 413 /*
 414  * Get the item with index idx.
 415  * Return 0 with the value stored in val if succeeds, otherwise return -1.
 416  */
 417 static int
 418 drmach_array_get(drmach_array_t *arr, uint_t idx, drmachid_t *val)
 419 {
 420         if (idx < arr->min_index || idx > arr->max_index)
 421                 return (-1);
 422         *val = arr->arr[idx - arr->min_index];
 423         return (0);
 424 }
 425 
 426 static int
 427 drmach_array_first(drmach_array_t *arr, uint_t *idx, drmachid_t *val)
 428 {
 429         int rv;
 430 
 431         *idx = arr->min_index;
 432         while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
 433                 *idx += 1;
 434 
 435         return (rv);
 436 }
 437 
 438 static int
 439 drmach_array_next(drmach_array_t *arr, uint_t *idx, drmachid_t *val)
 440 {
 441         int rv;
 442 
 443         *idx += 1;
 444         while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
 445                 *idx += 1;
 446 
 447         return (rv);
 448 }
 449 
 450 static void
 451 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
 452 {
 453         drmachid_t      val;
 454         uint_t          idx;
 455         int             rv;
 456 
 457         rv = drmach_array_first(arr, &idx, &val);
 458         while (rv == 0) {
 459                 (*disposer)(val);
 460                 rv = drmach_array_next(arr, &idx, &val);
 461         }
 462 
 463         kmem_free(arr->arr, arr->arr_sz);
 464         kmem_free(arr, sizeof (*arr));
 465 }
 466 
 467 static drmach_board_t *
 468 drmach_get_board_by_bnum(uint_t bnum)
 469 {
 470         drmachid_t id;
 471 
 472         if (drmach_array_get(drmach_boards, bnum, &id) == 0)
 473                 return ((drmach_board_t *)id);
 474         else
 475                 return (NULL);
 476 }
 477 
 478 sbd_error_t *
 479 drmach_device_new(drmach_node_t *node,
 480         drmach_board_t *bp, int portid, drmachid_t *idp)
 481 {
 482         int              i;
 483         int              rv;
 484         drmach_device_t  proto;
 485         sbd_error_t     *err;
 486         char             name[OBP_MAXDRVNAME];
 487 
 488         rv = node->getprop(node, ACPIDEV_DR_PROP_DEVNAME, name, OBP_MAXDRVNAME);
 489         if (rv) {
 490                 /* every node is expected to have a name */
 491                 err = drerr_new(1, EX86_GETPROP, "device node %s: property %s",
 492                     ddi_node_name(node->getdip(node)),
 493                     ACPIDEV_DR_PROP_DEVNAME);
 494                 return (err);
 495         }
 496 
 497         /*
 498          * The node currently being examined is not listed in the name2type[]
 499          * array.  In this case, the node is no interest to drmach.  Both
 500          * dp and err are initialized here to yield nothing (no device or
 501          * error structure) for this case.
 502          */
 503         i = drmach_name2type_idx(name);
 504         if (i < 0) {
 505                 *idp = (drmachid_t)0;
 506                 return (NULL);
 507         }
 508 
 509         /* device specific new function will set unum */
 510         bzero(&proto, sizeof (proto));
 511         proto.type = drmach_name2type[i].type;
 512         proto.bp = bp;
 513         proto.node = node;
 514         proto.portid = portid;
 515 
 516         return (drmach_name2type[i].new(&proto, idp));
 517 }
 518 
 519 static void
 520 drmach_device_dispose(drmachid_t id)
 521 {
 522         drmach_device_t *self = id;
 523 
 524         self->cm.dispose(id);
 525 }
 526 
 527 static sbd_error_t *
 528 drmach_device_status(drmachid_t id, drmach_status_t *stat)
 529 {
 530         drmach_common_t *cp;
 531 
 532         if (!DRMACH_IS_ID(id))
 533                 return (drerr_new(0, EX86_NOTID, NULL));
 534         cp = id;
 535 
 536         return (cp->status(id, stat));
 537 }
 538 
 539 drmach_board_t *
 540 drmach_board_new(uint_t bnum, int boot_board)
 541 {
 542         sbd_error_t *err;
 543         drmach_board_t  *bp;
 544         dev_info_t *dip = NULL;
 545 
 546         bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
 547         bp->cm.isa = (void *)drmach_board_new;
 548         bp->cm.release = drmach_board_release;
 549         bp->cm.status = drmach_board_status;
 550 
 551         bp->bnum = bnum;
 552         bp->devices = NULL;
 553         bp->tree = drmach_node_new();
 554 
 555         acpidev_dr_lock_all();
 556         if (ACPI_FAILURE(acpidev_dr_get_board_handle(bnum, &bp->tree->here))) {
 557                 acpidev_dr_unlock_all();
 558                 drmach_board_dispose(bp);
 559                 return (NULL);
 560         }
 561         acpidev_dr_unlock_all();
 562         ASSERT(bp->tree->here != NULL);
 563 
 564         err = drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
 565         if (err != NULL) {
 566                 sbd_err_clear(&err);
 567                 drmach_board_dispose(bp);
 568                 return (NULL);
 569         }
 570 
 571         if (acpidev_dr_device_is_powered(bp->tree->here)) {
 572                 bp->boot_board = boot_board;
 573                 bp->powered = 1;
 574         } else {
 575                 bp->boot_board = 0;
 576                 bp->powered = 0;
 577         }
 578         bp->assigned = boot_board;
 579         if (ACPI_SUCCESS(acpica_get_devinfo(bp->tree->here, &dip))) {
 580                 bp->connected = 1;
 581         } else {
 582                 bp->connected = 0;
 583         }
 584 
 585         (void) drmach_array_set(drmach_boards, bnum, bp);
 586 
 587         return (bp);
 588 }
 589 
 590 static void
 591 drmach_board_dispose(drmachid_t id)
 592 {
 593         drmach_board_t *bp;
 594 
 595         ASSERT(DRMACH_IS_BOARD_ID(id));
 596         bp = id;
 597 
 598         if (bp->tree)
 599                 drmach_node_dispose(bp->tree);
 600 
 601         if (bp->devices)
 602                 drmach_array_dispose(bp->devices, drmach_device_dispose);
 603 
 604         kmem_free(bp, sizeof (drmach_board_t));
 605 }
 606 
 607 static sbd_error_t *
 608 drmach_board_release(drmachid_t id)
 609 {
 610         if (!DRMACH_IS_BOARD_ID(id))
 611                 return (drerr_new(0, EX86_INAPPROP, NULL));
 612 
 613         return (NULL);
 614 }
 615 
 616 static int
 617 drmach_board_check_power(drmach_board_t *bp)
 618 {
 619         DRMACH_HANDLE   hdl;
 620 
 621         hdl = drmach_node_get_dnode(bp->tree);
 622 
 623         return (acpidev_dr_device_is_powered(hdl));
 624 }
 625 
 626 struct drmach_board_list_dep_arg {
 627         int     count;
 628         size_t  len;
 629         ssize_t off;
 630         char    *buf;
 631         char    temp[MAXPATHLEN];
 632 };
 633 
 634 static ACPI_STATUS
 635 drmach_board_generate_name(ACPI_HANDLE hdl, UINT32 lvl, void *ctx,
 636     void **retval)
 637 {
 638         _NOTE(ARGUNUSED(retval));
 639 
 640         struct drmach_board_list_dep_arg *argp = ctx;
 641 
 642         ASSERT(hdl != NULL);
 643         ASSERT(lvl == UINT32_MAX);
 644         ASSERT(ctx != NULL);
 645 
 646         /* Skip non-board devices. */
 647         if (!acpidev_dr_device_is_board(hdl)) {
 648                 return (AE_OK);
 649         }
 650 
 651         if (ACPI_FAILURE(acpidev_dr_get_board_name(hdl, argp->temp,
 652             sizeof (argp->temp)))) {
 653                 DRMACH_PR("!drmach_board_generate_name: failed to "
 654                     "generate board name for handle %p.", hdl);
 655                 /* Keep on walking. */
 656                 return (AE_OK);
 657         }
 658         argp->count++;
 659         argp->off += snprintf(argp->buf + argp->off, argp->len - argp->off,
 660             " %s", argp->temp);
 661         if (argp->off >= argp->len) {
 662                 return (AE_CTRL_TERMINATE);
 663         }
 664 
 665         return (AE_OK);
 666 }
 667 
 668 static ssize_t
 669 drmach_board_list_dependency(ACPI_HANDLE hdl, boolean_t edl, char *prefix,
 670     char *buf, size_t len)
 671 {
 672         ACPI_STATUS rc;
 673         ssize_t off;
 674         struct drmach_board_list_dep_arg *ap;
 675 
 676         ASSERT(buf != NULL && len != 0);
 677         if (buf == NULL || len == 0) {
 678                 return (-1);
 679         }
 680 
 681         ap = kmem_zalloc(sizeof (*ap), KM_SLEEP);
 682         ap->buf = buf;
 683         ap->len = len;
 684         ap->off = snprintf(buf, len, "%s", prefix);
 685         if (ap->off >= len) {
 686                 *buf = '\0';
 687                 kmem_free(ap, sizeof (*ap));
 688                 return (-1);
 689         }
 690 
 691         /* Generate the device dependency list. */
 692         if (edl) {
 693                 rc = acpidev_dr_device_walk_edl(hdl,
 694                     drmach_board_generate_name, ap, NULL);
 695         } else {
 696                 rc = acpidev_dr_device_walk_ejd(hdl,
 697                     drmach_board_generate_name, ap, NULL);
 698         }
 699         if (ACPI_FAILURE(rc)) {
 700                 *buf = '\0';
 701                 ap->off = -1;
 702         /* No device has dependency on this board. */
 703         } else if (ap->count == 0) {
 704                 *buf = '\0';
 705                 ap->off = 0;
 706         }
 707 
 708         off = ap->off;
 709         kmem_free(ap, sizeof (*ap));
 710 
 711         return (off);
 712 }
 713 
 714 static sbd_error_t *
 715 drmach_board_status(drmachid_t id, drmach_status_t *stat)
 716 {
 717         sbd_error_t     *err = NULL;
 718         drmach_board_t  *bp;
 719         DRMACH_HANDLE   hdl;
 720         size_t          off;
 721 
 722         if (!DRMACH_IS_BOARD_ID(id))
 723                 return (drerr_new(0, EX86_INAPPROP, NULL));
 724         bp = id;
 725 
 726         if (bp->tree == NULL)
 727                 return (drerr_new(0, EX86_INAPPROP, NULL));
 728         hdl = drmach_node_get_dnode(bp->tree);
 729         if (hdl == NULL)
 730                 return (drerr_new(0, EX86_INAPPROP, NULL));
 731 
 732         stat->busy = 0;                      /* assume not busy */
 733         stat->configured = 0;                /* assume not configured */
 734         stat->assigned = bp->assigned;
 735         stat->powered = bp->powered = acpidev_dr_device_is_powered(hdl);
 736         stat->empty = !acpidev_dr_device_is_present(hdl);
 737         if (ACPI_SUCCESS(acpidev_dr_device_check_status(hdl))) {
 738                 stat->cond = bp->cond = SBD_COND_OK;
 739         } else {
 740                 stat->cond = bp->cond = SBD_COND_FAILED;
 741         }
 742         stat->info[0] = '\0';
 743 
 744         /* Generate the eject device list. */
 745         if (drmach_board_list_dependency(hdl, B_TRUE, "EDL:",
 746             stat->info, sizeof (stat->info)) < 0) {
 747                 DRMACH_PR("!drmach_board_status: failed to generate "
 748                     "eject device list for board %d.", bp->bnum);
 749                 stat->info[0] = '\0';
 750         }
 751         off = strlen(stat->info);
 752         if (off < sizeof (stat->info)) {
 753                 if (drmach_board_list_dependency(hdl, B_FALSE,
 754                     off ? ", EJD:" : "EJD:",
 755                     stat->info + off, sizeof (stat->info) - off) < 0) {
 756                         DRMACH_PR("!drmach_board_status: failed to generate "
 757                             "eject dependent device for board %d.", bp->bnum);
 758                         stat->info[off] = '\0';
 759                 }
 760         }
 761 
 762         switch (acpidev_dr_get_board_type(bp->tree->get_dnode(bp->tree))) {
 763         case ACPIDEV_CPU_BOARD:
 764                 (void) strlcpy(stat->type, "CPU Board", sizeof (stat->type));
 765                 break;
 766         case ACPIDEV_MEMORY_BOARD:
 767                 (void) strlcpy(stat->type, "MemoryBoard", sizeof (stat->type));
 768                 break;
 769         case ACPIDEV_IO_BOARD:
 770                 (void) strlcpy(stat->type, "IO Board", sizeof (stat->type));
 771                 break;
 772         case ACPIDEV_SYSTEM_BOARD:
 773                 /*FALLTHROUGH*/
 774         default:
 775                 (void) strlcpy(stat->type, "SystemBoard", sizeof (stat->type));
 776                 break;
 777         }
 778 
 779         if (bp->devices) {
 780                 int              rv;
 781                 uint_t           d_idx;
 782                 drmachid_t       d_id;
 783 
 784                 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
 785                 while (rv == 0) {
 786                         drmach_status_t d_stat;
 787 
 788                         err = drmach_device_status(d_id, &d_stat);
 789                         if (err)
 790                                 break;
 791 
 792                         stat->busy |= d_stat.busy;
 793                         stat->configured |= d_stat.configured;
 794 
 795                         rv = drmach_array_next(bp->devices, &d_idx, &d_id);
 796                 }
 797         }
 798 
 799         return (err);
 800 }
 801 
 802 /*
 803  * When DR is initialized, we walk the device tree and acquire a hold on
 804  * all the nodes that are interesting to DR. This is so that the corresponding
 805  * branches cannot be deleted.
 806  */
 807 static int
 808 drmach_hold_rele_devtree(dev_info_t *rdip, void *arg)
 809 {
 810         int *holdp = (int *)arg;
 811         ACPI_HANDLE hdl = NULL;
 812         acpidev_data_handle_t dhdl;
 813 
 814         /* Skip nodes and subtrees which are not created by acpidev. */
 815         if (ACPI_FAILURE(acpica_get_handle(rdip, &hdl))) {
 816                 return (DDI_WALK_PRUNECHILD);
 817         }
 818         ASSERT(hdl != NULL);
 819         dhdl = acpidev_data_get_handle(hdl);
 820         if (dhdl == NULL) {
 821                 return (DDI_WALK_PRUNECHILD);
 822         }
 823 
 824         /* Hold/release devices which are interesting to DR operations. */
 825         if (acpidev_data_dr_ready(dhdl)) {
 826                 if (*holdp) {
 827                         ASSERT(!e_ddi_branch_held(rdip));
 828                         e_ddi_branch_hold(rdip);
 829                 } else {
 830                         ASSERT(e_ddi_branch_held(rdip));
 831                         e_ddi_branch_rele(rdip);
 832                 }
 833         }
 834 
 835         return (DDI_WALK_CONTINUE);
 836 }
 837 
 838 static void
 839 drmach_hold_devtree(void)
 840 {
 841         dev_info_t *dip;
 842         int circ;
 843         int hold = 1;
 844 
 845         dip = ddi_root_node();
 846         ndi_devi_enter(dip, &circ);
 847         ddi_walk_devs(ddi_get_child(dip), drmach_hold_rele_devtree, &hold);
 848         ndi_devi_exit(dip, circ);
 849 }
 850 
 851 static void
 852 drmach_release_devtree(void)
 853 {
 854         dev_info_t *dip;
 855         int circ;
 856         int hold = 0;
 857 
 858         dip = ddi_root_node();
 859         ndi_devi_enter(dip, &circ);
 860         ddi_walk_devs(ddi_get_child(dip), drmach_hold_rele_devtree, &hold);
 861         ndi_devi_exit(dip, circ);
 862 }
 863 
 864 static boolean_t
 865 drmach_cpr_callb(void *arg, int code)
 866 {
 867         _NOTE(ARGUNUSED(arg));
 868 
 869         if (code == CB_CODE_CPR_CHKPT) {
 870                 /*
 871                  * Temporarily block CPR operations if there are DR operations
 872                  * ongoing.
 873                  */
 874                 rw_enter(&drmach_cpr_rwlock, RW_WRITER);
 875         } else {
 876                 rw_exit(&drmach_cpr_rwlock);
 877         }
 878 
 879         return (B_TRUE);
 880 }
 881 
 882 static int
 883 drmach_init(void)
 884 {
 885         DRMACH_HANDLE   hdl;
 886         drmachid_t      id;
 887         uint_t          bnum;
 888 
 889         if (MAX_BOARDS > SHRT_MAX) {
 890                 cmn_err(CE_WARN, "!drmach_init: system has too many (%d) "
 891                     "hotplug capable boards.", MAX_BOARDS);
 892                 return (ENXIO);
 893         } else if (MAX_CMP_UNITS_PER_BOARD > 1) {
 894                 cmn_err(CE_WARN, "!drmach_init: DR doesn't support multiple "
 895                     "(%d) physical processors on one board.",
 896                     MAX_CMP_UNITS_PER_BOARD);
 897                 return (ENXIO);
 898         } else if (MAX_CORES_PER_CMP & (MAX_CORES_PER_CMP - 1)) {
 899                 cmn_err(CE_WARN, "!drmach_init: number of logical CPUs (%d) in "
 900                     "physical processor is not power of 2.",
 901                     MAX_CORES_PER_CMP);
 902                 return (ENXIO);
 903         } else if (MAX_CPU_UNITS_PER_BOARD > DEVSET_CPU_NUMBER ||
 904             MAX_MEM_UNITS_PER_BOARD > DEVSET_MEM_NUMBER ||
 905             MAX_IO_UNITS_PER_BOARD > DEVSET_IO_NUMBER) {
 906                 cmn_err(CE_WARN, "!drmach_init: system has more CPU/memory/IO "
 907                     "units than the DR driver can handle.");
 908                 return (ENXIO);
 909         }
 910 
 911         rw_init(&drmach_cpr_rwlock, NULL, RW_DEFAULT, NULL);
 912         drmach_cpr_cid = callb_add(drmach_cpr_callb, NULL,
 913             CB_CL_CPR_PM, "drmach");
 914 
 915         rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
 916         drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
 917         drmach_domain.allow_dr = acpidev_dr_capable();
 918 
 919         for (bnum = 0; bnum < MAX_BOARDS; bnum++) {
 920                 hdl = NULL;
 921                 if (ACPI_FAILURE(acpidev_dr_get_board_handle(bnum, &hdl)) ||
 922                     hdl == NULL) {
 923                         cmn_err(CE_WARN, "!drmach_init: failed to lookup ACPI "
 924                             "handle for board %d.", bnum);
 925                         continue;
 926                 }
 927                 if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
 928                         DRMACH_PR("!drmach_init: failed to get handle "
 929                             "for board %d.", bnum);
 930                         ASSERT(0);
 931                         goto error;
 932                 } else if (id == NULL) {
 933                         (void) drmach_board_new(bnum, 1);
 934                 }
 935         }
 936 
 937         /*
 938          * Walk descendants of the devinfo root node and hold
 939          * all devinfo branches of interest.
 940          */
 941         drmach_hold_devtree();
 942 
 943         return (0);
 944 
 945 error:
 946         drmach_array_dispose(drmach_boards, drmach_board_dispose);
 947         rw_destroy(&drmach_boards_rwlock);
 948         rw_destroy(&drmach_cpr_rwlock);
 949         return (ENXIO);
 950 }
 951 
 952 static void
 953 drmach_fini(void)
 954 {
 955         rw_enter(&drmach_boards_rwlock, RW_WRITER);
 956         if (drmach_boards != NULL) {
 957                 drmach_array_dispose(drmach_boards, drmach_board_dispose);
 958                 drmach_boards = NULL;
 959         }
 960         rw_exit(&drmach_boards_rwlock);
 961 
 962         /*
 963          * Walk descendants of the root devinfo node
 964          * release holds acquired on branches in drmach_init()
 965          */
 966         drmach_release_devtree();
 967 
 968         (void) callb_delete(drmach_cpr_cid);
 969         rw_destroy(&drmach_cpr_rwlock);
 970         rw_destroy(&drmach_boards_rwlock);
 971 }
 972 
 973 sbd_error_t *
 974 drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
 975 {
 976         drmach_io_t     *ip;
 977         int             portid;
 978 
 979         portid = proto->portid;
 980         ASSERT(portid != -1);
 981         proto->unum = portid;
 982 
 983         ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
 984         bcopy(proto, &ip->dev, sizeof (ip->dev));
 985         ip->dev.node = drmach_node_dup(proto->node);
 986         ip->dev.cm.isa = (void *)drmach_io_new;
 987         ip->dev.cm.dispose = drmach_io_dispose;
 988         ip->dev.cm.release = drmach_io_release;
 989         ip->dev.cm.status = drmach_io_status;
 990         (void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
 991             ip->dev.type, ip->dev.unum);
 992 
 993         *idp = (drmachid_t)ip;
 994 
 995         return (NULL);
 996 }
 997 
 998 static void
 999 drmach_io_dispose(drmachid_t id)
1000 {
1001         drmach_io_t *self;
1002 
1003         ASSERT(DRMACH_IS_IO_ID(id));
1004 
1005         self = id;
1006         if (self->dev.node)
1007                 drmach_node_dispose(self->dev.node);
1008 
1009         kmem_free(self, sizeof (*self));
1010 }
1011 
1012 static sbd_error_t *
1013 drmach_io_release(drmachid_t id)
1014 {
1015         if (!DRMACH_IS_IO_ID(id))
1016                 return (drerr_new(0, EX86_INAPPROP, NULL));
1017 
1018         return (NULL);
1019 }
1020 
1021 static sbd_error_t *
1022 drmach_io_status(drmachid_t id, drmach_status_t *stat)
1023 {
1024         drmach_device_t *dp;
1025         sbd_error_t     *err;
1026         int              configured;
1027 
1028         ASSERT(DRMACH_IS_IO_ID(id));
1029         dp = id;
1030 
1031         err = drmach_io_is_attached(id, &configured);
1032         if (err)
1033                 return (err);
1034 
1035         stat->assigned = dp->bp->assigned;
1036         stat->powered = dp->bp->powered;
1037         stat->configured = (configured != 0);
1038         stat->busy = dp->busy;
1039         (void) strlcpy(stat->type, dp->type, sizeof (stat->type));
1040         stat->info[0] = '\0';
1041 
1042         return (NULL);
1043 }
1044 
1045 sbd_error_t *
1046 drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
1047 {
1048         int              portid;
1049         processorid_t    cpuid;
1050         drmach_cpu_t    *cp = NULL;
1051 
1052         /* the portid is APIC ID of the node */
1053         portid = proto->portid;
1054         ASSERT(portid != -1);
1055 
1056         /*
1057          * Assume all CPUs are homogeneous and have the same number of
1058          * cores/threads.
1059          */
1060         proto->unum = portid % MAX_CPU_UNITS_PER_BOARD;
1061 
1062         cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
1063         bcopy(proto, &cp->dev, sizeof (cp->dev));
1064         cp->dev.node = drmach_node_dup(proto->node);
1065         cp->dev.cm.isa = (void *)drmach_cpu_new;
1066         cp->dev.cm.dispose = drmach_cpu_dispose;
1067         cp->dev.cm.release = drmach_cpu_release;
1068         cp->dev.cm.status = drmach_cpu_status;
1069         (void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
1070             cp->dev.type, cp->dev.unum);
1071 
1072         cp->apicid = portid;
1073         if (ACPI_SUCCESS(acpica_get_cpu_id_by_object(
1074             drmach_node_get_dnode(proto->node), &cpuid))) {
1075                 cp->cpuid = cpuid;
1076         } else {
1077                 cp->cpuid = -1;
1078         }
1079 
1080         /* Mark CPU0 as busy, many other components have dependency on it. */
1081         if (cp->cpuid == 0) {
1082                 cp->dev.busy = 1;
1083         }
1084 
1085         *idp = (drmachid_t)cp;
1086 
1087         return (NULL);
1088 }
1089 
1090 static void
1091 drmach_cpu_dispose(drmachid_t id)
1092 {
1093         drmach_cpu_t    *self;
1094 
1095         ASSERT(DRMACH_IS_CPU_ID(id));
1096 
1097         self = id;
1098         if (self->dev.node)
1099                 drmach_node_dispose(self->dev.node);
1100 
1101         kmem_free(self, sizeof (*self));
1102 }
1103 
1104 static sbd_error_t *
1105 drmach_cpu_release(drmachid_t id)
1106 {
1107         if (!DRMACH_IS_CPU_ID(id))
1108                 return (drerr_new(0, EX86_INAPPROP, NULL));
1109 
1110         return (NULL);
1111 }
1112 
1113 static sbd_error_t *
1114 drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
1115 {
1116         drmach_cpu_t *cp;
1117         drmach_device_t *dp;
1118 
1119         ASSERT(DRMACH_IS_CPU_ID(id));
1120         cp = (drmach_cpu_t *)id;
1121         dp = &cp->dev;
1122 
1123         stat->assigned = dp->bp->assigned;
1124         stat->powered = dp->bp->powered;
1125         mutex_enter(&cpu_lock);
1126         stat->configured = (cpu_get(cp->cpuid) != NULL);
1127         mutex_exit(&cpu_lock);
1128         stat->busy = dp->busy;
1129         (void) strlcpy(stat->type, dp->type, sizeof (stat->type));
1130         stat->info[0] = '\0';
1131 
1132         return (NULL);
1133 }
1134 
1135 static int
1136 drmach_setup_mc_info(DRMACH_HANDLE hdl, drmach_mem_t *mp)
1137 {
1138         uint_t i, j, count;
1139         struct memlist  *ml = NULL, *ml2 = NULL;
1140         acpidev_regspec_t *regp;
1141         uint64_t align, addr_min, addr_max, total_size, skipped_size;
1142 
1143         if (hdl == NULL) {
1144                 return (-1);
1145         } else if (ACPI_FAILURE(acpidev_dr_get_mem_alignment(hdl, &align))) {
1146                 return (-1);
1147         } else {
1148                 ASSERT((align & (align - 1)) == 0);
1149                 mp->mem_alignment = align;
1150         }
1151 
1152         addr_min = UINT64_MAX;
1153         addr_max = 0;
1154         total_size = 0;
1155         skipped_size = 0;
1156         /*
1157          * There's a memory hole just below 4G on x86, which needs special
1158          * handling. All other addresses assigned to a specific memory device
1159          * should be contiguous.
1160          */
1161         if (ACPI_FAILURE(acpidev_dr_device_get_regspec(hdl, TRUE, &regp,
1162             &count))) {
1163                 return (-1);
1164         }
1165         for (i = 0, j = 0; i < count; i++) {
1166                 uint64_t        addr, size;
1167 
1168                 addr  = (uint64_t)regp[i].phys_mid << 32;
1169                 addr |= (uint64_t)regp[i].phys_low;
1170                 size  = (uint64_t)regp[i].size_hi << 32;
1171                 size |= (uint64_t)regp[i].size_low;
1172                 if (size == 0)
1173                         continue;
1174                 else
1175                         j++;
1176 
1177                 total_size += size;
1178                 if (addr < addr_min)
1179                         addr_min = addr;
1180                 if (addr + size > addr_max)
1181                         addr_max = addr + size;
1182                 if (mp->dev.bp->boot_board ||
1183                     j <= acpidev_dr_max_segments_per_mem_device()) {
1184                         ml = memlist_add_span(ml, addr, size);
1185                 } else {
1186                         skipped_size += size;
1187                 }
1188         }
1189         acpidev_dr_device_free_regspec(regp, count);
1190 
1191         if (skipped_size != 0) {
1192                 cmn_err(CE_WARN, "!drmach: too many (%d) segments on memory "
1193                     "device, max (%d) segments supported, 0x%" PRIx64 " bytes "
1194                     "of memory skipped.",
1195                     j, acpidev_dr_max_segments_per_mem_device(), skipped_size);
1196         }
1197 
1198         mp->slice_base = addr_min;
1199         mp->slice_top = addr_max;
1200         mp->slice_size = total_size;
1201 
1202         if (mp->dev.bp->boot_board) {
1203                 uint64_t endpa = _ptob64(physmax + 1);
1204 
1205                 /*
1206                  * we intersect phys_install to get base_pa.
1207                  * This only works at boot-up time.
1208                  */
1209                 memlist_read_lock();
1210                 ml2 = memlist_dup(phys_install);
1211                 memlist_read_unlock();
1212 
1213                 ml2 = memlist_del_span(ml2, 0ull, mp->slice_base);
1214                 if (ml2 && endpa > addr_max) {
1215                         ml2 = memlist_del_span(ml2, addr_max, endpa - addr_max);
1216                 }
1217         }
1218 
1219         /*
1220          * Create a memlist for the memory board.
1221          * The created memlist only contains configured memory if there's
1222          * configured memory on the board, otherwise it contains all memory
1223          * on the board.
1224          */
1225         if (ml2) {
1226                 uint64_t nbytes = 0;
1227                 struct memlist *p;
1228 
1229                 for (p = ml2; p; p = p->ml_next) {
1230                         nbytes += p->ml_size;
1231                 }
1232                 if (nbytes == 0) {
1233                         memlist_delete(ml2);
1234                         ml2 = NULL;
1235                 } else {
1236                         /* Node has configured memory at boot time. */
1237                         mp->base_pa = ml2->ml_address;
1238                         mp->nbytes = nbytes;
1239                         mp->memlist = ml2;
1240                         if (ml)
1241                                 memlist_delete(ml);
1242                 }
1243         }
1244         if (ml2 == NULL) {
1245                 /* Not configured at boot time. */
1246                 mp->base_pa = UINT64_MAX;
1247                 mp->nbytes = 0;
1248                 mp->memlist = ml;
1249         }
1250 
1251         return (0);
1252 }
1253 
1254 sbd_error_t *
1255 drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
1256 {
1257         DRMACH_HANDLE   hdl;
1258         drmach_mem_t    *mp;
1259         int             portid;
1260 
1261         mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
1262         portid = proto->portid;
1263         ASSERT(portid != -1);
1264         proto->unum = portid;
1265 
1266         bcopy(proto, &mp->dev, sizeof (mp->dev));
1267         mp->dev.node = drmach_node_dup(proto->node);
1268         mp->dev.cm.isa = (void *)drmach_mem_new;
1269         mp->dev.cm.dispose = drmach_mem_dispose;
1270         mp->dev.cm.release = drmach_mem_release;
1271         mp->dev.cm.status = drmach_mem_status;
1272 
1273         (void) snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s%d",
1274             mp->dev.type, proto->unum);
1275         hdl = mp->dev.node->get_dnode(mp->dev.node);
1276         ASSERT(hdl != NULL);
1277         if (drmach_setup_mc_info(hdl, mp) != 0) {
1278                 kmem_free(mp, sizeof (drmach_mem_t));
1279                 *idp = (drmachid_t)NULL;
1280                 return (drerr_new(1, EX86_MC_SETUP, NULL));
1281         }
1282 
1283         /* make sure we do not create memoryless nodes */
1284         if (mp->nbytes == 0 && mp->slice_size == 0) {
1285                 kmem_free(mp, sizeof (drmach_mem_t));
1286                 *idp = (drmachid_t)NULL;
1287         } else
1288                 *idp = (drmachid_t)mp;
1289 
1290         return (NULL);
1291 }
1292 
1293 static void
1294 drmach_mem_dispose(drmachid_t id)
1295 {
1296         drmach_mem_t *mp;
1297 
1298         ASSERT(DRMACH_IS_MEM_ID(id));
1299 
1300         mp = id;
1301 
1302         if (mp->dev.node)
1303                 drmach_node_dispose(mp->dev.node);
1304 
1305         if (mp->memlist) {
1306                 memlist_delete(mp->memlist);
1307                 mp->memlist = NULL;
1308         }
1309 
1310         kmem_free(mp, sizeof (*mp));
1311 }
1312 
1313 static sbd_error_t *
1314 drmach_mem_release(drmachid_t id)
1315 {
1316         if (!DRMACH_IS_MEM_ID(id))
1317                 return (drerr_new(0, EX86_INAPPROP, NULL));
1318 
1319         return (NULL);
1320 }
1321 
1322 static sbd_error_t *
1323 drmach_mem_status(drmachid_t id, drmach_status_t *stat)
1324 {
1325         uint64_t         pa;
1326         drmach_mem_t    *dp;
1327         struct memlist  *ml = NULL;
1328 
1329         ASSERT(DRMACH_IS_MEM_ID(id));
1330         dp = id;
1331 
1332         /* get starting physical address of target memory */
1333         pa = dp->base_pa;
1334         /* round down to slice boundary */
1335         pa &= ~(dp->mem_alignment - 1);
1336 
1337         /* stop at first span that is in slice */
1338         memlist_read_lock();
1339         for (ml = phys_install; ml; ml = ml->ml_next)
1340                 if (ml->ml_address >= pa && ml->ml_address < dp->slice_top)
1341                         break;
1342         memlist_read_unlock();
1343 
1344         stat->assigned = dp->dev.bp->assigned;
1345         stat->powered = dp->dev.bp->powered;
1346         stat->configured = (ml != NULL);
1347         stat->busy = dp->dev.busy;
1348         (void) strlcpy(stat->type, dp->dev.type, sizeof (stat->type));
1349         stat->info[0] = '\0';
1350 
1351         return (NULL);
1352 }
1353 
1354 /*
1355  * Public interfaces exported to support platform independent dr driver.
1356  */
1357 uint_t
1358 drmach_max_boards(void)
1359 {
1360         return (acpidev_dr_max_boards());
1361 }
1362 
1363 uint_t
1364 drmach_max_io_units_per_board(void)
1365 {
1366         return (acpidev_dr_max_io_units_per_board());
1367 }
1368 
1369 uint_t
1370 drmach_max_cmp_units_per_board(void)
1371 {
1372         return (acpidev_dr_max_cmp_units_per_board());
1373 }
1374 
1375 uint_t
1376 drmach_max_mem_units_per_board(void)
1377 {
1378         return (acpidev_dr_max_mem_units_per_board());
1379 }
1380 
1381 uint_t
1382 drmach_max_core_per_cmp(void)
1383 {
1384         return (acpidev_dr_max_cpu_units_per_cmp());
1385 }
1386 
1387 sbd_error_t *
1388 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts, void *argp)
1389 {
1390         drmach_board_t  *bp = (drmach_board_t *)id;
1391         sbd_error_t     *err = NULL;
1392 
1393         /* allow status and ncm operations to always succeed */
1394         if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
1395                 return (NULL);
1396         }
1397 
1398         switch (cmd) {
1399         case SBD_CMD_POWERON:
1400         case SBD_CMD_POWEROFF:
1401                 /*
1402                  * Disable fast reboot if CPU/MEM/IOH hotplug event happens.
1403                  * Note: this is a temporary solution and will be revised when
1404                  * fast reboot can support CPU/MEM/IOH DR operations in future.
1405                  *
1406                  * ACPI BIOS generates some static ACPI tables, such as MADT,
1407                  * SRAT and SLIT, to describe system hardware configuration on
1408                  * power-on. When CPU/MEM/IOH hotplug event happens, those
1409                  * static tables won't be updated and will become stale.
1410                  *
1411                  * If we reset system by fast reboot, BIOS will have no chance
1412                  * to regenerate those staled static tables. Fast reboot can't
1413                  * tolerate such inconsistency between staled ACPI tables and
1414                  * real hardware configuration yet.
1415                  *
1416                  * A temporary solution is introduced to disable fast reboot if
1417                  * CPU/MEM/IOH hotplug event happens. This solution should be
1418                  * revised when fast reboot is enhanced to support CPU/MEM/IOH
1419                  * DR operations.
1420                  */
1421                 fastreboot_disable(FBNS_HOTPLUG);
1422                 /*FALLTHROUGH*/
1423 
1424         default:
1425                 /* Block out the CPR thread. */
1426                 rw_enter(&drmach_cpr_rwlock, RW_READER);
1427                 break;
1428         }
1429 
1430         /* check all other commands for the required option string */
1431         if ((opts->size > 0) && (opts->copts != NULL)) {
1432                 if (strstr(opts->copts, ACPIDEV_CMD_OST_PREFIX) == NULL) {
1433                         err = drerr_new(1, EX86_SUPPORT, NULL);
1434                 }
1435         } else {
1436                 err = drerr_new(1, EX86_SUPPORT, NULL);
1437         }
1438 
1439         if (!err && id && DRMACH_IS_BOARD_ID(id)) {
1440                 switch (cmd) {
1441                 case SBD_CMD_TEST:
1442                         break;
1443                 case SBD_CMD_CONNECT:
1444                         if (bp->connected)
1445                                 err = drerr_new(0, ESBD_STATE, NULL);
1446                         else if (!drmach_domain.allow_dr)
1447                                 err = drerr_new(1, EX86_SUPPORT, NULL);
1448                         break;
1449                 case SBD_CMD_DISCONNECT:
1450                         if (!bp->connected)
1451                                 err = drerr_new(0, ESBD_STATE, NULL);
1452                         else if (!drmach_domain.allow_dr)
1453                                 err = drerr_new(1, EX86_SUPPORT, NULL);
1454                         break;
1455                 default:
1456                         if (!drmach_domain.allow_dr)
1457                                 err = drerr_new(1, EX86_SUPPORT, NULL);
1458                         break;
1459 
1460                 }
1461         }
1462 
1463         /*
1464          * CPU/memory/IO DR operations will be supported in stages on x86.
1465          * With early versions, some operations should be blocked here.
1466          * This temporary hook will be removed when all CPU/memory/IO DR
1467          * operations are supported on x86 systems.
1468          *
1469          * We only need to filter unsupported device types for
1470          * SBD_CMD_CONFIGURE/SBD_CMD_UNCONFIGURE commands, all other
1471          * commands are supported by all device types.
1472          */
1473         if (!err && (cmd == SBD_CMD_CONFIGURE || cmd == SBD_CMD_UNCONFIGURE)) {
1474                 int             i;
1475                 dr_devset_t     *devsetp = (dr_devset_t *)argp;
1476                 dr_devset_t     devset = *devsetp;
1477 
1478                 switch (cmd) {
1479                 case SBD_CMD_CONFIGURE:
1480                         if (!plat_dr_support_cpu()) {
1481                                 DEVSET_DEL(devset, SBD_COMP_CPU,
1482                                     DEVSET_ANYUNIT);
1483                         } else {
1484                                 for (i = MAX_CPU_UNITS_PER_BOARD;
1485                                     i < DEVSET_CPU_NUMBER; i++) {
1486                                         DEVSET_DEL(devset, SBD_COMP_CPU, i);
1487                                 }
1488                         }
1489 
1490                         if (!plat_dr_support_memory()) {
1491                                 DEVSET_DEL(devset, SBD_COMP_MEM,
1492                                     DEVSET_ANYUNIT);
1493                         } else {
1494                                 for (i = MAX_MEM_UNITS_PER_BOARD;
1495                                     i < DEVSET_MEM_NUMBER; i++) {
1496                                         DEVSET_DEL(devset, SBD_COMP_MEM, i);
1497                                 }
1498                         }
1499 
1500                         /* No support of configuring IOH devices yet. */
1501                         DEVSET_DEL(devset, SBD_COMP_IO, DEVSET_ANYUNIT);
1502                         break;
1503 
1504                 case SBD_CMD_UNCONFIGURE:
1505                         if (!plat_dr_support_cpu()) {
1506                                 DEVSET_DEL(devset, SBD_COMP_CPU,
1507                                     DEVSET_ANYUNIT);
1508                         } else {
1509                                 for (i = MAX_CPU_UNITS_PER_BOARD;
1510                                     i < DEVSET_CPU_NUMBER; i++) {
1511                                         DEVSET_DEL(devset, SBD_COMP_CPU, i);
1512                                 }
1513                         }
1514 
1515                         /* No support of unconfiguring MEM/IOH devices yet. */
1516                         DEVSET_DEL(devset, SBD_COMP_MEM, DEVSET_ANYUNIT);
1517                         DEVSET_DEL(devset, SBD_COMP_IO, DEVSET_ANYUNIT);
1518                         break;
1519                 }
1520 
1521                 *devsetp = devset;
1522                 if (DEVSET_IS_NULL(devset)) {
1523                         err = drerr_new(1, EX86_SUPPORT, NULL);
1524                 }
1525         }
1526 
1527         return (err);
1528 }
1529 
1530 sbd_error_t *
1531 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts, int rv)
1532 {
1533         _NOTE(ARGUNUSED(id, opts, rv));
1534 
1535         switch (cmd) {
1536         case SBD_CMD_STATUS:
1537         case SBD_CMD_GETNCM:
1538                 break;
1539 
1540         default:
1541                 rw_exit(&drmach_cpr_rwlock);
1542                 break;
1543         }
1544 
1545         return (NULL);
1546 }
1547 
1548 sbd_error_t *
1549 drmach_configure(drmachid_t id, int flags)
1550 {
1551         _NOTE(ARGUNUSED(flags));
1552 
1553         drmach_device_t         *dp;
1554         sbd_error_t             *err = NULL;
1555         dev_info_t              *rdip;
1556         dev_info_t              *fdip = NULL;
1557 
1558         if (!DRMACH_IS_DEVICE_ID(id))
1559                 return (drerr_new(0, EX86_INAPPROP, NULL));
1560         dp = id;
1561 
1562         rdip = dp->node->getdip(dp->node);
1563         ASSERT(rdip);
1564         ASSERT(e_ddi_branch_held(rdip));
1565 
1566         /* allocate cpu id for the CPU device. */
1567         if (DRMACH_IS_CPU_ID(id)) {
1568                 DRMACH_HANDLE hdl = drmach_node_get_dnode(dp->node);
1569                 ASSERT(hdl != NULL);
1570                 if (ACPI_FAILURE(acpidev_dr_allocate_cpuid(hdl, NULL))) {
1571                         err = drerr_new(1, EX86_ALLOC_CPUID, NULL);
1572                 }
1573                 return (err);
1574         }
1575 
1576         if (DRMACH_IS_MEM_ID(id)) {
1577                 err = drmach_mem_update_lgrp(id);
1578                 if (err)
1579                         return (err);
1580         }
1581 
1582         if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
1583                 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1584                 dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
1585 
1586                 (void) ddi_pathname(dip, path);
1587                 err = drerr_new(1, EX86_DRVFAIL, path);
1588                 kmem_free(path, MAXPATHLEN);
1589 
1590                 /* If non-NULL, fdip is returned held and must be released */
1591                 if (fdip != NULL)
1592                         ddi_release_devi(fdip);
1593         }
1594 
1595         return (err);
1596 }
1597 
1598 sbd_error_t *
1599 drmach_unconfigure(drmachid_t id, int flags)
1600 {
1601         _NOTE(ARGUNUSED(flags));
1602 
1603         drmach_device_t *dp;
1604         sbd_error_t     *err = NULL;
1605         dev_info_t      *rdip, *fdip = NULL;
1606 
1607         if (!DRMACH_IS_DEVICE_ID(id))
1608                 return (drerr_new(0, EX86_INAPPROP, NULL));
1609         dp = id;
1610 
1611         rdip = dp->node->getdip(dp->node);
1612         ASSERT(rdip);
1613         ASSERT(e_ddi_branch_held(rdip));
1614 
1615         if (DRMACH_IS_CPU_ID(id)) {
1616                 DRMACH_HANDLE hdl = drmach_node_get_dnode(dp->node);
1617                 ASSERT(hdl != NULL);
1618                 if (ACPI_FAILURE(acpidev_dr_free_cpuid(hdl))) {
1619                         err = drerr_new(1, EX86_FREE_CPUID, NULL);
1620                 }
1621                 return (err);
1622         }
1623 
1624         /*
1625          * Note: FORCE flag is no longer necessary under devfs
1626          */
1627         if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) {
1628                 char            *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1629 
1630                 /*
1631                  * If non-NULL, fdip is returned held and must be released.
1632                  */
1633                 if (fdip != NULL) {
1634                         (void) ddi_pathname(fdip, path);
1635                         ndi_rele_devi(fdip);
1636                 } else {
1637                         (void) ddi_pathname(rdip, path);
1638                 }
1639 
1640                 err = drerr_new(1, EX86_DRVFAIL, path);
1641 
1642                 kmem_free(path, MAXPATHLEN);
1643         }
1644 
1645         return (err);
1646 }
1647 
1648 sbd_error_t *
1649 drmach_get_dip(drmachid_t id, dev_info_t **dip)
1650 {
1651         drmach_device_t *dp;
1652 
1653         if (!DRMACH_IS_DEVICE_ID(id))
1654                 return (drerr_new(0, EX86_INAPPROP, NULL));
1655         dp = id;
1656 
1657         *dip = dp->node->getdip(dp->node);
1658 
1659         return (NULL);
1660 }
1661 
1662 sbd_error_t *
1663 drmach_release(drmachid_t id)
1664 {
1665         drmach_common_t *cp;
1666 
1667         if (!DRMACH_IS_DEVICE_ID(id))
1668                 return (drerr_new(0, EX86_INAPPROP, NULL));
1669         cp = id;
1670 
1671         return (cp->release(id));
1672 }
1673 
1674 sbd_error_t *
1675 drmach_status(drmachid_t id, drmach_status_t *stat)
1676 {
1677         drmach_common_t *cp;
1678         sbd_error_t     *err;
1679 
1680         rw_enter(&drmach_boards_rwlock, RW_READER);
1681         if (!DRMACH_IS_ID(id)) {
1682                 rw_exit(&drmach_boards_rwlock);
1683                 return (drerr_new(0, EX86_NOTID, NULL));
1684         }
1685         cp = (drmach_common_t *)id;
1686         err = cp->status(id, stat);
1687         rw_exit(&drmach_boards_rwlock);
1688 
1689         return (err);
1690 }
1691 
1692 static sbd_error_t *
1693 drmach_update_acpi_status(drmachid_t id, drmach_opts_t *opts)
1694 {
1695         char            *copts;
1696         drmach_board_t  *bp;
1697         DRMACH_HANDLE   hdl;
1698         int             event, code;
1699         boolean_t       inprogress = B_FALSE;
1700 
1701         if (DRMACH_NULL_ID(id) || !DRMACH_IS_BOARD_ID(id))
1702                 return (drerr_new(0, EX86_INAPPROP, NULL));
1703         bp = (drmach_board_t *)id;
1704         hdl = drmach_node_get_dnode(bp->tree);
1705         ASSERT(hdl != NULL);
1706         if (hdl == NULL)
1707                 return (drerr_new(0, EX86_INAPPROP, NULL));
1708 
1709         /* Get the status code. */
1710         copts = opts->copts;
1711         if (strncmp(copts, ACPIDEV_CMD_OST_INPROGRESS,
1712             strlen(ACPIDEV_CMD_OST_INPROGRESS)) == 0) {
1713                 inprogress = B_TRUE;
1714                 code = ACPI_OST_STA_INSERT_IN_PROGRESS;
1715                 copts += strlen(ACPIDEV_CMD_OST_INPROGRESS);
1716         } else if (strncmp(copts, ACPIDEV_CMD_OST_SUCCESS,
1717             strlen(ACPIDEV_CMD_OST_SUCCESS)) == 0) {
1718                 code = ACPI_OST_STA_SUCCESS;
1719                 copts += strlen(ACPIDEV_CMD_OST_SUCCESS);
1720         } else if (strncmp(copts, ACPIDEV_CMD_OST_FAILURE,
1721             strlen(ACPIDEV_CMD_OST_FAILURE)) == 0) {
1722                 code = ACPI_OST_STA_FAILURE;
1723                 copts += strlen(ACPIDEV_CMD_OST_FAILURE);
1724         } else if (strncmp(copts, ACPIDEV_CMD_OST_NOOP,
1725             strlen(ACPIDEV_CMD_OST_NOOP)) == 0) {
1726                 return (NULL);
1727         } else {
1728                 return (drerr_new(0, EX86_UNKPTCMD, opts->copts));
1729         }
1730 
1731         /* Get the event type. */
1732         copts = strstr(copts, ACPIDEV_EVENT_TYPE_ATTR_NAME);
1733         if (copts == NULL) {
1734                 return (drerr_new(0, EX86_UNKPTCMD, opts->copts));
1735         }
1736         copts += strlen(ACPIDEV_EVENT_TYPE_ATTR_NAME);
1737         if (copts[0] != '=') {
1738                 return (drerr_new(0, EX86_UNKPTCMD, opts->copts));
1739         }
1740         copts += strlen("=");
1741         if (strncmp(copts, ACPIDEV_EVENT_TYPE_BUS_CHECK,
1742             strlen(ACPIDEV_EVENT_TYPE_BUS_CHECK)) == 0) {
1743                 event = ACPI_NOTIFY_BUS_CHECK;
1744         } else if (strncmp(copts, ACPIDEV_EVENT_TYPE_DEVICE_CHECK,
1745             strlen(ACPIDEV_EVENT_TYPE_DEVICE_CHECK)) == 0) {
1746                 event = ACPI_NOTIFY_DEVICE_CHECK;
1747         } else if (strncmp(copts, ACPIDEV_EVENT_TYPE_DEVICE_CHECK_LIGHT,
1748             strlen(ACPIDEV_EVENT_TYPE_DEVICE_CHECK_LIGHT)) == 0) {
1749                 event = ACPI_NOTIFY_DEVICE_CHECK_LIGHT;
1750         } else if (strncmp(copts, ACPIDEV_EVENT_TYPE_EJECT_REQUEST,
1751             strlen(ACPIDEV_EVENT_TYPE_EJECT_REQUEST)) == 0) {
1752                 event = ACPI_NOTIFY_EJECT_REQUEST;
1753                 if (inprogress) {
1754                         code = ACPI_OST_STA_EJECT_IN_PROGRESS;
1755                 }
1756         } else {
1757                 return (drerr_new(0, EX86_UNKPTCMD, opts->copts));
1758         }
1759 
1760         (void) acpidev_eval_ost(hdl, event, code, NULL, 0);
1761 
1762         return (NULL);
1763 }
1764 
1765 static struct {
1766         const char      *name;
1767         sbd_error_t     *(*handler)(drmachid_t id, drmach_opts_t *opts);
1768 } drmach_pt_arr[] = {
1769         { ACPIDEV_CMD_OST_PREFIX,       &drmach_update_acpi_status  },
1770         /* the following line must always be last */
1771         { NULL,                         NULL                            }
1772 };
1773 
1774 sbd_error_t *
1775 drmach_passthru(drmachid_t id, drmach_opts_t *opts)
1776 {
1777         int             i;
1778         sbd_error_t     *err;
1779 
1780         i = 0;
1781         while (drmach_pt_arr[i].name != NULL) {
1782                 int len = strlen(drmach_pt_arr[i].name);
1783 
1784                 if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
1785                         break;
1786 
1787                 i += 1;
1788         }
1789 
1790         if (drmach_pt_arr[i].name == NULL)
1791                 err = drerr_new(0, EX86_UNKPTCMD, opts->copts);
1792         else
1793                 err = (*drmach_pt_arr[i].handler)(id, opts);
1794 
1795         return (err);
1796 }
1797 
1798 /*
1799  * Board specific interfaces to support dr driver
1800  */
1801 static int
1802 drmach_get_portid(drmach_node_t *np)
1803 {
1804         uint32_t        portid;
1805 
1806         if (np->getprop(np, ACPIDEV_DR_PROP_PORTID,
1807             &portid, sizeof (portid)) == 0) {
1808                 /*
1809                  * acpidev returns portid as uint32_t, validates it.
1810                  */
1811                 if (portid > INT_MAX) {
1812                         return (-1);
1813                 } else {
1814                         return (portid);
1815                 }
1816         }
1817 
1818         return (-1);
1819 }
1820 
1821 /*
1822  * This is a helper function to determine if a given
1823  * node should be considered for a dr operation according
1824  * to predefined dr type nodes and the node's name.
1825  * Formal Parameter : The name of a device node.
1826  * Return Value: -1, name does not map to a valid dr type.
1827  *               A value greater or equal to 0, name is a valid dr type.
1828  */
1829 static int
1830 drmach_name2type_idx(char *name)
1831 {
1832         int     index, ntypes;
1833 
1834         if (name == NULL)
1835                 return (-1);
1836 
1837         /*
1838          * Determine how many possible types are currently supported
1839          * for dr.
1840          */
1841         ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
1842 
1843         /* Determine if the node's name correspond to a predefined type. */
1844         for (index = 0; index < ntypes; index++) {
1845                 if (strcmp(drmach_name2type[index].name, name) == 0)
1846                         /* The node is an allowed type for dr. */
1847                         return (index);
1848         }
1849 
1850         /*
1851          * If the name of the node does not map to any of the
1852          * types in the array drmach_name2type then the node is not of
1853          * interest to dr.
1854          */
1855         return (-1);
1856 }
1857 
1858 static int
1859 drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
1860 {
1861         drmach_node_t                   *node = args->node;
1862         drmach_board_cb_data_t          *data = args->data;
1863         drmach_board_t                  *obj = data->obj;
1864 
1865         int             rv, portid;
1866         uint32_t        bnum;
1867         drmachid_t      id;
1868         drmach_device_t *device;
1869         char            name[OBP_MAXDRVNAME];
1870 
1871         portid = drmach_get_portid(node);
1872         rv = node->getprop(node, ACPIDEV_DR_PROP_DEVNAME,
1873             name, OBP_MAXDRVNAME);
1874         if (rv)
1875                 return (0);
1876 
1877         rv = node->getprop(node, ACPIDEV_DR_PROP_BOARDNUM,
1878             &bnum, sizeof (bnum));
1879         if (rv) {
1880                 return (0);
1881         }
1882         if (bnum > INT_MAX) {
1883                 return (0);
1884         }
1885 
1886         if (bnum != obj->bnum)
1887                 return (0);
1888 
1889         if (drmach_name2type_idx(name) < 0) {
1890                 return (0);
1891         }
1892 
1893         /*
1894          * Create a device data structure from this node data.
1895          * The call may yield nothing if the node is not of interest
1896          * to drmach.
1897          */
1898         data->err = drmach_device_new(node, obj, portid, &id);
1899         if (data->err)
1900                 return (-1);
1901         else if (!id) {
1902                 /*
1903                  * drmach_device_new examined the node we passed in
1904                  * and determined that it was one not of interest to
1905                  * drmach.  So, it is skipped.
1906                  */
1907                 return (0);
1908         }
1909 
1910         rv = drmach_array_set(obj->devices, data->ndevs++, id);
1911         if (rv) {
1912                 data->err = DRMACH_INTERNAL_ERROR();
1913                 return (-1);
1914         }
1915         device = id;
1916 
1917         data->err = (*data->found)(data->a, device->type, device->unum, id);
1918 
1919         return (data->err == NULL ? 0 : -1);
1920 }
1921 
1922 sbd_error_t *
1923 drmach_board_find_devices(drmachid_t id, void *a,
1924         sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
1925 {
1926         drmach_board_t          *bp = (drmach_board_t *)id;
1927         sbd_error_t             *err;
1928         int                      max_devices;
1929         int                      rv;
1930         drmach_board_cb_data_t  data;
1931 
1932         if (!DRMACH_IS_BOARD_ID(id))
1933                 return (drerr_new(0, EX86_INAPPROP, NULL));
1934 
1935         max_devices  = MAX_CPU_UNITS_PER_BOARD;
1936         max_devices += MAX_MEM_UNITS_PER_BOARD;
1937         max_devices += MAX_IO_UNITS_PER_BOARD;
1938 
1939         if (bp->devices == NULL)
1940                 bp->devices = drmach_array_new(0, max_devices);
1941         ASSERT(bp->tree != NULL);
1942 
1943         data.obj = bp;
1944         data.ndevs = 0;
1945         data.found = found;
1946         data.a = a;
1947         data.err = NULL;
1948 
1949         acpidev_dr_lock_all();
1950         rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
1951         acpidev_dr_unlock_all();
1952         if (rv == 0) {
1953                 err = NULL;
1954         } else {
1955                 drmach_array_dispose(bp->devices, drmach_device_dispose);
1956                 bp->devices = NULL;
1957 
1958                 if (data.err)
1959                         err = data.err;
1960                 else
1961                         err = DRMACH_INTERNAL_ERROR();
1962         }
1963 
1964         return (err);
1965 }
1966 
1967 int
1968 drmach_board_lookup(int bnum, drmachid_t *id)
1969 {
1970         int     rv = 0;
1971 
1972         if (bnum < 0) {
1973                 *id = 0;
1974                 return (-1);
1975         }
1976 
1977         rw_enter(&drmach_boards_rwlock, RW_READER);
1978         if (drmach_array_get(drmach_boards, (uint_t)bnum, id)) {
1979                 *id = 0;
1980                 rv = -1;
1981         }
1982         rw_exit(&drmach_boards_rwlock);
1983 
1984         return (rv);
1985 }
1986 
1987 sbd_error_t *
1988 drmach_board_name(int bnum, char *buf, int buflen)
1989 {
1990         ACPI_HANDLE hdl;
1991         sbd_error_t *err = NULL;
1992 
1993         if (bnum < 0) {
1994                 return (drerr_new(1, EX86_BNUM, "%d", bnum));
1995         }
1996 
1997         acpidev_dr_lock_all();
1998         if (ACPI_FAILURE(acpidev_dr_get_board_handle(bnum, &hdl))) {
1999                 DRMACH_PR("!drmach_board_name: failed to lookup ACPI handle "
2000                     "for board %d.", bnum);
2001                 err = drerr_new(1, EX86_BNUM, "%d", bnum);
2002         } else if (ACPI_FAILURE(acpidev_dr_get_board_name(hdl, buf, buflen))) {
2003                 DRMACH_PR("!drmach_board_name: failed to generate board name "
2004                     "for board %d.", bnum);
2005                 err = drerr_new(0, EX86_INVALID_ARG,
2006                     ": buffer is too small for board name.");
2007         }
2008         acpidev_dr_unlock_all();
2009 
2010         return (err);
2011 }
2012 
2013 int
2014 drmach_board_is_floating(drmachid_t id)
2015 {
2016         drmach_board_t *bp;
2017 
2018         if (!DRMACH_IS_BOARD_ID(id))
2019                 return (0);
2020 
2021         bp = (drmach_board_t *)id;
2022 
2023         return ((drmach_domain.floating & (1ULL << bp->bnum)) ? 1 : 0);
2024 }
2025 
2026 static ACPI_STATUS
2027 drmach_board_check_dependent_cb(ACPI_HANDLE hdl, UINT32 lvl, void *ctx,
2028     void **retval)
2029 {
2030         uint32_t bdnum;
2031         drmach_board_t *bp;
2032         ACPI_STATUS rc = AE_OK;
2033         int cmd = (int)(intptr_t)ctx;
2034 
2035         ASSERT(hdl != NULL);
2036         ASSERT(lvl == UINT32_MAX);
2037         ASSERT(retval != NULL);
2038 
2039         /* Skip non-board devices. */
2040         if (!acpidev_dr_device_is_board(hdl)) {
2041                 return (AE_OK);
2042         } else if (ACPI_FAILURE(acpidev_dr_get_board_number(hdl, &bdnum))) {
2043                 DRMACH_PR("!drmach_board_check_dependent_cb: failed to get "
2044                     "board number for object %p.\n", hdl);
2045                 return (AE_ERROR);
2046         } else if (bdnum > MAX_BOARDS) {
2047                 DRMACH_PR("!drmach_board_check_dependent_cb: board number %u "
2048                     "is too big, max %u.", bdnum, MAX_BOARDS);
2049                 return (AE_ERROR);
2050         }
2051 
2052         bp = drmach_get_board_by_bnum(bdnum);
2053         switch (cmd) {
2054         case SBD_CMD_CONNECT:
2055                 /*
2056                  * Its parent board should be present, assigned, powered and
2057                  * connected when connecting the child board.
2058                  */
2059                 if (bp == NULL) {
2060                         *retval = hdl;
2061                         rc = AE_ERROR;
2062                 } else {
2063                         bp->powered = acpidev_dr_device_is_powered(hdl);
2064                         if (!bp->connected || !bp->powered || !bp->assigned) {
2065                                 *retval = hdl;
2066                                 rc = AE_ERROR;
2067                         }
2068                 }
2069                 break;
2070 
2071         case SBD_CMD_POWERON:
2072                 /*
2073                  * Its parent board should be present, assigned and powered when
2074                  * powering on the child board.
2075                  */
2076                 if (bp == NULL) {
2077                         *retval = hdl;
2078                         rc = AE_ERROR;
2079                 } else {
2080                         bp->powered = acpidev_dr_device_is_powered(hdl);
2081                         if (!bp->powered || !bp->assigned) {
2082                                 *retval = hdl;
2083                                 rc = AE_ERROR;
2084                         }
2085                 }
2086                 break;
2087 
2088         case SBD_CMD_ASSIGN:
2089                 /*
2090                  * Its parent board should be present and assigned when
2091                  * assigning the child board.
2092                  */
2093                 if (bp == NULL) {
2094                         *retval = hdl;
2095                         rc = AE_ERROR;
2096                 } else if (!bp->assigned) {
2097                         *retval = hdl;
2098                         rc = AE_ERROR;
2099                 }
2100                 break;
2101 
2102         case SBD_CMD_DISCONNECT:
2103                 /*
2104                  * The child board should be disconnected if present when
2105                  * disconnecting its parent board.
2106                  */
2107                 if (bp != NULL && bp->connected) {
2108                         *retval = hdl;
2109                         rc = AE_ERROR;
2110                 }
2111                 break;
2112 
2113         case SBD_CMD_POWEROFF:
2114                 /*
2115                  * The child board should be disconnected and powered off if
2116                  * present when powering off its parent board.
2117                  */
2118                 if (bp != NULL) {
2119                         bp->powered = acpidev_dr_device_is_powered(hdl);
2120                         if (bp->connected || bp->powered) {
2121                                 *retval = hdl;
2122                                 rc = AE_ERROR;
2123                         }
2124                 }
2125                 break;
2126 
2127         case SBD_CMD_UNASSIGN:
2128                 /*
2129                  * The child board should be disconnected, powered off and
2130                  * unassigned if present when unassigning its parent board.
2131                  */
2132                 if (bp != NULL) {
2133                         bp->powered = acpidev_dr_device_is_powered(hdl);
2134                         if (bp->connected || bp->powered || bp->assigned) {
2135                                 *retval = hdl;
2136                                 rc = AE_ERROR;
2137                         }
2138                 }
2139                 break;
2140 
2141         default:
2142                 /* Return success for all other commands. */
2143                 break;
2144         }
2145 
2146         return (rc);
2147 }
2148 
2149 sbd_error_t *
2150 drmach_board_check_dependent(int cmd, drmach_board_t *bp)
2151 {
2152         int reverse;
2153         char *name;
2154         sbd_error_t *err = NULL;
2155         DRMACH_HANDLE hdl;
2156         DRMACH_HANDLE dp = NULL;
2157 
2158         ASSERT(bp != NULL);
2159         ASSERT(DRMACH_IS_BOARD_ID(bp));
2160         ASSERT(RW_LOCK_HELD(&drmach_boards_rwlock));
2161 
2162         hdl = drmach_node_get_dnode(bp->tree);
2163         if (hdl == NULL)
2164                 return (drerr_new(0, EX86_INAPPROP, NULL));
2165 
2166         switch (cmd) {
2167         case SBD_CMD_ASSIGN:
2168         case SBD_CMD_POWERON:
2169         case SBD_CMD_CONNECT:
2170                 if (ACPI_SUCCESS(acpidev_dr_device_walk_ejd(hdl,
2171                     &drmach_board_check_dependent_cb,
2172                     (void *)(intptr_t)cmd, &dp))) {
2173                         return (NULL);
2174                 }
2175                 reverse = 0;
2176                 break;
2177 
2178         case SBD_CMD_UNASSIGN:
2179         case SBD_CMD_POWEROFF:
2180         case SBD_CMD_DISCONNECT:
2181                 if (ACPI_SUCCESS(acpidev_dr_device_walk_edl(hdl,
2182                     &drmach_board_check_dependent_cb,
2183                     (void *)(intptr_t)cmd, &dp))) {
2184                         return (NULL);
2185                 }
2186                 reverse = 1;
2187                 break;
2188 
2189         default:
2190                 return (drerr_new(0, EX86_INAPPROP, NULL));
2191         }
2192 
2193         if (dp == NULL) {
2194                 return (drerr_new(1, EX86_WALK_DEPENDENCY, "%s", bp->cm.name));
2195         }
2196         name = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
2197         if (ACPI_FAILURE(acpidev_dr_get_board_name(dp, name, MAXPATHLEN))) {
2198                 err = drerr_new(1, EX86_WALK_DEPENDENCY, "%s", bp->cm.name);
2199         } else if (reverse == 0) {
2200                 err = drerr_new(1, EX86_WALK_DEPENDENCY,
2201                     "%s, depends on board %s", bp->cm.name, name);
2202         } else {
2203                 err = drerr_new(1, EX86_WALK_DEPENDENCY,
2204                     "board %s depends on %s", name, bp->cm.name);
2205         }
2206         kmem_free(name, MAXPATHLEN);
2207 
2208         return (err);
2209 }
2210 
2211 sbd_error_t *
2212 drmach_board_assign(int bnum, drmachid_t *id)
2213 {
2214         sbd_error_t     *err = NULL;
2215 
2216         if (bnum < 0) {
2217                 return (drerr_new(1, EX86_BNUM, "%d", bnum));
2218         }
2219 
2220         rw_enter(&drmach_boards_rwlock, RW_WRITER);
2221 
2222         if (drmach_array_get(drmach_boards, bnum, id) == -1) {
2223                 err = drerr_new(1, EX86_BNUM, "%d", bnum);
2224         } else {
2225                 drmach_board_t  *bp;
2226 
2227                 /*
2228                  * Board has already been created, downgrade to reader.
2229                  */
2230                 if (*id)
2231                         rw_downgrade(&drmach_boards_rwlock);
2232 
2233                 bp = *id;
2234                 if (!(*id))
2235                         bp = *id  =
2236                             (drmachid_t)drmach_board_new(bnum, 0);
2237 
2238                 if (bp == NULL) {
2239                         DRMACH_PR("!drmach_board_assign: failed to create "
2240                             "object for board %d.", bnum);
2241                         err = drerr_new(1, EX86_BNUM, "%d", bnum);
2242                 } else {
2243                         err = drmach_board_check_dependent(SBD_CMD_ASSIGN, bp);
2244                         if (err == NULL)
2245                                 bp->assigned = 1;
2246                 }
2247         }
2248 
2249         rw_exit(&drmach_boards_rwlock);
2250 
2251         return (err);
2252 }
2253 
2254 sbd_error_t *
2255 drmach_board_unassign(drmachid_t id)
2256 {
2257         drmach_board_t  *bp;
2258         sbd_error_t     *err;
2259         drmach_status_t  stat;
2260 
2261         if (DRMACH_NULL_ID(id))
2262                 return (NULL);
2263 
2264         if (!DRMACH_IS_BOARD_ID(id)) {
2265                 return (drerr_new(0, EX86_INAPPROP, NULL));
2266         }
2267         bp = id;
2268 
2269         rw_enter(&drmach_boards_rwlock, RW_WRITER);
2270 
2271         err = drmach_board_status(id, &stat);
2272         if (err) {
2273                 rw_exit(&drmach_boards_rwlock);
2274                 return (err);
2275         }
2276 
2277         if (stat.configured || stat.busy) {
2278                 err = drerr_new(0, EX86_CONFIGBUSY, bp->cm.name);
2279         } else if (bp->connected) {
2280                 err = drerr_new(0, EX86_CONNECTBUSY, bp->cm.name);
2281         } else if (stat.powered) {
2282                 err = drerr_new(0, EX86_POWERBUSY, bp->cm.name);
2283         } else {
2284                 err = drmach_board_check_dependent(SBD_CMD_UNASSIGN, bp);
2285                 if (err == NULL) {
2286                         if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
2287                                 err = DRMACH_INTERNAL_ERROR();
2288                         else
2289                                 drmach_board_dispose(bp);
2290                 }
2291         }
2292 
2293         rw_exit(&drmach_boards_rwlock);
2294 
2295         return (err);
2296 }
2297 
2298 sbd_error_t *
2299 drmach_board_poweron(drmachid_t id)
2300 {
2301         drmach_board_t  *bp;
2302         sbd_error_t *err = NULL;
2303         DRMACH_HANDLE hdl;
2304 
2305         if (!DRMACH_IS_BOARD_ID(id))
2306                 return (drerr_new(0, EX86_INAPPROP, NULL));
2307         bp = id;
2308 
2309         hdl = drmach_node_get_dnode(bp->tree);
2310         if (hdl == NULL)
2311                 return (drerr_new(0, EX86_INAPPROP, NULL));
2312 
2313         bp->powered = drmach_board_check_power(bp);
2314         if (bp->powered) {
2315                 return (NULL);
2316         }
2317 
2318         rw_enter(&drmach_boards_rwlock, RW_WRITER);
2319         err = drmach_board_check_dependent(SBD_CMD_POWERON, bp);
2320         if (err == NULL) {
2321                 acpidev_dr_lock_all();
2322                 if (ACPI_FAILURE(acpidev_dr_device_poweron(hdl)))
2323                         err = drerr_new(0, EX86_POWERON, NULL);
2324                 acpidev_dr_unlock_all();
2325 
2326                 /* Check whether the board is powered on. */
2327                 bp->powered = drmach_board_check_power(bp);
2328                 if (err == NULL && bp->powered == 0)
2329                         err = drerr_new(0, EX86_POWERON, NULL);
2330         }
2331         rw_exit(&drmach_boards_rwlock);
2332 
2333         return (err);
2334 }
2335 
2336 sbd_error_t *
2337 drmach_board_poweroff(drmachid_t id)
2338 {
2339         sbd_error_t     *err = NULL;
2340         drmach_board_t  *bp;
2341         drmach_status_t  stat;
2342         DRMACH_HANDLE    hdl;
2343 
2344         if (DRMACH_NULL_ID(id))
2345                 return (NULL);
2346 
2347         if (!DRMACH_IS_BOARD_ID(id))
2348                 return (drerr_new(0, EX86_INAPPROP, NULL));
2349         bp = id;
2350 
2351         hdl = drmach_node_get_dnode(bp->tree);
2352         if (hdl == NULL)
2353                 return (drerr_new(0, EX86_INAPPROP, NULL));
2354 
2355         /* Check whether the board is busy, configured or connected. */
2356         err = drmach_board_status(id, &stat);
2357         if (err != NULL)
2358                 return (err);
2359         if (stat.configured || stat.busy) {
2360                 return (drerr_new(0, EX86_CONFIGBUSY, bp->cm.name));
2361         } else if (bp->connected) {
2362                 return (drerr_new(0, EX86_CONNECTBUSY, bp->cm.name));
2363         }
2364 
2365         bp->powered = drmach_board_check_power(bp);
2366         if (bp->powered == 0) {
2367                 return (NULL);
2368         }
2369 
2370         rw_enter(&drmach_boards_rwlock, RW_WRITER);
2371         err = drmach_board_check_dependent(SBD_CMD_POWEROFF, bp);
2372         if (err == NULL) {
2373                 acpidev_dr_lock_all();
2374                 if (ACPI_FAILURE(acpidev_dr_device_poweroff(hdl)))
2375                         err = drerr_new(0, EX86_POWEROFF, NULL);
2376                 acpidev_dr_unlock_all();
2377 
2378                 bp->powered = drmach_board_check_power(bp);
2379                 if (err == NULL && bp->powered != 0)
2380                         err = drerr_new(0, EX86_POWEROFF, NULL);
2381         }
2382         rw_exit(&drmach_boards_rwlock);
2383 
2384         return (err);
2385 }
2386 
2387 sbd_error_t *
2388 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
2389 {
2390         _NOTE(ARGUNUSED(opts, force));
2391 
2392         drmach_board_t  *bp;
2393         DRMACH_HANDLE    hdl;
2394 
2395         if (DRMACH_NULL_ID(id))
2396                 return (NULL);
2397 
2398         if (!DRMACH_IS_BOARD_ID(id))
2399                 return (drerr_new(0, EX86_INAPPROP, NULL));
2400         bp = id;
2401 
2402         hdl = drmach_node_get_dnode(bp->tree);
2403         if (hdl == NULL)
2404                 return (drerr_new(0, EX86_INAPPROP, NULL));
2405 
2406         if (ACPI_FAILURE(acpidev_dr_device_check_status(hdl)))
2407                 return (drerr_new(0, EX86_IN_FAILURE, NULL));
2408 
2409         return (NULL);
2410 }
2411 
2412 sbd_error_t *
2413 drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
2414 {
2415         _NOTE(ARGUNUSED(opts));
2416 
2417         sbd_error_t     *err = NULL;
2418         drmach_board_t  *bp = (drmach_board_t *)id;
2419         DRMACH_HANDLE   hdl;
2420 
2421         if (!DRMACH_IS_BOARD_ID(id))
2422                 return (drerr_new(0, EX86_INAPPROP, NULL));
2423         bp = (drmach_board_t *)id;
2424 
2425         hdl = drmach_node_get_dnode(bp->tree);
2426         if (hdl == NULL)
2427                 return (drerr_new(0, EX86_INAPPROP, NULL));
2428 
2429         rw_enter(&drmach_boards_rwlock, RW_WRITER);
2430         err = drmach_board_check_dependent(SBD_CMD_CONNECT, bp);
2431         if (err == NULL) {
2432                 acpidev_dr_lock_all();
2433                 if (ACPI_FAILURE(acpidev_dr_device_insert(hdl))) {
2434                         (void) acpidev_dr_device_remove(hdl);
2435                         err = drerr_new(1, EX86_PROBE, NULL);
2436                 } else {
2437                         bp->connected = 1;
2438                 }
2439                 acpidev_dr_unlock_all();
2440         }
2441         rw_exit(&drmach_boards_rwlock);
2442 
2443         return (err);
2444 }
2445 
2446 sbd_error_t *
2447 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
2448 {
2449         _NOTE(ARGUNUSED(opts));
2450 
2451         DRMACH_HANDLE hdl;
2452         drmach_board_t *bp;
2453         drmach_status_t stat;
2454         sbd_error_t *err = NULL;
2455 
2456         if (DRMACH_NULL_ID(id))
2457                 return (NULL);
2458         if (!DRMACH_IS_BOARD_ID(id))
2459                 return (drerr_new(0, EX86_INAPPROP, NULL));
2460         bp = (drmach_board_t *)id;
2461 
2462         hdl = drmach_node_get_dnode(bp->tree);
2463         if (hdl == NULL)
2464                 return (drerr_new(0, EX86_INAPPROP, NULL));
2465 
2466         /* Check whether the board is busy or configured. */
2467         err = drmach_board_status(id, &stat);
2468         if (err != NULL)
2469                 return (err);
2470         if (stat.configured || stat.busy)
2471                 return (drerr_new(0, EX86_CONFIGBUSY, bp->cm.name));
2472 
2473         rw_enter(&drmach_boards_rwlock, RW_WRITER);
2474         err = drmach_board_check_dependent(SBD_CMD_DISCONNECT, bp);
2475         if (err == NULL) {
2476                 acpidev_dr_lock_all();
2477                 if (ACPI_SUCCESS(acpidev_dr_device_remove(hdl))) {
2478                         bp->connected = 0;
2479                 } else {
2480                         err = drerr_new(1, EX86_DEPROBE, bp->cm.name);
2481                 }
2482                 acpidev_dr_unlock_all();
2483         }
2484         rw_exit(&drmach_boards_rwlock);
2485 
2486         return (err);
2487 }
2488 
2489 sbd_error_t *
2490 drmach_board_deprobe(drmachid_t id)
2491 {
2492         drmach_board_t  *bp;
2493 
2494         if (!DRMACH_IS_BOARD_ID(id))
2495                 return (drerr_new(0, EX86_INAPPROP, NULL));
2496         bp = id;
2497 
2498         cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum);
2499 
2500         if (bp->devices) {
2501                 drmach_array_dispose(bp->devices, drmach_device_dispose);
2502                 bp->devices = NULL;
2503         }
2504 
2505         bp->boot_board = 0;
2506 
2507         return (NULL);
2508 }
2509 
2510 /*
2511  * CPU specific interfaces to support dr driver
2512  */
2513 sbd_error_t *
2514 drmach_cpu_disconnect(drmachid_t id)
2515 {
2516         if (!DRMACH_IS_CPU_ID(id))
2517                 return (drerr_new(0, EX86_INAPPROP, NULL));
2518 
2519         return (NULL);
2520 }
2521 
2522 sbd_error_t *
2523 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
2524 {
2525         drmach_cpu_t *cpu;
2526 
2527         if (!DRMACH_IS_CPU_ID(id))
2528                 return (drerr_new(0, EX86_INAPPROP, NULL));
2529         cpu = (drmach_cpu_t *)id;
2530 
2531         if (cpu->cpuid == -1) {
2532                 if (ACPI_SUCCESS(acpica_get_cpu_id_by_object(
2533                     drmach_node_get_dnode(cpu->dev.node), cpuid))) {
2534                         cpu->cpuid = *cpuid;
2535                 } else {
2536                         *cpuid = -1;
2537                 }
2538         } else {
2539                 *cpuid = cpu->cpuid;
2540         }
2541 
2542         return (NULL);
2543 }
2544 
2545 sbd_error_t *
2546 drmach_cpu_get_impl(drmachid_t id, int *ip)
2547 {
2548         if (!DRMACH_IS_CPU_ID(id))
2549                 return (drerr_new(0, EX86_INAPPROP, NULL));
2550 
2551         /* Assume all CPUs in system are homogeneous. */
2552         *ip = X86_CPU_IMPL_UNKNOWN;
2553 
2554         kpreempt_disable();
2555         if (cpuid_getvendor(CPU) == X86_VENDOR_Intel) {
2556                 /* NHM-EX CPU */
2557                 if (cpuid_getfamily(CPU) == 0x6 &&
2558                     cpuid_getmodel(CPU) == 0x2e) {
2559                         *ip = X86_CPU_IMPL_NEHALEM_EX;
2560                 }
2561         }
2562         kpreempt_enable();
2563 
2564         return (NULL);
2565 }
2566 
2567 /*
2568  * Memory specific interfaces to support dr driver
2569  */
2570 
2571 /*
2572  * When drmach_mem_new() is called, the mp->base_pa field is set to the base
2573  * address of configured memory if there's configured memory on the board,
2574  * otherwise set to UINT64_MAX. For hot-added memory board, there's no
2575  * configured memory when drmach_mem_new() is called, so mp->base_pa is set
2576  * to UINT64_MAX and we need to set a correct value for it after memory
2577  * hot-add  operations.
2578  * A hot-added memory board may contain multiple memory segments,
2579  * drmach_mem_add_span() will be called once for each segment, so we can't
2580  * rely on the basepa argument. And it's possible that only part of a memory
2581  * segment is added into OS, so need to intersect with phys_installed list
2582  * to get the real base address of configured memory on the board.
2583  */
2584 sbd_error_t *
2585 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
2586 {
2587         _NOTE(ARGUNUSED(basepa));
2588 
2589         uint64_t        nbytes = 0;
2590         uint64_t        endpa;
2591         drmach_mem_t    *mp;
2592         struct memlist  *ml2;
2593         struct memlist  *p;
2594 
2595         ASSERT(size != 0);
2596 
2597         if (!DRMACH_IS_MEM_ID(id))
2598                 return (drerr_new(0, EX86_INAPPROP, NULL));
2599         mp = (drmach_mem_t *)id;
2600 
2601         /* Compute basepa and size of installed memory. */
2602         endpa = _ptob64(physmax + 1);
2603         memlist_read_lock();
2604         ml2 = memlist_dup(phys_install);
2605         memlist_read_unlock();
2606         ml2 = memlist_del_span(ml2, 0ull, mp->slice_base);
2607         if (ml2 && endpa > mp->slice_top) {
2608                 ml2 = memlist_del_span(ml2, mp->slice_top,
2609                     endpa - mp->slice_top);
2610         }
2611 
2612         ASSERT(ml2);
2613         if (ml2) {
2614                 for (p = ml2; p; p = p->ml_next) {
2615                         nbytes += p->ml_size;
2616                         if (mp->base_pa > p->ml_address)
2617                                 mp->base_pa = p->ml_address;
2618                 }
2619                 ASSERT(nbytes > 0);
2620                 mp->nbytes += nbytes;
2621                 memlist_delete(ml2);
2622         }
2623 
2624         return (NULL);
2625 }
2626 
2627 static sbd_error_t *
2628 drmach_mem_update_lgrp(drmachid_t id)
2629 {
2630         ACPI_STATUS     rc;
2631         DRMACH_HANDLE   hdl;
2632         void            *hdlp;
2633         drmach_mem_t    *mp;
2634         update_membounds_t umb;
2635 
2636         if (!DRMACH_IS_MEM_ID(id))
2637                 return (drerr_new(0, EX86_INAPPROP, NULL));
2638         mp = (drmach_mem_t *)id;
2639         /* No need to update lgrp if memory is already installed. */
2640         if (mp->nbytes != 0)
2641                 return (NULL);
2642         /* No need to update lgrp if lgrp is disabled. */
2643         if (max_mem_nodes == 1)
2644                 return (NULL);
2645 
2646         /* Add memory to lgroup */
2647         hdl = mp->dev.node->get_dnode(mp->dev.node);
2648         rc = acpidev_dr_device_get_memory_index(hdl, &umb.u_device_id);
2649         ASSERT(ACPI_SUCCESS(rc));
2650         if (ACPI_FAILURE(rc)) {
2651                 cmn_err(CE_WARN, "drmach: failed to get device id of memory, "
2652                     "can't update lgrp information.");
2653                 return (drerr_new(0, EX86_INTERNAL, NULL));
2654         }
2655         rc = acpidev_dr_get_mem_numa_info(hdl, mp->memlist, &hdlp,
2656             &umb.u_domain, &umb.u_sli_cnt, &umb.u_sli_ptr);
2657         ASSERT(ACPI_SUCCESS(rc));
2658         if (ACPI_FAILURE(rc)) {
2659                 cmn_err(CE_WARN, "drmach: failed to get lgrp info of memory, "
2660                     "can't update lgrp information.");
2661                 return (drerr_new(0, EX86_INTERNAL, NULL));
2662         }
2663         umb.u_base = (uint64_t)mp->slice_base;
2664         umb.u_length = (uint64_t)(mp->slice_top - mp->slice_base);
2665         lgrp_plat_config(LGRP_CONFIG_MEM_ADD, (uintptr_t)&umb);
2666         acpidev_dr_free_mem_numa_info(hdlp);
2667 
2668         return (NULL);
2669 }
2670 
2671 sbd_error_t *
2672 drmach_mem_enable(drmachid_t id)
2673 {
2674         if (!DRMACH_IS_MEM_ID(id))
2675                 return (drerr_new(0, EX86_INAPPROP, NULL));
2676         else
2677                 return (NULL);
2678 }
2679 
2680 sbd_error_t *
2681 drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem)
2682 {
2683         drmach_mem_t *mp;
2684 
2685         if (!DRMACH_IS_MEM_ID(id))
2686                 return (drerr_new(0, EX86_INAPPROP, NULL));
2687         mp = (drmach_mem_t *)id;
2688 
2689         /*
2690          * This is only used by dr to round up/down the memory
2691          * for copying.
2692          */
2693         mem->mi_alignment_mask = mp->mem_alignment - 1;
2694         mem->mi_basepa = mp->base_pa;
2695         mem->mi_size = mp->nbytes;
2696         mem->mi_slice_base = mp->slice_base;
2697         mem->mi_slice_top = mp->slice_top;
2698         mem->mi_slice_size = mp->slice_size;
2699 
2700         return (NULL);
2701 }
2702 
2703 sbd_error_t *
2704 drmach_mem_get_slice_info(drmachid_t id,
2705     uint64_t *bp, uint64_t *ep, uint64_t *sp)
2706 {
2707         drmach_mem_t *mp;
2708 
2709         if (!DRMACH_IS_MEM_ID(id))
2710                 return (drerr_new(0, EX86_INAPPROP, NULL));
2711         mp = (drmach_mem_t *)id;
2712 
2713         if (bp)
2714                 *bp = mp->slice_base;
2715         if (ep)
2716                 *ep = mp->slice_top;
2717         if (sp)
2718                 *sp = mp->slice_size;
2719 
2720         return (NULL);
2721 }
2722 
2723 sbd_error_t *
2724 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
2725 {
2726 #ifdef  DEBUG
2727         int             rv;
2728 #endif
2729         drmach_mem_t    *mem;
2730         struct memlist  *mlist;
2731 
2732         if (!DRMACH_IS_MEM_ID(id))
2733                 return (drerr_new(0, EX86_INAPPROP, NULL));
2734         mem = (drmach_mem_t *)id;
2735 
2736         mlist = memlist_dup(mem->memlist);
2737         *ml = mlist;
2738 
2739 #ifdef DEBUG
2740         /*
2741          * Make sure the incoming memlist doesn't already
2742          * intersect with what's present in the system (phys_install).
2743          */
2744         memlist_read_lock();
2745         rv = memlist_intersect(phys_install, mlist);
2746         memlist_read_unlock();
2747         if (rv) {
2748                 DRMACH_PR("Derived memlist intersects with phys_install\n");
2749                 memlist_dump(mlist);
2750 
2751                 DRMACH_PR("phys_install memlist:\n");
2752                 memlist_dump(phys_install);
2753 
2754                 memlist_delete(mlist);
2755                 return (DRMACH_INTERNAL_ERROR());
2756         }
2757 
2758         DRMACH_PR("Derived memlist:");
2759         memlist_dump(mlist);
2760 #endif
2761 
2762         return (NULL);
2763 }
2764 
2765 processorid_t
2766 drmach_mem_cpu_affinity(drmachid_t id)
2767 {
2768         _NOTE(ARGUNUSED(id));
2769 
2770         return (CPU_CURRENT);
2771 }
2772 
2773 int
2774 drmach_copy_rename_need_suspend(drmachid_t id)
2775 {
2776         _NOTE(ARGUNUSED(id));
2777 
2778         return (0);
2779 }
2780 
2781 /*
2782  * IO specific interfaces to support dr driver
2783  */
2784 sbd_error_t *
2785 drmach_io_pre_release(drmachid_t id)
2786 {
2787         if (!DRMACH_IS_IO_ID(id))
2788                 return (drerr_new(0, EX86_INAPPROP, NULL));
2789 
2790         return (NULL);
2791 }
2792 
2793 sbd_error_t *
2794 drmach_io_unrelease(drmachid_t id)
2795 {
2796         if (!DRMACH_IS_IO_ID(id))
2797                 return (drerr_new(0, EX86_INAPPROP, NULL));
2798 
2799         return (NULL);
2800 }
2801 
2802 sbd_error_t *
2803 drmach_io_post_release(drmachid_t id)
2804 {
2805         _NOTE(ARGUNUSED(id));
2806 
2807         return (NULL);
2808 }
2809 
2810 sbd_error_t *
2811 drmach_io_post_attach(drmachid_t id)
2812 {
2813         if (!DRMACH_IS_IO_ID(id))
2814                 return (drerr_new(0, EX86_INAPPROP, NULL));
2815 
2816         return (NULL);
2817 }
2818 
2819 sbd_error_t *
2820 drmach_io_is_attached(drmachid_t id, int *yes)
2821 {
2822         drmach_device_t *dp;
2823         dev_info_t      *dip;
2824         int             state;
2825 
2826         if (!DRMACH_IS_IO_ID(id))
2827                 return (drerr_new(0, EX86_INAPPROP, NULL));
2828         dp = id;
2829 
2830         dip = dp->node->getdip(dp->node);
2831         if (dip == NULL) {
2832                 *yes = 0;
2833                 return (NULL);
2834         }
2835 
2836         state = ddi_get_devstate(dip);
2837         *yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
2838             (state == DDI_DEVSTATE_UP));
2839 
2840         return (NULL);
2841 }
2842 
2843 /*
2844  * Miscellaneous interfaces to support dr driver
2845  */
2846 int
2847 drmach_verify_sr(dev_info_t *dip, int sflag)
2848 {
2849         _NOTE(ARGUNUSED(dip, sflag));
2850 
2851         return (0);
2852 }
2853 
2854 void
2855 drmach_suspend_last(void)
2856 {
2857 }
2858 
2859 void
2860 drmach_resume_first(void)
2861 {
2862 }
2863 
2864 /*
2865  * Log a DR sysevent.
2866  * Return value: 0 success, non-zero failure.
2867  */
2868 int
2869 drmach_log_sysevent(int board, char *hint, int flag, int verbose)
2870 {
2871         sysevent_t                      *ev = NULL;
2872         sysevent_id_t                   eid;
2873         int                             rv, km_flag;
2874         sysevent_value_t                evnt_val;
2875         sysevent_attr_list_t            *evnt_attr_list = NULL;
2876         sbd_error_t                     *err;
2877         char                            attach_pnt[MAXNAMELEN];
2878 
2879         km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
2880         attach_pnt[0] = '\0';
2881         err = drmach_board_name(board, attach_pnt, MAXNAMELEN);
2882         if (err != NULL) {
2883                 sbd_err_clear(&err);
2884                 rv = -1;
2885                 goto logexit;
2886         }
2887         if (verbose) {
2888                 DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
2889                     attach_pnt, hint, flag, verbose);
2890         }
2891 
2892         if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
2893             SUNW_KERN_PUB"dr", km_flag)) == NULL) {
2894                 rv = -2;
2895                 goto logexit;
2896         }
2897         evnt_val.value_type = SE_DATA_TYPE_STRING;
2898         evnt_val.value.sv_string = attach_pnt;
2899         if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
2900             km_flag)) != 0)
2901                 goto logexit;
2902 
2903         evnt_val.value_type = SE_DATA_TYPE_STRING;
2904         evnt_val.value.sv_string = hint;
2905         if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
2906             km_flag)) != 0) {
2907                 sysevent_free_attr(evnt_attr_list);
2908                 goto logexit;
2909         }
2910 
2911         (void) sysevent_attach_attributes(ev, evnt_attr_list);
2912 
2913         /*
2914          * Log the event but do not sleep waiting for its
2915          * delivery. This provides insulation from syseventd.
2916          */
2917         rv = log_sysevent(ev, SE_NOSLEEP, &eid);
2918 
2919 logexit:
2920         if (ev)
2921                 sysevent_free(ev);
2922         if ((rv != 0) && verbose)
2923                 cmn_err(CE_WARN, "!drmach_log_sysevent failed (rv %d) for %s "
2924                     " %s\n", rv, attach_pnt, hint);
2925 
2926         return (rv);
2927 }