1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Solaris x86 ACPI ThermalZone Monitor
  29  */
  30 
  31 
  32 #include <sys/errno.h>
  33 #include <sys/conf.h>
  34 #include <sys/modctl.h>
  35 #include <sys/open.h>
  36 #include <sys/stat.h>
  37 #include <sys/ddi.h>
  38 #include <sys/sunddi.h>
  39 #include <sys/ksynch.h>
  40 #include <sys/uadmin.h>
  41 #include <sys/acpi/acpi.h>
  42 #include <sys/acpica.h>
  43 #include <sys/sdt.h>
  44 
  45 #include "tzmon.h"
  46 
  47 
  48 #define TZMON_ENUM_TRIP_POINTS  1
  49 #define TZMON_ENUM_DEV_LISTS    2
  50 #define TZMON_ENUM_ALL          (TZMON_ENUM_TRIP_POINTS | TZMON_ENUM_DEV_LISTS)
  51 
  52 /*
  53  * TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor"
  54  * plus a two-digit instance number plus a NULL.  If the taskq name is changed
  55  * (particularly if it is lengthened), then this value needs to change.
  56  */
  57 #define TZ_TASKQ_NAME_LEN       21
  58 
  59 /*
  60  * Kelvin to Celsius conversion
  61  * The formula for converting degrees Kelvin to degrees Celsius is
  62  * C = K - 273.15 (we round to 273.2).  The unit for thermal zone
  63  * temperatures is tenths of a degree Kelvin.  Use tenth of a degree
  64  * to convert, then make a whole number out of it.
  65  */
  66 #define K_TO_C(temp)            (((temp) - 2732) / 10)
  67 
  68 
  69 /* cb_ops or dev_ops forward declarations */
  70 static  int     tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
  71     void *arg, void **result);
  72 static  int     tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
  73 static  int     tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
  74 
  75 /* other forward declarations */
  76 static void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx);
  77 static void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv);
  78 static thermal_zone_t *tzmon_alloc_zone();
  79 static void tzmon_free_zone_list();
  80 static void tzmon_discard_buffers(thermal_zone_t *tzp);
  81 static void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp,
  82         int enum_flag);
  83 static ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest,
  84     void *ctx, void **rv);
  85 static void tzmon_find_zones(void);
  86 static void tzmon_monitor(void *ctx);
  87 static void tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name);
  88 static void tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name);
  89 static void tzmon_eval_zone(thermal_zone_t *tzp);
  90 static void tzmon_do_shutdown(void);
  91 
  92 extern void halt(char *);
  93 
  94 static struct cb_ops    tzmon_cb_ops = {
  95         nodev,                  /* no open routine      */
  96         nodev,                  /* no close routine     */
  97         nodev,                  /* not a block driver   */
  98         nodev,                  /* no print routine     */
  99         nodev,                  /* no dump routine      */
 100         nodev,                  /* no read routine      */
 101         nodev,                  /* no write routine     */
 102         nodev,                  /* no ioctl routine     */
 103         nodev,                  /* no devmap routine    */
 104         nodev,                  /* no mmap routine      */
 105         nodev,                  /* no segmap routine    */
 106         nochpoll,               /* no chpoll routine    */
 107         ddi_prop_op,
 108         0,                      /* not a STREAMS driver */
 109         D_NEW | D_MP,           /* safe for multi-thread/multi-processor */
 110 };
 111 
 112 static struct dev_ops tzmon_ops = {
 113         DEVO_REV,               /* devo_rev */
 114         0,                      /* devo_refcnt */
 115         tzmon_getinfo,          /* devo_getinfo */
 116         nulldev,                /* devo_identify */
 117         nulldev,                /* devo_probe */
 118         tzmon_attach,           /* devo_attach */
 119         tzmon_detach,           /* devo_detach */
 120         nodev,                  /* devo_reset */
 121         &tzmon_cb_ops,              /* devo_cb_ops */
 122         (struct bus_ops *)0,    /* devo_bus_ops */
 123         NULL,                   /* devo_power */
 124         ddi_quiesce_not_needed,         /* devo_quiesce */
 125 };
 126 
 127 extern  struct  mod_ops mod_driverops;
 128 
 129 static  struct modldrv modldrv = {
 130         &mod_driverops,
 131         "ACPI Thermal Zone Monitor",
 132         &tzmon_ops,
 133 };
 134 
 135 static  struct modlinkage modlinkage = {
 136         MODREV_1,               /* MODREV_1 indicated by manual */
 137         (void *)&modldrv,
 138         NULL,                   /* termination of list of linkage structures */
 139 };
 140 
 141 /* globals for this module */
 142 static dev_info_t       *tzmon_dip;
 143 static thermal_zone_t   *zone_list;
 144 static int              zone_count;
 145 static kmutex_t         zone_list_lock;
 146 static kcondvar_t       zone_list_condvar;
 147 
 148 
 149 /*
 150  * _init, _info, and _fini support loading and unloading the driver.
 151  */
 152 int
 153 _init(void)
 154 {
 155         return (mod_install(&modlinkage));
 156 }
 157 
 158 
 159 int
 160 _info(struct modinfo *modinfop)
 161 {
 162         return (mod_info(&modlinkage, modinfop));
 163 }
 164 
 165 
 166 int
 167 _fini(void)
 168 {
 169         return (mod_remove(&modlinkage));
 170 }
 171 
 172 
 173 static int
 174 tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 175 {
 176         if (cmd != DDI_ATTACH)
 177                 return (DDI_FAILURE);
 178 
 179         if (tzmon_dip != NULL)
 180                 return (DDI_FAILURE);
 181 
 182         /*
 183          * Check to see if ACPI CA services are available
 184          */
 185         if (AcpiSubsystemStatus() != AE_OK)
 186                 return (DDI_FAILURE);
 187 
 188         mutex_init(&zone_list_lock, NULL, MUTEX_DRIVER, NULL);
 189         cv_init(&zone_list_condvar, NULL, CV_DRIVER, NULL);
 190 
 191         tzmon_find_zones();
 192         mutex_enter(&zone_list_lock);
 193         if (zone_count < 1) {
 194                 mutex_exit(&zone_list_lock);
 195                 mutex_destroy(&zone_list_lock);
 196                 cv_destroy(&zone_list_condvar);
 197                 return (DDI_FAILURE);
 198         }
 199         mutex_exit(&zone_list_lock);
 200 
 201         if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 0,
 202             DDI_PSEUDO, 0) == DDI_FAILURE) {
 203                 tzmon_free_zone_list();
 204                 mutex_destroy(&zone_list_lock);
 205                 cv_destroy(&zone_list_condvar);
 206                 return (DDI_FAILURE);
 207         }
 208 
 209         tzmon_dip = dip;
 210 
 211         ddi_report_dev(dip);
 212 
 213         return (DDI_SUCCESS);
 214 }
 215 
 216 
 217 /*ARGSUSED*/
 218 static int
 219 tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 220 {
 221         int error;
 222 
 223         switch (infocmd) {
 224         case DDI_INFO_DEVT2DEVINFO:
 225                 *result = tzmon_dip;
 226                 if (tzmon_dip == NULL)
 227                         error = DDI_FAILURE;
 228                 else
 229                         error = DDI_SUCCESS;
 230                 break;
 231         case DDI_INFO_DEVT2INSTANCE:
 232                 *result = 0;
 233                 error = DDI_SUCCESS;
 234                 break;
 235         default:
 236                 *result = NULL;
 237                 error = DDI_FAILURE;
 238         }
 239 
 240         return (error);
 241 }
 242 
 243 
 244 static int
 245 tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 246 {
 247         thermal_zone_t *tzp = zone_list;
 248 
 249         if (cmd != DDI_DETACH)
 250                 return (DDI_FAILURE);
 251 
 252         /* free allocated thermal zone name(s) */
 253         while (tzp != NULL) {
 254                 AcpiOsFree(tzp->zone_name);
 255                 tzp = tzp->next;
 256         }
 257 
 258         /* discard zone list assets */
 259         tzmon_free_zone_list();
 260 
 261         ddi_remove_minor_node(dip, NULL);
 262         tzmon_dip = NULL;
 263 
 264         mutex_destroy(&zone_list_lock);
 265         cv_destroy(&zone_list_condvar);
 266 
 267         return (DDI_SUCCESS);
 268 }
 269 
 270 
 271 /*
 272  * tzmon_notify_zone
 273  * Thermal zone notification handler.
 274  */
 275 static void
 276 tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx)
 277 {
 278         thermal_zone_t *tzp = (thermal_zone_t *)ctx;
 279 
 280         switch (val) {
 281         case 0x80:      /* Thermal Zone status changed */
 282                 tzmon_eval_zone(tzp);
 283                 break;
 284         case 0x81:      /* Thermal Zone trip points changed */
 285                 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_TRIP_POINTS);
 286                 break;
 287         case 0x82:      /* Device Lists changed */
 288                 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_DEV_LISTS);
 289                 break;
 290         case 0x83:      /* Thermal Relationship Table changed */
 291                 /* not handling _TRT objects, so not handling this event */
 292                 DTRACE_PROBE1(trt__change, char *, (char *)tzp->zone_name);
 293                 break;
 294         default:
 295                 break;
 296         }
 297 }
 298 
 299 
 300 /*
 301  * tzmon_eval_int
 302  * Evaluate the object/method as an integer.
 303  */
 304 static void
 305 tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv)
 306 {
 307 
 308         if (acpica_eval_int(obj, method, rv) != AE_OK)
 309                 *rv = -1;
 310 }
 311 
 312 
 313 /*
 314  * tzmon_alloc_zone
 315  * Allocate memory for the zone structure and initialize it lock mutex.
 316  */
 317 static thermal_zone_t *
 318 tzmon_alloc_zone()
 319 {
 320         thermal_zone_t *tzp;
 321 
 322         tzp = kmem_zalloc(sizeof (thermal_zone_t), KM_SLEEP);
 323         mutex_init(&tzp->lock, NULL, MUTEX_DRIVER, NULL);
 324 
 325         return (tzp);
 326 }
 327 
 328 
 329 /*
 330  * tzmon_free_zone_list
 331  * Free the zone list, either because attach failed or detach initiated.
 332  */
 333 static void
 334 tzmon_free_zone_list()
 335 {
 336         thermal_zone_t *tzp = zone_list;
 337 
 338         while (tzp != NULL) {
 339                 thermal_zone_t *next;
 340 
 341                 mutex_enter(&tzp->lock);
 342 
 343                 /*
 344                  * Remove the notify handler for the zone.  Not much to
 345                  * do if this fails (since we are on our way out), so
 346                  * just ignore failure.
 347                  */
 348                 (void) AcpiRemoveNotifyHandler(tzp->obj, ACPI_DEVICE_NOTIFY,
 349                     tzmon_notify_zone);
 350 
 351                 /* Shut down monitor thread, if running */
 352                 if (tzp->taskq != NULL) {
 353                         tzp->polling_period = 0;
 354                         cv_broadcast(&zone_list_condvar);
 355 
 356                         /* Drop mutex to allow the thread to run */
 357                         mutex_exit(&tzp->lock);
 358                         ddi_taskq_destroy(tzp->taskq);
 359                         mutex_enter(&tzp->lock);
 360                 }
 361 
 362                 tzmon_discard_buffers(tzp);
 363                 mutex_exit(&tzp->lock);
 364                 mutex_destroy(&tzp->lock);
 365 
 366                 next = tzp->next;
 367                 kmem_free(tzp, sizeof (thermal_zone_t));
 368                 tzp = next;
 369         }
 370 }
 371 
 372 
 373 static void
 374 tzmon_discard_buffers(thermal_zone_t *tzp)
 375 {
 376         int level;
 377 
 378         for (level = 0; level < TZ_NUM_LEVELS; level++) {
 379                 if (tzp->al[level].Pointer != NULL)
 380                         AcpiOsFree(tzp->al[level].Pointer);
 381         }
 382 
 383         if (tzp->psl.Pointer != NULL)
 384                 AcpiOsFree(tzp->psl.Pointer);
 385 }
 386 
 387 
 388 /*
 389  * tzmon_enumerate_zone
 390  * Enumerates the contents of a thermal zone and updates passed-in
 391  * thermal_zone or creates a new one if tzp is NULL. Newly-created
 392  * zones are linked into the global zone_list.
 393  */
 394 static void
 395 tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, int enum_flag)
 396 {
 397         ACPI_STATUS status;
 398         ACPI_BUFFER zone_name;
 399         int     level;
 400         int     instance;
 401         char    abuf[5];
 402 
 403         /*
 404          * Newly-created zones and existing zones both require
 405          * some individual attention.
 406          */
 407         if (tzp == NULL) {
 408                 /* New zone required */
 409                 tzp = tzmon_alloc_zone();
 410                 mutex_enter(&zone_list_lock);
 411                 tzp->next = zone_list;
 412                 zone_list = tzp;
 413 
 414                 /*
 415                  * It is exceedingly unlikely that instance will exceed 99.
 416                  * However, if it does, this will cause problems when
 417                  * creating the taskq for this thermal zone.
 418                  */
 419                 instance = zone_count;
 420                 zone_count++;
 421                 mutex_exit(&zone_list_lock);
 422                 mutex_enter(&tzp->lock);
 423                 tzp->obj = obj;
 424 
 425                 /*
 426                  * Set to a low level.  Will get set to the actual
 427                  * current power level when the thread monitor polls
 428                  * the current temperature.
 429                  */
 430                 tzp->current_level = 0;
 431 
 432                 /* Get the zone name in case we need to display it later */
 433                 zone_name.Length = ACPI_ALLOCATE_BUFFER;
 434                 zone_name.Pointer = NULL;
 435 
 436                 status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &zone_name);
 437                 ASSERT(status == AE_OK);
 438 
 439                 tzp->zone_name = zone_name.Pointer;
 440 
 441                 status = AcpiInstallNotifyHandler(obj, ACPI_DEVICE_NOTIFY,
 442                     tzmon_notify_zone, (void *)tzp);
 443                 ASSERT(status == AE_OK);
 444         } else {
 445                 /* Existing zone - toss out allocated items */
 446                 mutex_enter(&tzp->lock);
 447                 ASSERT(tzp->obj == obj);
 448 
 449                 if (enum_flag & TZMON_ENUM_DEV_LISTS)
 450                         tzmon_discard_buffers(tzp);
 451         }
 452 
 453         if (enum_flag & TZMON_ENUM_TRIP_POINTS) {
 454                 for (level = 0; level < TZ_NUM_LEVELS; level++) {
 455                         (void) snprintf(abuf, 5, "_AC%d", level);
 456                         tzmon_eval_int(obj, abuf, &tzp->ac[level]);
 457 
 458                 }
 459 
 460                 tzmon_eval_int(obj, "_CRT", &tzp->crt);
 461                 tzmon_eval_int(obj, "_HOT", &tzp->hot);
 462                 tzmon_eval_int(obj, "_PSV", &tzp->psv);
 463         }
 464 
 465         if (enum_flag & TZMON_ENUM_DEV_LISTS) {
 466                 for (level = 0; level < TZ_NUM_LEVELS; level++) {
 467                         if (tzp->ac[level] == -1) {
 468                                 tzp->al[level].Length = 0;
 469                                 tzp->al[level].Pointer = NULL;
 470                         } else {
 471                                 (void) snprintf(abuf, 5, "_AL%d", level);
 472                                 tzp->al[level].Length = ACPI_ALLOCATE_BUFFER;
 473                                 tzp->al[level].Pointer = NULL;
 474                                 if (AcpiEvaluateObjectTyped(obj, abuf, NULL,
 475                                     &tzp->al[level], ACPI_TYPE_PACKAGE) !=
 476                                     AE_OK) {
 477                                         DTRACE_PROBE2(alx__missing, int, level,
 478                                             char *, (char *)tzp->zone_name);
 479 
 480                                         tzp->al[level].Length = 0;
 481                                         tzp->al[level].Pointer = NULL;
 482                                 }
 483                         }
 484                 }
 485 
 486                 tzp->psl.Length = ACPI_ALLOCATE_BUFFER;
 487                 tzp->psl.Pointer = NULL;
 488                 (void) AcpiEvaluateObjectTyped(obj, "_PSL", NULL, &tzp->psl,
 489                     ACPI_TYPE_PACKAGE);
 490         }
 491 
 492         tzmon_eval_int(obj, "_TC1", &tzp->tc1);
 493         tzmon_eval_int(obj, "_TC2", &tzp->tc2);
 494         tzmon_eval_int(obj, "_TSP", &tzp->tsp);
 495         tzmon_eval_int(obj, "_TZP", &tzp->tzp);
 496 
 497         if (tzp->tzp == 0) {
 498                 tzp->polling_period = 0;
 499         } else {
 500                 if (tzp->tzp < 0)
 501                         tzp->polling_period = TZ_DEFAULT_PERIOD;
 502                 else
 503                         tzp->polling_period = tzp->tzp/10;
 504 
 505                 /* start monitor thread if needed */
 506                 if (tzp->taskq == NULL) {
 507                         char taskq_name[TZ_TASKQ_NAME_LEN];
 508 
 509                         (void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
 510                             "AcpiThermalMonitor%02d", instance);
 511                         tzp->taskq = ddi_taskq_create(tzmon_dip,
 512                             taskq_name, 1, TASKQ_DEFAULTPRI, 0);
 513                         if (tzp->taskq == NULL) {
 514                                 tzp->polling_period = 0;
 515                                 cmn_err(CE_WARN, "tzmon: could not create "
 516                                     "monitor thread for thermal zone %s - "
 517                                     "monitor by notify only",
 518                                     (char *)tzp->zone_name);
 519                         } else {
 520                                 (void) ddi_taskq_dispatch(tzp->taskq,
 521                                     tzmon_monitor, tzp, DDI_SLEEP);
 522                         }
 523                 }
 524         }
 525 
 526         mutex_exit(&tzp->lock);
 527 }
 528 
 529 
 530 /*
 531  * tzmon_zone_callback
 532  * Enumerate the thermal zone if it has a _TMP (current thermal zone
 533  * operating temperature) method.
 534  */
 535 /*ARGSUSED*/
 536 static ACPI_STATUS
 537 tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
 538 {
 539         ACPI_HANDLE tmpobj;
 540 
 541         /*
 542          * We get both ThermalZone() and Scope(\_TZ) objects here;
 543          * look for _TMP (without which a zone is invalid) to pick
 544          * between them (and ignore invalid zones)
 545          */
 546         if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
 547                 tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
 548         }
 549 
 550         return (AE_OK);
 551 }
 552 
 553 
 554 /*
 555  * tzmon_find_zones
 556  * Find all of the thermal zones by calling a ACPICA function that
 557  * walks the ACPI namespace and invokes a callback for each thermal
 558  * object found.
 559  */
 560 static void
 561 tzmon_find_zones()
 562 {
 563         ACPI_STATUS status;
 564         int retval;
 565 
 566         status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
 567             8, tzmon_zone_callback, NULL, NULL, (void **)&retval);
 568 
 569         ASSERT(status == AE_OK);
 570 }
 571 
 572 
 573 /*
 574  * tzmon_monitor
 575  * Run as a separate thread, this wakes according to polling period and
 576  * checks particular objects in the thermal zone.  One instance per
 577  * thermal zone.
 578  */
 579 static void
 580 tzmon_monitor(void *ctx)
 581 {
 582         thermal_zone_t *tzp = (thermal_zone_t *)ctx;
 583         clock_t ticks;
 584 
 585         do {
 586                 /* Check out the zone */
 587                 tzmon_eval_zone(tzp);
 588 
 589                 /* Go back to sleep */
 590                 mutex_enter(&tzp->lock);
 591                 ticks = drv_usectohz(tzp->polling_period * 1000000);
 592                 if (ticks > 0)
 593                         (void) cv_reltimedwait(&zone_list_condvar,
 594                             &tzp->lock, ticks, TR_CLOCK_TICK);
 595                 mutex_exit(&tzp->lock);
 596         } while (ticks > 0);
 597 }
 598 
 599 
 600 /*
 601  * tzmon_set_power_device
 602  */
 603 static void
 604 tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name)
 605 {
 606         ACPI_BUFFER rb;
 607         ACPI_OBJECT *pr0;
 608         ACPI_STATUS status;
 609         int i;
 610 
 611         rb.Length = ACPI_ALLOCATE_BUFFER;
 612         rb.Pointer = NULL;
 613         status = AcpiEvaluateObjectTyped(dev, "_PR0", NULL, &rb,
 614             ACPI_TYPE_PACKAGE);
 615         if (status != AE_OK) {
 616                 DTRACE_PROBE2(alx__error, int, 2, char *, tz_name);
 617                 return;
 618         }
 619 
 620         pr0 = ((ACPI_OBJECT *)rb.Pointer);
 621         for (i = 0; i < pr0->Package.Count; i++) {
 622                 status = AcpiEvaluateObject(
 623                     pr0->Package.Elements[i].Reference.Handle,
 624                     on_off ? "_ON" : "_OFF", NULL, NULL);
 625                 if (status != AE_OK) {
 626                         DTRACE_PROBE2(alx__error, int, 4, char *, tz_name);
 627                 }
 628         }
 629 
 630         AcpiOsFree(rb.Pointer);
 631 }
 632 
 633 
 634 /*
 635  * tzmon_set_power
 636  * Turn on or turn off all devices in the supplied list.
 637  */
 638 static void
 639 tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name)
 640 {
 641         ACPI_OBJECT *devs;
 642         int i;
 643 
 644         devs = ((ACPI_OBJECT *)devlist.Pointer);
 645         if (devs->Type != ACPI_TYPE_PACKAGE) {
 646                 DTRACE_PROBE2(alx__error, int, 1, char *, tz_name);
 647                 return;
 648         }
 649 
 650         for (i = 0; i < devs->Package.Count; i++)
 651                 tzmon_set_power_device(
 652                     devs->Package.Elements[i].Reference.Handle, on_off,
 653                     tz_name);
 654 }
 655 
 656 
 657 /*
 658  * tzmon_eval_zone
 659  * Evaluate the current conditions within the thermal zone.
 660  */
 661 static void
 662 tzmon_eval_zone(thermal_zone_t *tzp)
 663 {
 664         int tmp, new_level, level;
 665 
 666         mutex_enter(&tzp->lock);
 667 
 668         /* get the current temperature from ACPI */
 669         tzmon_eval_int(tzp->obj, "_TMP", &tmp);
 670         DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot,
 671             char *, (char *)tzp->zone_name);
 672 
 673         /* _HOT handling */
 674         if (tzp->hot > 0 && tmp >= tzp->hot) {
 675                 cmn_err(CE_WARN,
 676                     "tzmon: Thermal zone (%s) is too hot (%d C); "
 677                     "initiating shutdown\n",
 678                     (char *)tzp->zone_name, K_TO_C(tmp));
 679 
 680                 tzmon_do_shutdown();
 681         }
 682 
 683         /* _CRT handling */
 684         if (tzp->crt > 0 && tmp >= tzp->crt) {
 685                 cmn_err(CE_WARN,
 686                     "tzmon: Thermal zone (%s) is critically hot (%d C); "
 687                     "initiating rapid shutdown\n",
 688                     (char *)tzp->zone_name, K_TO_C(tmp));
 689 
 690                 /* shut down (fairly) immediately */
 691                 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
 692         }
 693 
 694         /*
 695          * use the temperature to determine whether the thermal zone
 696          * is at a new active cooling threshold level
 697          */
 698         for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
 699                 if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
 700                         new_level = level;
 701                         break;
 702                 }
 703         }
 704 
 705         /*
 706          * if the active cooling threshold has changed, turn off the
 707          * devices associated with the old one and turn on the new one
 708          */
 709         if (tzp->current_level != new_level) {
 710                 if ((tzp->current_level >= 0) &&
 711                     (tzp->al[tzp->current_level].Length != 0))
 712                         tzmon_set_power(tzp->al[tzp->current_level], 0,
 713                             (char *)tzp->zone_name);
 714 
 715                 if ((new_level >= 0) &&
 716                     (tzp->al[new_level].Length != 0))
 717                         tzmon_set_power(tzp->al[new_level], 1,
 718                             (char *)tzp->zone_name);
 719 
 720                 tzp->current_level = new_level;
 721         }
 722 
 723         mutex_exit(&tzp->lock);
 724 }
 725 
 726 
 727 /*
 728  * tzmon_do_shutdown
 729  * Initiates shutdown by sending a SIGPWR signal to init.
 730  */
 731 static void
 732 tzmon_do_shutdown(void)
 733 {
 734         proc_t *initpp;
 735 
 736         mutex_enter(&pidlock);
 737         initpp = prfind(P_INITPID);
 738         mutex_exit(&pidlock);
 739 
 740         /* if we can't find init, just halt */
 741         if (initpp == NULL) {
 742                 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
 743         }
 744 
 745         /* graceful shutdown with inittab and all getting involved */
 746         psignal(initpp, SIGPWR);
 747 }