xref: /illumos-gate/usr/src/uts/i86pc/io/tzmon/tzmon.c (revision 802b83c445ef5ffc9777155491dfe4fcd9793946)
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 2007 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <sys/errno.h>
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/open.h>
37 #include <sys/stat.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/ksynch.h>
41 #include <sys/uadmin.h>
42 #include <sys/acpi/acpi.h>
43 #include <sys/acpica.h>
44 #include <sys/sdt.h>
45 
46 #include "tzmon.h"
47 
48 
49 #define	TZMON_ENUM_TRIP_POINTS	1
50 #define	TZMON_ENUM_DEV_LISTS	2
51 #define	TZMON_ENUM_ALL		(TZMON_ENUM_TRIP_POINTS	| TZMON_ENUM_DEV_LISTS)
52 
53 /*
54  * TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor"
55  * plus a two-digit instance number plus a NULL.  If the taskq name is changed
56  * (particularly if it is lengthened), then this value needs to change.
57  */
58 #define	TZ_TASKQ_NAME_LEN	21
59 
60 /*
61  * Kelvin to Celsius conversion
62  * The formula for converting degrees Kelvin to degrees Celsius is
63  * C = K - 273.15 (we round to 273.2).  The unit for thermal zone
64  * temperatures is tenths of a degree Kelvin.  Use tenth of a degree
65  * to convert, then make a whole number out of it.
66  */
67 #define	K_TO_C(temp)		(((temp) - 2732) / 10)
68 
69 
70 /* cb_ops or dev_ops forward declarations */
71 static	int	tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
72     void *arg, void **result);
73 static	int	tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
74 static	int	tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
75 
76 /* other forward declarations */
77 static void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx);
78 static void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv);
79 static thermal_zone_t *tzmon_alloc_zone();
80 static void tzmon_free_zone_list();
81 static void tzmon_discard_buffers(thermal_zone_t *tzp);
82 static void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp,
83 	int enum_flag);
84 static ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest,
85     void *ctx, void **rv);
86 static void tzmon_find_zones(void);
87 static void tzmon_monitor(void *ctx);
88 static void tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name);
89 static void tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name);
90 static void tzmon_eval_zone(thermal_zone_t *tzp);
91 static void tzmon_do_shutdown(void);
92 
93 extern void halt(char *);
94 
95 static struct cb_ops	tzmon_cb_ops = {
96 	nodev,			/* no open routine	*/
97 	nodev,			/* no close routine	*/
98 	nodev,			/* not a block driver	*/
99 	nodev,			/* no print routine	*/
100 	nodev,			/* no dump routine	*/
101 	nodev,			/* no read routine	*/
102 	nodev,			/* no write routine	*/
103 	nodev,			/* no ioctl routine	*/
104 	nodev,			/* no devmap routine	*/
105 	nodev,			/* no mmap routine	*/
106 	nodev,			/* no segmap routine	*/
107 	nochpoll,		/* no chpoll routine	*/
108 	ddi_prop_op,
109 	0,			/* not a STREAMS driver	*/
110 	D_NEW | D_MP,		/* safe for multi-thread/multi-processor */
111 };
112 
113 static struct dev_ops tzmon_ops = {
114 	DEVO_REV,		/* devo_rev */
115 	0,			/* devo_refcnt */
116 	tzmon_getinfo,		/* devo_getinfo */
117 	nulldev,		/* devo_identify */
118 	nulldev,		/* devo_probe */
119 	tzmon_attach,		/* devo_attach */
120 	tzmon_detach,		/* devo_detach */
121 	nodev,			/* devo_reset */
122 	&tzmon_cb_ops,		/* devo_cb_ops */
123 	(struct bus_ops *)0,	/* devo_bus_ops */
124 	NULL,			/* devo_power */
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 (AcpiEvaluateObject(obj, abuf, NULL,
475 				    &tzp->al[level]) != AE_OK) {
476 					DTRACE_PROBE2(alx__missing, int, level,
477 					    char *, (char *)tzp->zone_name);
478 
479 					tzp->al[level].Length = 0;
480 					tzp->al[level].Pointer = NULL;
481 				}
482 			}
483 		}
484 
485 		tzp->psl.Length = ACPI_ALLOCATE_BUFFER;
486 		tzp->psl.Pointer = NULL;
487 		(void) AcpiEvaluateObject(obj, "_PSL", NULL, &tzp->psl);
488 	}
489 
490 	tzmon_eval_int(obj, "_TC1", &tzp->tc1);
491 	tzmon_eval_int(obj, "_TC2", &tzp->tc2);
492 	tzmon_eval_int(obj, "_TSP", &tzp->tsp);
493 	tzmon_eval_int(obj, "_TZP", &tzp->tzp);
494 
495 	if (tzp->tzp == 0) {
496 		tzp->polling_period = 0;
497 	} else {
498 		if (tzp->tzp < 0)
499 			tzp->polling_period = TZ_DEFAULT_PERIOD;
500 		else
501 			tzp->polling_period = tzp->tzp/10;
502 
503 		/* start monitor thread if needed */
504 		if (tzp->taskq == NULL) {
505 			char taskq_name[TZ_TASKQ_NAME_LEN];
506 
507 			(void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
508 			    "AcpiThermalMonitor%02d", instance);
509 			tzp->taskq = ddi_taskq_create(tzmon_dip,
510 			    taskq_name, 1, TASKQ_DEFAULTPRI, 0);
511 			if (tzp->taskq == NULL) {
512 				tzp->polling_period = 0;
513 				cmn_err(CE_WARN, "tzmon: could not create "
514 				    "monitor thread for thermal zone %s - "
515 				    "monitor by notify only",
516 				    (char *)tzp->zone_name);
517 			} else {
518 				(void) ddi_taskq_dispatch(tzp->taskq,
519 				    tzmon_monitor, tzp, DDI_SLEEP);
520 			}
521 		}
522 	}
523 
524 	mutex_exit(&tzp->lock);
525 }
526 
527 
528 /*
529  * tzmon_zone_callback
530  * Enumerate the thermal zone if it has a _TMP (current thermal zone
531  * operating temperature) method.
532  */
533 /*ARGSUSED*/
534 static ACPI_STATUS
535 tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
536 {
537 	ACPI_HANDLE tmpobj;
538 
539 	/*
540 	 * We get both ThermalZone() and Scope(\_TZ) objects here;
541 	 * look for _TMP (without which a zone is invalid) to pick
542 	 * between them (and ignore invalid zones)
543 	 */
544 	if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
545 		tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
546 	}
547 
548 	return (AE_OK);
549 }
550 
551 
552 /*
553  * tzmon_find_zones
554  * Find all of the thermal zones by calling a ACPICA function that
555  * walks the ACPI namespace and invokes a callback for each thermal
556  * object found.
557  */
558 static void
559 tzmon_find_zones()
560 {
561 	ACPI_STATUS status;
562 	int retval;
563 
564 	status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
565 	    8, tzmon_zone_callback, NULL, (void **)&retval);
566 
567 	ASSERT(status == AE_OK);
568 }
569 
570 
571 /*
572  * tzmon_monitor
573  * Run as a separate thread, this wakes according to polling period and
574  * checks particular objects in the thermal zone.  One instance per
575  * thermal zone.
576  */
577 static void
578 tzmon_monitor(void *ctx)
579 {
580 	thermal_zone_t *tzp = (thermal_zone_t *)ctx;
581 	clock_t ticks;
582 
583 	do {
584 		/* Check out the zone */
585 		tzmon_eval_zone(tzp);
586 
587 		/* Go back to sleep */
588 		mutex_enter(&tzp->lock);
589 		ticks = drv_usectohz(tzp->polling_period * 1000000);
590 		if (ticks > 0)
591 			(void) cv_timedwait(&zone_list_condvar, &tzp->lock,
592 			    ddi_get_lbolt() + ticks);
593 		mutex_exit(&tzp->lock);
594 	} while (ticks > 0);
595 }
596 
597 
598 /*
599  * tzmon_set_power_device
600  */
601 static void
602 tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name)
603 {
604 	ACPI_BUFFER rb;
605 	ACPI_OBJECT *pr0;
606 	ACPI_STATUS status;
607 	int i;
608 
609 	rb.Length = ACPI_ALLOCATE_BUFFER;
610 	rb.Pointer = NULL;
611 	status = AcpiEvaluateObject(dev, "_PR0", NULL, &rb);
612 	if (status != AE_OK) {
613 		DTRACE_PROBE2(alx__error, int, 2, char *, tz_name);
614 		return;
615 	}
616 
617 	pr0 = ((ACPI_OBJECT *)rb.Pointer);
618 	if (pr0->Type != ACPI_TYPE_PACKAGE) {
619 		DTRACE_PROBE2(alx__error, int, 3, char *, tz_name);
620 		AcpiOsFree(rb.Pointer);
621 		return;
622 	}
623 
624 	for (i = 0; i < pr0->Package.Count; i++) {
625 		status = AcpiEvaluateObject(
626 		    pr0->Package.Elements[i].Reference.Handle,
627 		    on_off ? "_ON" : "_OFF", NULL, NULL);
628 		if (status != AE_OK) {
629 			DTRACE_PROBE2(alx__error, int, 4, char *, tz_name);
630 		}
631 	}
632 
633 	AcpiOsFree(rb.Pointer);
634 }
635 
636 
637 /*
638  * tzmon_set_power
639  * Turn on or turn off all devices in the supplied list.
640  */
641 static void
642 tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name)
643 {
644 	ACPI_OBJECT *devs;
645 	int i;
646 
647 	devs = ((ACPI_OBJECT *)devlist.Pointer);
648 	if (devs->Type != ACPI_TYPE_PACKAGE) {
649 		DTRACE_PROBE2(alx__error, int, 1, char *, tz_name);
650 		return;
651 	}
652 
653 	for (i = 0; i < devs->Package.Count; i++)
654 		tzmon_set_power_device(
655 		    devs->Package.Elements[i].Reference.Handle, on_off,
656 		    tz_name);
657 }
658 
659 
660 /*
661  * tzmon_eval_zone
662  * Evaluate the current conditions within the thermal zone.
663  */
664 static void
665 tzmon_eval_zone(thermal_zone_t *tzp)
666 {
667 	int tmp, new_level, level;
668 
669 	mutex_enter(&tzp->lock);
670 
671 	/* get the current temperature from ACPI */
672 	tzmon_eval_int(tzp->obj, "_TMP", &tmp);
673 	DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot,
674 	    char *, (char *)tzp->zone_name);
675 
676 	/* _HOT handling */
677 	if (tzp->hot > 0 && tmp >= tzp->hot) {
678 		cmn_err(CE_WARN,
679 		    "tzmon: Thermal zone (%s) is too hot (%d C); "
680 		    "initiating shutdown\n",
681 		    (char *)tzp->zone_name, K_TO_C(tmp));
682 
683 		tzmon_do_shutdown();
684 	}
685 
686 	/* _CRT handling */
687 	if (tzp->crt > 0 && tmp >= tzp->crt) {
688 		cmn_err(CE_WARN,
689 		    "tzmon: Thermal zone (%s) is critically hot (%d C); "
690 		    "initiating rapid shutdown\n",
691 		    (char *)tzp->zone_name, K_TO_C(tmp));
692 
693 		/* shut down (fairly) immediately */
694 		mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
695 	}
696 
697 	/*
698 	 * use the temperature to determine whether the thermal zone
699 	 * is at a new active cooling threshold level
700 	 */
701 	for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
702 		if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
703 			new_level = level;
704 			break;
705 		}
706 	}
707 
708 	/*
709 	 * if the active cooling threshold has changed, turn off the
710 	 * devices associated with the old one and turn on the new one
711 	 */
712 	if (tzp->current_level != new_level) {
713 		if ((tzp->current_level >= 0) &&
714 		    (tzp->al[tzp->current_level].Length != 0))
715 			tzmon_set_power(tzp->al[tzp->current_level], 0,
716 			    (char *)tzp->zone_name);
717 
718 		if ((new_level >= 0) &&
719 		    (tzp->al[new_level].Length != 0))
720 			tzmon_set_power(tzp->al[new_level], 1,
721 			    (char *)tzp->zone_name);
722 
723 		tzp->current_level = new_level;
724 	}
725 
726 	mutex_exit(&tzp->lock);
727 }
728 
729 
730 /*
731  * tzmon_do_shutdown
732  * Initiates shutdown by sending a SIGPWR signal to init.
733  */
734 static void
735 tzmon_do_shutdown(void)
736 {
737 	proc_t *initpp;
738 
739 	mutex_enter(&pidlock);
740 	initpp = prfind(P_INITPID);
741 	mutex_exit(&pidlock);
742 
743 	/* if we can't find init, just halt */
744 	if (initpp == NULL) {
745 		mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
746 	}
747 
748 	/* graceful shutdown with inittab and all getting involved */
749 	psignal(initpp, SIGPWR);
750 }
751