xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/chicago/envd/piclenvd.c (revision f22acdfff536d452df49dd85c5ecd42092b8fcad)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains the environmental PICL plug-in module.
31  */
32 
33 /*
34  * This plugin sets up the PICLTREE for Chicago WS.
35  * It provides functionality to get/set temperatures and
36  * fan speeds.
37  *
38  * The environmental policy defaults to the auto mode
39  * as programmed by OBP at boot time.
40  */
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <sys/sysmacros.h>
45 #include <limits.h>
46 #include <string.h>
47 #include <strings.h>
48 #include <stdarg.h>
49 #include <alloca.h>
50 #include <unistd.h>
51 #include <sys/processor.h>
52 #include <syslog.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <picl.h>
56 #include <picltree.h>
57 #include <picldefs.h>
58 #include <pthread.h>
59 #include <signal.h>
60 #include <libdevinfo.h>
61 #include <sys/pm.h>
62 #include <sys/open.h>
63 #include <sys/time.h>
64 #include <sys/utsname.h>
65 #include <sys/systeminfo.h>
66 #include <note.h>
67 #include <sys/pic16f747.h>
68 #include "envd.h"
69 #include <sys/scsi/scsi.h>
70 #include <sys/scsi/generic/commands.h>
71 
72 int	debug_fd;
73 /*
74  * PICL plugin entry points
75  */
76 static void piclenvd_register(void);
77 static void piclenvd_init(void);
78 static void piclenvd_fini(void);
79 
80 /*
81  * Env setup routines
82  */
83 extern void env_picl_setup(void);
84 extern void env_picl_destroy(void);
85 extern int env_picl_setup_tuneables(void);
86 
87 static boolean_t has_fan_failed(env_fan_t *fanp);
88 
89 #pragma init(piclenvd_register)
90 
91 /*
92  * Plugin registration information
93  */
94 static picld_plugin_reg_t my_reg_info = {
95 	PICLD_PLUGIN_VERSION,
96 	PICLD_PLUGIN_CRITICAL,
97 	"SUNW_piclenvd",
98 	piclenvd_init,
99 	piclenvd_fini,
100 };
101 
102 #define	REGISTER_INFORMATION_STRING_LENGTH	16
103 static char fan_rpm_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
104 static char fan_status_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
105 
106 static int	scsi_log_sense(env_disk_t *diskp, uchar_t page_code,
107 			uchar_t *pagebuf, uint16_t pagelen);
108 static int	get_disk_temp(env_disk_t *);
109 
110 /*
111  * Env thread variables
112  */
113 static boolean_t  system_shutdown_started = B_FALSE;
114 static boolean_t  system_temp_thr_created = B_FALSE;
115 static pthread_t  system_temp_thr_id;
116 static pthread_attr_t thr_attr;
117 static boolean_t  disk_temp_thr_created = B_FALSE;
118 static pthread_t  disk_temp_thr_id;
119 static boolean_t  fan_thr_created = B_FALSE;
120 static pthread_t  fan_thr_id;
121 
122 /*
123  * PM thread related variables
124  */
125 static pthread_t	pmthr_tid;	/* pmthr thread ID */
126 static int		pm_fd = -1;	/* PM device file descriptor */
127 static boolean_t	pmthr_created = B_FALSE;
128 static int		cur_lpstate;	/* cur low power state */
129 
130 /*
131  * Envd plug-in verbose flag set by SUNW_PICLENVD_DEBUG environment var
132  * Setting the verbose tuneable also enables debugging for better
133  * control
134  */
135 int	env_debug = 0;
136 
137 /*
138  * These are debug variables for keeping track of the total number
139  * of Fan and Temp sensor retries over the lifetime of the plugin.
140  */
141 static int total_fan_retries = 0;
142 static int total_temp_retries = 0;
143 
144 /*
145  * Fan devices
146  */
147 static env_fan_t envd_system_fan0 = {
148 	ENV_SYSTEM_FAN0, ENV_SYSTEM_FAN0_DEVFS, SYSTEM_FAN0_ID,
149 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
150 };
151 static env_fan_t envd_system_fan1 = {
152 	ENV_SYSTEM_FAN1, ENV_SYSTEM_FAN1_DEVFS, SYSTEM_FAN1_ID,
153 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
154 };
155 static env_fan_t envd_system_fan2 = {
156 	ENV_SYSTEM_FAN2, ENV_SYSTEM_FAN2_DEVFS, SYSTEM_FAN2_ID,
157 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
158 };
159 static env_fan_t envd_system_fan3 = {
160 	ENV_SYSTEM_FAN3, ENV_SYSTEM_FAN3_DEVFS, SYSTEM_FAN3_ID,
161 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
162 };
163 static env_fan_t envd_system_fan4 = {
164 	ENV_SYSTEM_FAN4, ENV_SYSTEM_FAN4_DEVFS, SYSTEM_FAN4_ID,
165 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX, -1, -1,
166 };
167 
168 /*
169  * Disk devices
170  */
171 static env_disk_t envd_disk0 = {
172 	ENV_DISK0, ENV_DISK0_DEVFS, DISK0_PHYSPATH, DISK0_NODE_PATH,
173 	DISK0_ID, -1,
174 };
175 static env_disk_t envd_disk1 = {
176 	ENV_DISK1, ENV_DISK1_DEVFS, DISK1_PHYSPATH, DISK1_NODE_PATH,
177 	DISK1_ID, -1,
178 };
179 static env_disk_t envd_disk2 = {
180 	ENV_DISK2, ENV_DISK2_DEVFS, DISK2_PHYSPATH, DISK2_NODE_PATH,
181 	DISK2_ID, -1,
182 };
183 static env_disk_t envd_disk3 = {
184 	ENV_DISK3, ENV_DISK3_DEVFS, DISK3_PHYSPATH, DISK3_NODE_PATH,
185 	DISK3_ID, -1,
186 };
187 
188 /*
189  * Sensors
190  */
191 static env_sensor_t envd_sensor_cpu0 = {
192 	SENSOR_CPU0, SENSOR_CPU0_DEVFS, CPU0_SENSOR_ID, -1,
193 	CPU0_HIGH_SHUTDOWN, CPU0_HIGH_WARNING, CPU0_LOW_WARNING,
194 	CPU0_LOW_SHUTDOWN, CPU0_LOW_POWER_OFF, CPU0_HIGH_POWER_OFF,
195 };
196 static env_sensor_t envd_sensor_cpu1 = {
197 	SENSOR_CPU1, SENSOR_CPU1_DEVFS, CPU1_SENSOR_ID, -1,
198 	CPU1_HIGH_SHUTDOWN, CPU1_HIGH_WARNING, CPU1_LOW_WARNING,
199 	CPU1_LOW_SHUTDOWN, CPU1_LOW_POWER_OFF, CPU1_HIGH_POWER_OFF,
200 };
201 static env_sensor_t envd_sensor_adt7462 = {
202 	SENSOR_ADT7462, SENSOR_ADT7462_DEVFS, ADT7462_SENSOR_ID, -1,
203 	ADT7462_HIGH_SHUTDOWN, ADT7462_HIGH_WARNING, ADT7462_LOW_WARNING,
204 	ADT7462_LOW_SHUTDOWN, ADT7462_LOW_POWER_OFF, ADT7462_HIGH_POWER_OFF,
205 };
206 static env_sensor_t envd_sensor_mb = {
207 	SENSOR_MB, SENSOR_MB_DEVFS, MB_SENSOR_ID, -1,
208 	MB_HIGH_SHUTDOWN, MB_HIGH_WARNING, MB_LOW_WARNING,
209 	MB_LOW_SHUTDOWN, MB_LOW_POWER_OFF, MB_HIGH_POWER_OFF,
210 };
211 static env_sensor_t envd_sensor_lm95221 = {
212 	SENSOR_LM95221, SENSOR_LM95221_DEVFS, LM95221_SENSOR_ID, -1,
213 	LM95221_HIGH_SHUTDOWN, LM95221_HIGH_WARNING, LM95221_LOW_WARNING,
214 	LM95221_LOW_SHUTDOWN, LM95221_LOW_POWER_OFF, LM95221_HIGH_POWER_OFF,
215 };
216 static env_sensor_t envd_sensor_fire = {
217 	SENSOR_FIRE, SENSOR_FIRE_DEVFS, FIRE_SENSOR_ID, -1,
218 	FIRE_HIGH_SHUTDOWN, FIRE_HIGH_WARNING, FIRE_LOW_WARNING,
219 	FIRE_LOW_SHUTDOWN, FIRE_LOW_POWER_OFF, FIRE_HIGH_POWER_OFF,
220 };
221 static env_sensor_t envd_sensor_lsi1064 = {
222 	SENSOR_LSI1064, SENSOR_LSI1064_DEVFS, LSI1064_SENSOR_ID, -1,
223 	LSI1064_HIGH_SHUTDOWN, LSI1064_HIGH_WARNING, LSI1064_LOW_WARNING,
224 	LSI1064_LOW_SHUTDOWN, LSI1064_LOW_POWER_OFF, LSI1064_HIGH_POWER_OFF,
225 };
226 static env_sensor_t envd_sensor_front_panel = {
227 	SENSOR_FRONT_PANEL, SENSOR_FRONT_PANEL_DEVFS, FRONT_PANEL_SENSOR_ID, -1,
228 	FRONT_PANEL_HIGH_SHUTDOWN, FRONT_PANEL_HIGH_WARNING,
229 	FRONT_PANEL_LOW_WARNING, FRONT_PANEL_LOW_SHUTDOWN,
230 	FRONT_PANEL_LOW_POWER_OFF, FRONT_PANEL_HIGH_POWER_OFF,
231 };
232 
233 /*
234  * The vendor-id and device-id are the properties associated with
235  * the SCSI controller. This is used to identify a particular controller
236  * like LSI1064.
237  */
238 #define	VENDOR_ID	"vendor-id"
239 #define	DEVICE_ID	"device-id"
240 
241 /*
242  * The implementation for SCSI disk drives to supply info. about
243  * temperature is not mandatory. Hence we first determine if the
244  * temperature page is supported. To do this we need to scan the list
245  * of pages supported.
246  */
247 #define	SUPPORTED_LPAGES	0
248 #define	TEMPERATURE_PAGE	0x0D
249 #define	LOGPAGEHDRSIZE	4
250 
251 /*
252  * NULL terminated array of fans
253  */
254 static env_fan_t *envd_fans[] = {
255 	&envd_system_fan0,
256 	&envd_system_fan1,
257 	&envd_system_fan2,
258 	&envd_system_fan3,
259 	&envd_system_fan4,
260 	NULL
261 };
262 
263 /*
264  * NULL terminated array of disks
265  */
266 static env_disk_t *envd_disks[] = {
267 	&envd_disk0,
268 	&envd_disk1,
269 	&envd_disk2,
270 	&envd_disk3,
271 	NULL
272 };
273 
274 /*
275  * NULL terminated array of temperature sensors
276  */
277 #define	N_ENVD_SENSORS	8
278 static env_sensor_t *envd_sensors[] = {
279 	&envd_sensor_cpu0,
280 	&envd_sensor_cpu1,
281 	&envd_sensor_adt7462,
282 	&envd_sensor_mb,
283 	&envd_sensor_lm95221,
284 	&envd_sensor_fire,
285 	&envd_sensor_lsi1064,
286 	&envd_sensor_front_panel,
287 	NULL
288 };
289 
290 #define	NOT_AVAILABLE	"NA"
291 
292 /*
293  * Tuneables
294  */
295 #define	ENABLE	1
296 #define	DISABLE	0
297 
298 static	int	disk_high_warn_temperature	= DISK_HIGH_WARN_TEMPERATURE;
299 static	int	disk_low_warn_temperature	= DISK_LOW_WARN_TEMPERATURE;
300 static	int	disk_high_shutdown_temperature	=
301 						DISK_HIGH_SHUTDOWN_TEMPERATURE;
302 static	int	disk_low_shutdown_temperature	= DISK_LOW_SHUTDOWN_TEMPERATURE;
303 
304 static	int	disk_scan_interval		= DISK_SCAN_INTERVAL;
305 static	int	sensor_scan_interval		= SENSOR_SCAN_INTERVAL;
306 static	int	fan_scan_interval		= FAN_SCAN_INTERVAL;
307 
308 static int get_int_val(ptree_rarg_t *parg, void *buf);
309 static int set_int_val(ptree_warg_t *parg, const void *buf);
310 static int get_string_val(ptree_rarg_t *parg, void *buf);
311 static int set_string_val(ptree_warg_t *parg, const void *buf);
312 
313 static int 	shutdown_override	= 0;
314 static int	sensor_warning_interval	= SENSOR_WARNING_INTERVAL;
315 static int	sensor_warning_duration	= SENSOR_WARNING_DURATION;
316 static int	sensor_shutdown_interval = SENSOR_SHUTDOWN_INTERVAL;
317 static int	disk_warning_interval	= DISK_WARNING_INTERVAL;
318 static int	disk_warning_duration	= DISK_WARNING_DURATION;
319 static int 	disk_shutdown_interval	= DISK_SHUTDOWN_INTERVAL;
320 static int	system_temp_monitor	= 1;	/* enabled */
321 static int	fan_monitor		= 1;	/* enabled */
322 static int	pm_monitor		= 1;	/* enabled */
323 int		disk_temp_monitor	= 1;	/* enabled */
324 
325 static char	shutdown_cmd[] = SHUTDOWN_CMD;
326 
327 env_tuneable_t tuneables[] = {
328 	{"system_temp-monitor", PICL_PTYPE_INT, &system_temp_monitor,
329 	    &get_int_val, &set_int_val, sizeof (int)},
330 
331 	{"fan-monitor", PICL_PTYPE_INT, &fan_monitor,
332 	    &get_int_val, &set_int_val, sizeof (int)},
333 
334 	{"pm-monitor", PICL_PTYPE_INT, &pm_monitor,
335 	    &get_int_val, &set_int_val, sizeof (int)},
336 
337 	{"shutdown-override", PICL_PTYPE_INT, &shutdown_override,
338 	    &get_int_val, &set_int_val, sizeof (int)},
339 
340 	{"sensor-warning-duration", PICL_PTYPE_INT,
341 	    &sensor_warning_duration,
342 	    &get_int_val, &set_int_val,
343 	    sizeof (int)},
344 
345 	{"disk-scan-interval", PICL_PTYPE_INT,
346 	    &disk_scan_interval,
347 	    &get_int_val, &set_int_val,
348 	    sizeof (int)},
349 
350 	{"fan-scan-interval", PICL_PTYPE_INT,
351 	    &fan_scan_interval,
352 	    &get_int_val, &set_int_val,
353 	    sizeof (int)},
354 
355 	{"sensor-scan-interval", PICL_PTYPE_INT,
356 	    &sensor_scan_interval,
357 	    &get_int_val, &set_int_val,
358 	    sizeof (int)},
359 
360 	{"sensor_warning-interval", PICL_PTYPE_INT, &sensor_warning_interval,
361 	    &get_int_val, &set_int_val,
362 	    sizeof (int)},
363 
364 	{"sensor_shutdown-interval", PICL_PTYPE_INT, &sensor_shutdown_interval,
365 	    &get_int_val, &set_int_val,
366 	    sizeof (int)},
367 
368 	{"disk_warning-interval", PICL_PTYPE_INT, &disk_warning_interval,
369 	    &get_int_val, &set_int_val,
370 	    sizeof (int)},
371 
372 	{"disk_warning-duration", PICL_PTYPE_INT, &disk_warning_duration,
373 	    &get_int_val, &set_int_val,
374 	    sizeof (int)},
375 
376 	{"disk_shutdown-interval", PICL_PTYPE_INT, &disk_shutdown_interval,
377 	    &get_int_val, &set_int_val,
378 	    sizeof (int)},
379 
380 	{"shutdown-command", PICL_PTYPE_CHARSTRING, shutdown_cmd,
381 	    &get_string_val, &set_string_val,
382 	    sizeof (shutdown_cmd)},
383 
384 	{"monitor-disk-temp", PICL_PTYPE_INT, &disk_temp_monitor,
385 	    &get_int_val, &set_int_val, sizeof (int)},
386 
387 	{"disk-high-warn-temperature", PICL_PTYPE_INT,
388 	    &disk_high_warn_temperature, &get_int_val,
389 	    &set_int_val, sizeof (int)},
390 
391 	{"disk-low-warn-temperature", PICL_PTYPE_INT,
392 	    &disk_low_warn_temperature, &get_int_val,
393 	    &set_int_val, sizeof (int)},
394 
395 	{"disk-high-shutdown-temperature", PICL_PTYPE_INT,
396 	    &disk_high_shutdown_temperature, &get_int_val,
397 	    &set_int_val, sizeof (int)},
398 
399 	{"disk-low-shutdown-temperature", PICL_PTYPE_INT,
400 	    &disk_low_shutdown_temperature, &get_int_val,
401 	    &set_int_val, sizeof (int)},
402 
403 	{"verbose", PICL_PTYPE_INT, &env_debug,
404 	    &get_int_val, &set_int_val, sizeof (int)}
405 };
406 
407 /*
408  * We use this to figure out how many tuneables there are
409  * This is variable because the publishing routine needs this info
410  * in piclenvsetup.c
411  */
412 int	ntuneables = (sizeof (tuneables)/sizeof (tuneables[0]));
413 
414 /*
415  * Lookup fan and return a pointer to env_fan_t data structure.
416  */
417 env_fan_t *
418 fan_lookup(char *name)
419 {
420 	int		i;
421 	env_fan_t	*fanp;
422 
423 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
424 		if (strcmp(fanp->name, name) == 0)
425 			return (fanp);
426 	}
427 	return (NULL);
428 }
429 
430 /*
431  * Lookup sensor and return a pointer to env_sensor_t data structure.
432  */
433 env_sensor_t *
434 sensor_lookup(char *name)
435 {
436 	env_sensor_t	*sensorp;
437 	int		i;
438 
439 	for (i = 0; i < N_ENVD_SENSORS; ++i) {
440 		sensorp = envd_sensors[i];
441 		if (strcmp(sensorp->name, name) == 0)
442 			return (sensorp);
443 	}
444 	return (NULL);
445 }
446 
447 /*
448  * Lookup disk and return a pointer to env_disk_t data structure.
449  */
450 env_disk_t *
451 disk_lookup(char *name)
452 {
453 	int		i;
454 	env_disk_t	*diskp;
455 
456 	for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
457 		if (strncmp(diskp->name, name, strlen(name)) == 0)
458 			return (diskp);
459 	}
460 	return (NULL);
461 }
462 
463 /*
464  * Get current temperature
465  * Returns -1 on error, 0 if successful
466  */
467 int
468 get_temperature(env_sensor_t *sensorp, tempr_t *temp)
469 {
470 	int	fd = sensorp->fd;
471 	int	retval = 0;
472 
473 	if (fd == -1)
474 		retval = -1;
475 	else if (ioctl(fd, PIC_GET_TEMPERATURE, temp) != 0) {
476 
477 		retval = -1;
478 
479 		sensorp->error++;
480 
481 		if (sensorp->error == MAX_SENSOR_RETRIES) {
482 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL,
483 			    sensorp->name, errno, strerror(errno));
484 		}
485 
486 		total_temp_retries++;
487 		(void) sleep(1);
488 
489 	} else if (sensorp->error != 0) {
490 		if (sensorp->error >= MAX_SENSOR_RETRIES) {
491 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK,
492 			    sensorp->name);
493 		}
494 
495 		sensorp->error = 0;
496 
497 		if (total_temp_retries && env_debug) {
498 			envd_log(LOG_WARNING,
499 			    "Total retries for sensors = %d",
500 			    total_temp_retries);
501 		}
502 	}
503 
504 	return (retval);
505 }
506 
507 /*
508  * Get current disk temperature
509  * Returns -1 on error, 0 if successful
510  */
511 int
512 disk_temperature(env_disk_t *diskp, tempr_t *temp)
513 {
514 	int	retval = 0;
515 
516 	if (diskp == NULL)
517 		retval = -1;
518 	else
519 		*temp = diskp->current_temp;
520 
521 	return (retval);
522 }
523 
524 /*
525  * Get current fan speed
526  * This function returns a RPM value for fanspeed
527  * in fanspeedp.
528  * Returns -1 on error, 0 if successful
529  */
530 int
531 get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp)
532 {
533 	uint8_t tach;
534 	int	real_tach;
535 	int	retries;
536 
537 	if (fanp->fd == -1)
538 		return (-1);
539 
540 	if (has_fan_failed(fanp)) {
541 		*fanspeedp = 0;
542 		return (0);
543 	}
544 
545 	/* try to read the fan information */
546 	for (retries = 0; retries <= MAX_FAN_RETRIES; retries++) {
547 		if (ioctl(fanp->fd, PIC_GET_FAN_SPEED, &tach) == 0)
548 			break;
549 		(void) sleep(1);
550 	}
551 
552 	total_fan_retries += retries;
553 	if (retries == MAX_FAN_RETRIES)
554 		return (-1);
555 
556 	if (total_fan_retries && env_debug) {
557 		envd_log(LOG_WARNING, "total retries for fan = %d",
558 		    total_fan_retries);
559 	}
560 
561 	real_tach = tach << 8;
562 	*fanspeedp = TACH_TO_RPM(real_tach);
563 	return (0);
564 }
565 
566 /*
567  * Set fan speed
568  * This function accepts a percentage of fan speed
569  * from 0-100 and programs the HW monitor fans to the corresponding
570  * fanspeed value.
571  * Returns -1 on error, -2 on invalid args passed, 0 if successful
572  */
573 int
574 set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed)
575 {
576 	uint8_t	speed;
577 
578 	if (fanp->fd == -1)
579 		return (-1);
580 
581 	if (fanspeed < 0 || fanspeed > 100)
582 		return (-2);
583 
584 	speed = fanspeed;
585 	if (ioctl(fanp->fd, PIC_SET_FAN_SPEED, &speed) != 0)
586 		return (-1);
587 
588 	return (0);
589 }
590 
591 /*
592  * close all fan devices
593  */
594 static void
595 envd_close_fans(void)
596 {
597 	int		i;
598 	env_fan_t	*fanp;
599 
600 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
601 		if (fanp->fd != -1) {
602 			(void) close(fanp->fd);
603 			fanp->fd = -1;
604 		}
605 	}
606 }
607 
608 /*
609  * Close sensor devices and freeup resources
610  */
611 static void
612 envd_close_sensors(void)
613 {
614 	env_sensor_t	*sensorp;
615 	int		i;
616 
617 	for (i = 0; i < N_ENVD_SENSORS; ++i) {
618 		sensorp = envd_sensors[i];
619 		if (sensorp->fd != -1) {
620 			(void) close(sensorp->fd);
621 			sensorp->fd = -1;
622 		}
623 	}
624 }
625 
626 /*
627  * Open fan devices and initialize per fan data structure.
628  */
629 static int
630 envd_setup_fans(void)
631 {
632 	int		i, fd;
633 	env_fan_t	*fanp;
634 	int		fancnt = 0;
635 	picl_nodehdl_t tnodeh;
636 
637 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
638 		fanp->last_status = FAN_OK;
639 
640 		/* Make sure cpu0/1 present for validating cpu fans */
641 		if (fanp->id == CPU0_FAN_ID) {
642 			if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) !=
643 			    PICL_SUCCESS) {
644 					if (env_debug) {
645 						envd_log(LOG_ERR,
646 					"get node by path failed for %s\n",
647 						    CPU0_PATH);
648 					}
649 					fanp->present = B_FALSE;
650 					continue;
651 			}
652 		}
653 		if (fanp->id == CPU1_FAN_ID) {
654 			if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) !=
655 			    PICL_SUCCESS) {
656 					if (env_debug) {
657 						envd_log(LOG_ERR,
658 				"get node by path failed for %s\n", CPU0_PATH);
659 					}
660 					fanp->present = B_FALSE;
661 					continue;
662 			}
663 		}
664 		if ((fd = open(fanp->devfs_path, O_RDWR)) == -1) {
665 			envd_log(LOG_CRIT,
666 			    ENV_FAN_OPEN_FAIL, fanp->name,
667 			    fanp->devfs_path, errno, strerror(errno));
668 			fanp->present = B_FALSE;
669 			continue;
670 		}
671 		fanp->fd = fd;
672 		fanp->present = B_TRUE;
673 		fancnt++;
674 	}
675 
676 	if (fancnt == 0)
677 		return (-1);
678 
679 	return (0);
680 }
681 
682 static int
683 envd_setup_disks(void)
684 {
685 	int	ret, i, page_index, page_len;
686 	picl_nodehdl_t tnodeh;
687 	env_disk_t	*diskp;
688 	uint_t	vendor_id;
689 	uint_t	device_id;
690 	uchar_t	log_page[256];
691 
692 	if (ptree_get_node_by_path(SCSI_CONTROLLER_NODE_PATH,
693 	    &tnodeh) != PICL_SUCCESS) {
694 		if (env_debug) {
695 			envd_log(LOG_ERR,
696 "On-Board SCSI controller %s not found in the system.\n",
697 			    SCSI_CONTROLLER_NODE_PATH);
698 		}
699 		return (-1);
700 	}
701 
702 	if ((ret = ptree_get_propval_by_name(tnodeh, VENDOR_ID,
703 	    &vendor_id, sizeof (vendor_id))) != 0) {
704 		if (env_debug) {
705 			envd_log(LOG_ERR,
706 "Error in getting vendor-id for SCSI controller. ret = %d errno = 0x%d\n",
707 			    ret, errno);
708 		}
709 		return (-1);
710 	}
711 	if ((ret = ptree_get_propval_by_name(tnodeh, DEVICE_ID,
712 	    &device_id, sizeof (device_id))) != 0) {
713 		if (env_debug) {
714 			envd_log(LOG_ERR,
715 "Error in getting device-id for SCSI controller. ret = %d errno = 0x%d\n",
716 			    ret, errno);
717 		}
718 		return (-1);
719 	}
720 
721 	/*
722 	 * We have found LSI1064 SCSi controller onboard.
723 	 */
724 	for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
725 		if (ptree_get_node_by_path(diskp->nodepath,
726 		    &tnodeh) != PICL_SUCCESS) {
727 			diskp->present = B_FALSE;
728 			if (env_debug) {
729 				envd_log(LOG_ERR,
730 				    "DISK %d: %s not found in the system.\n",
731 				    diskp->id, diskp->nodepath);
732 			}
733 			continue;
734 		}
735 		if ((diskp->fd = open(diskp->devfs_path, O_RDONLY)) == -1) {
736 			diskp->present = B_FALSE;
737 			if (env_debug) {
738 				envd_log(LOG_ERR,
739 				    "Error in opening %s errno = 0x%x\n",
740 				    diskp->devfs_path, errno);
741 			}
742 			continue;
743 		}
744 		diskp->present = B_TRUE;
745 		diskp->tpage_supported = B_FALSE;
746 		diskp->warning_tstamp = 0;
747 		diskp->shutdown_tstamp = 0;
748 		diskp->high_warning = disk_high_warn_temperature;
749 		diskp->low_warning = disk_low_warn_temperature;
750 		diskp->high_shutdown = disk_high_shutdown_temperature;
751 		diskp->low_shutdown = disk_low_shutdown_temperature;
752 		/*
753 		 * Find out if the Temperature page is supported by the disk.
754 		 */
755 		if (scsi_log_sense(diskp, SUPPORTED_LPAGES, log_page,
756 		    sizeof (log_page)) != 0) {
757 			continue;
758 		}
759 
760 		page_len = ((log_page[2] << 8) & 0xFF00) | log_page[3];
761 
762 		for (page_index = LOGPAGEHDRSIZE;
763 		    page_index < page_len + LOGPAGEHDRSIZE; page_index++) {
764 			if (log_page[page_index] != TEMPERATURE_PAGE)
765 				continue;
766 
767 			diskp->tpage_supported = B_TRUE;
768 			if (env_debug) {
769 				envd_log(LOG_ERR, "tpage supported for %s\n",
770 				    diskp->nodepath);
771 			}
772 		}
773 
774 		if (get_disk_temp(diskp) < 0) {
775 			envd_log(LOG_ERR, " error reading temperature of:%s\n",
776 			    diskp->name);
777 		} else if (env_debug) {
778 			envd_log(LOG_ERR, "%s: temperature = %d\n",
779 			    diskp->name, diskp->current_temp);
780 		}
781 	}
782 
783 	return (0);
784 }
785 
786 /*
787  * Open temperature sensor devices and initialize per sensor data structure.
788  */
789 static int
790 envd_setup_sensors(void)
791 {
792 	env_sensor_t	*sensorp;
793 	int		sensorcnt = 0;
794 	int		i;
795 	picl_nodehdl_t	tnodeh;
796 
797 	for (i = 0; i < N_ENVD_SENSORS; i++) {
798 		if (env_debug)
799 			envd_log(LOG_ERR, "scanning sensor %d\n", i);
800 
801 		sensorp = envd_sensors[i];
802 
803 		/* Initialize sensor's initial state */
804 		sensorp->shutdown_initiated = B_FALSE;
805 		sensorp->warning_tstamp = 0;
806 		sensorp->shutdown_tstamp = 0;
807 		sensorp->error = 0;
808 
809 		/* Make sure cpu0/1 sensors are present */
810 		if (sensorp->id == CPU0_SENSOR_ID) {
811 			if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) !=
812 			    PICL_SUCCESS) {
813 				if (env_debug) {
814 					envd_log(LOG_ERR,
815 					"get node by path failed for %s\n",
816 					    CPU0_PATH);
817 				}
818 				sensorp->present = B_FALSE;
819 				continue;
820 			}
821 		}
822 		if (sensorp->id == CPU1_SENSOR_ID) {
823 			if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) !=
824 			    PICL_SUCCESS) {
825 				if (env_debug) {
826 					envd_log(LOG_ERR,
827 					"get node by path failed for %s\n",
828 					    CPU1_PATH);
829 				}
830 				sensorp->present = B_FALSE;
831 				continue;
832 			}
833 		}
834 
835 		sensorp->fd = open(sensorp->devfs_path, O_RDWR);
836 		if (sensorp->fd == -1) {
837 			if (env_debug) {
838 				envd_log(LOG_ERR, ENV_SENSOR_OPEN_FAIL,
839 				    sensorp->name, sensorp->devfs_path,
840 				    errno, strerror(errno));
841 			}
842 			sensorp->present = B_FALSE;
843 			continue;
844 		}
845 
846 		/*
847 		 * Determine if the front panel is attached, we want the
848 		 * information if it exists, but should not shut down
849 		 * the system if it is removed.
850 		 */
851 		if (sensorp->id == FRONT_PANEL_SENSOR_ID) {
852 			tempr_t temp;
853 			int	tries;
854 
855 			for (tries = 0; tries < MAX_SENSOR_RETRIES; tries++) {
856 				if (ioctl(sensorp->fd, PIC_GET_TEMPERATURE,
857 				    &temp) == 0) {
858 					break;
859 				}
860 				(void) sleep(1);
861 			}
862 			if (tries == MAX_SENSOR_RETRIES)
863 				sensorp->present = B_FALSE;
864 		}
865 
866 		sensorp->present = B_TRUE;
867 		sensorcnt++;
868 	}
869 
870 	if (sensorcnt == 0)
871 		return (-1);
872 
873 	return (0);
874 }
875 
876 /* ARGSUSED */
877 static void *
878 pmthr(void *args)
879 {
880 	pm_state_change_t	pmstate;
881 	char			physpath[PATH_MAX];
882 	int			pre_lpstate;
883 	uint8_t			estar_state;
884 	int			env_monitor_fd;
885 
886 	pmstate.physpath = physpath;
887 	pmstate.size = sizeof (physpath);
888 	cur_lpstate = 0;
889 	pre_lpstate = 1;
890 
891 	pm_fd = open(PM_DEVICE, O_RDWR);
892 	if (pm_fd == -1) {
893 		envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno));
894 		return (NULL);
895 	}
896 	for (;;) {
897 		/*
898 		 * Get PM state change events to check if the system
899 		 * is in lowest power state and inform PIC which controls
900 		 * fan speeds.
901 		 *
902 		 * To minimize polling, we use the blocking interface
903 		 * to get the power state change event here.
904 		 */
905 		if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) {
906 			if (errno != EINTR)
907 				break;
908 			continue;
909 		}
910 
911 		do {
912 			if (env_debug)  {
913 				envd_log(LOG_INFO,
914 				"pmstate event:0x%x flags:%x"
915 				"comp:%d oldval:%d newval:%d path:%s\n",
916 				    pmstate.event, pmstate.flags,
917 				    pmstate.component,
918 				    pmstate.old_level,
919 				    pmstate.new_level,
920 				    pmstate.physpath);
921 			}
922 			cur_lpstate =
923 			    (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0;
924 		} while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0);
925 
926 		if (pre_lpstate != cur_lpstate) {
927 			pre_lpstate = cur_lpstate;
928 			estar_state = (cur_lpstate & 0x1);
929 			if (env_debug)
930 				envd_log(LOG_ERR,
931 				    "setting PIC ESTAR SATE to %x\n",
932 				    estar_state);
933 
934 			env_monitor_fd = open(ENV_MONITOR_DEVFS, O_RDWR);
935 			if (env_monitor_fd != -1) {
936 				if (ioctl(env_monitor_fd, PIC_SET_ESTAR_MODE,
937 				    &estar_state) < 0) {
938 					if (env_debug)
939 						envd_log(LOG_ERR,
940 					"unable to set ESTAR_MODE in PIC\n");
941 				}
942 				(void) close(env_monitor_fd);
943 			} else {
944 				if (env_debug)
945 					envd_log(LOG_ERR,
946 				"Failed to open %s\n",
947 					    ENV_MONITOR_DEVFS);
948 			}
949 		}
950 	}
951 
952 	/*NOTREACHED*/
953 	return (NULL);
954 }
955 
956 /*
957  * This is env thread which monitors the current temperature when
958  * warning threshold is exceeded. The job is to make sure it does
959  * not execced/decrease shutdown threshold. If it does it will start
960  * forced shutdown to avoid reaching hardware poweroff via THERM interrupt.
961  */
962 /*ARGSUSED*/
963 static void *
964 system_temp_thr(void *args)
965 {
966 	char syscmd[BUFSIZ];
967 	char msgbuf[BUFSIZ];
968 	timespec_t	to;
969 	int	ret, i;
970 	env_sensor_t	*sensorp;
971 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
972 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
973 	time_t	ct;
974 	tempr_t  temp;
975 
976 	for (;;) {
977 		/*
978 		 * Sleep for specified seconds before issuing IOCTL
979 		 * again.
980 		 */
981 		(void) pthread_mutex_lock(&env_monitor_mutex);
982 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
983 		    &env_monitor_mutex, &to);
984 		to.tv_sec = sensor_scan_interval;
985 		to.tv_nsec = 0;
986 		if (ret != ETIMEDOUT) {
987 			(void) pthread_mutex_unlock(&env_monitor_mutex);
988 			continue;
989 		}
990 
991 		(void) pthread_mutex_unlock(&env_monitor_mutex);
992 		for (i = 0; i < N_ENVD_SENSORS; i++) {
993 			sensorp = envd_sensors[i];
994 			if (sensorp->present == B_FALSE)
995 				continue;
996 			if (get_temperature(sensorp, &temp) == -1)
997 				continue;
998 
999 			sensorp->cur_temp = temp;
1000 			if (env_debug) {
1001 				envd_log(LOG_ERR,
1002 				"%s temp = %d",
1003 				    sensorp->name, sensorp->cur_temp);
1004 			}
1005 
1006 			/*
1007 			 * If this sensor already triggered system shutdown,
1008 			 * don't log any more shutdown/warning messages for it.
1009 			 */
1010 			if (sensorp->shutdown_initiated)
1011 				continue;
1012 
1013 			/*
1014 			 * Check for the temperature in warning and shutdown
1015 			 * range and take appropriate action.
1016 			 */
1017 			if (SENSOR_TEMP_IN_WARNING_RANGE(sensorp->cur_temp,
1018 			    sensorp)) {
1019 				/*
1020 				 * Check if the temperature has been in
1021 				 * warning range during last
1022 				 * sensor_warning_duration interval. If so,
1023 				 * the temperature is truly in warning range
1024 				 * and we need to log a warning message, but
1025 				 * no more than once every
1026 				 * sensor_warning_interval seconds.
1027 				 */
1028 				time_t	wtstamp = sensorp->warning_tstamp;
1029 
1030 				ct = (time_t)(gethrtime() / NANOSEC);
1031 				if (sensorp->warning_start == 0)
1032 					sensorp->warning_start = ct;
1033 				if (((ct - sensorp->warning_start) >=
1034 				    sensor_warning_duration) &&
1035 				    (wtstamp == 0 || (ct - wtstamp) >=
1036 				    sensor_warning_interval)) {
1037 					envd_log(LOG_CRIT, ENV_WARNING_MSG,
1038 					    sensorp->name, sensorp->cur_temp,
1039 					    sensorp->low_warning,
1040 					    sensorp->high_warning);
1041 					sensorp->warning_tstamp = ct;
1042 				}
1043 			} else if (sensorp->warning_start != 0)
1044 				sensorp->warning_start = 0;
1045 
1046 			if (!shutdown_override &&
1047 			    SENSOR_TEMP_IN_SHUTDOWN_RANGE(sensorp->cur_temp,
1048 			    sensorp)) {
1049 				ct = (time_t)(gethrtime() / NANOSEC);
1050 				if (sensorp->shutdown_tstamp == 0)
1051 					sensorp->shutdown_tstamp = ct;
1052 
1053 				/*
1054 				 * Shutdown the system if the temperature
1055 				 * remains in the shutdown range for over
1056 				 * sensor_shutdown_interval seconds.
1057 				 */
1058 				if ((ct - sensorp->shutdown_tstamp) >=
1059 				    sensor_shutdown_interval) {
1060 					/*
1061 					 * Log error
1062 					 */
1063 					sensorp->shutdown_initiated = B_TRUE;
1064 					(void) snprintf(msgbuf, sizeof (msgbuf),
1065 					    ENV_SHUTDOWN_MSG, sensorp->name,
1066 					    sensorp->cur_temp,
1067 					    sensorp->low_shutdown,
1068 					    sensorp->high_shutdown);
1069 					envd_log(LOG_ALERT, msgbuf);
1070 
1071 					/*
1072 					 * Shutdown the system (only once)
1073 					 */
1074 					if (system_shutdown_started ==
1075 					    B_FALSE) {
1076 						(void) snprintf(syscmd,
1077 						    sizeof (syscmd),
1078 						    "%s \"%s\"", shutdown_cmd,
1079 						    msgbuf);
1080 
1081 						envd_log(LOG_ALERT, syscmd);
1082 						system_shutdown_started =
1083 						    B_TRUE;
1084 
1085 						(void) system(syscmd);
1086 					}
1087 				}
1088 			} else if (sensorp->shutdown_tstamp != 0)
1089 				sensorp->shutdown_tstamp = 0;
1090 
1091 		}
1092 	}	/* end of forever loop */
1093 
1094 	/*NOTREACHED*/
1095 	return (NULL);
1096 }
1097 
1098 static int
1099 scsi_log_sense(env_disk_t *diskp, uchar_t page_code, uchar_t *pagebuf,
1100 		uint16_t pagelen)
1101 {
1102 	struct uscsi_cmd	ucmd_buf;
1103 	uchar_t		cdb_buf[CDB_GROUP1];
1104 	struct	scsi_extended_sense	sense_buf;
1105 	int	ret_val;
1106 
1107 	bzero((void *)&cdb_buf, sizeof (cdb_buf));
1108 	bzero((void *)&ucmd_buf, sizeof (ucmd_buf));
1109 	bzero((void *)&sense_buf, sizeof (sense_buf));
1110 
1111 	cdb_buf[0] = SCMD_LOG_SENSE_G1;
1112 	cdb_buf[2] = (0x01 << 6) | page_code;
1113 	cdb_buf[7] = (uchar_t)((pagelen & 0xFF00) >> 8);
1114 	cdb_buf[8] = (uchar_t)(pagelen  & 0x00FF);
1115 
1116 	ucmd_buf.uscsi_cdb = (char *)cdb_buf;
1117 	ucmd_buf.uscsi_cdblen = sizeof (cdb_buf);
1118 	ucmd_buf.uscsi_bufaddr = (caddr_t)pagebuf;
1119 	ucmd_buf.uscsi_buflen = pagelen;
1120 	ucmd_buf.uscsi_rqbuf = (caddr_t)&sense_buf;
1121 	ucmd_buf.uscsi_rqlen = sizeof (struct scsi_extended_sense);
1122 	ucmd_buf.uscsi_flags = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
1123 	ucmd_buf.uscsi_timeout = 60;
1124 
1125 	ret_val = ioctl(diskp->fd, USCSICMD, ucmd_buf);
1126 	if ((ret_val == 0) && (ucmd_buf.uscsi_status == 0)) {
1127 		if (env_debug)
1128 			envd_log(LOG_ERR,
1129 		"log sense command for page_code 0x%x succeeded\n", page_code);
1130 		return (ret_val);
1131 	}
1132 	if (env_debug) {
1133 		envd_log(LOG_ERR, "log sense command for %s failed. "
1134 		    "page_code 0x%x ret_val = 0x%x "
1135 		    "status = 0x%x errno = 0x%x\n", diskp->name, page_code,
1136 		    ret_val, ucmd_buf.uscsi_status, errno);
1137 	}
1138 
1139 	return (1);
1140 }
1141 
1142 static int
1143 get_disk_temp(env_disk_t *diskp)
1144 {
1145 	int	ret;
1146 	uchar_t	tpage[256];
1147 
1148 	ret = scsi_log_sense(diskp, TEMPERATURE_PAGE, tpage, sizeof (tpage));
1149 	if (ret != 0) {
1150 		diskp->current_temp = DISK_INVALID_TEMP;
1151 		diskp->ref_temp = DISK_INVALID_TEMP;
1152 		return (-1);
1153 	}
1154 	/*
1155 	 * For the current temperature verify that the parameter
1156 	 * length is 0x02 and the parameter code is 0x00
1157 	 * Temperature value of 255(0xFF) is considered INVALID.
1158 	 */
1159 	if ((tpage[7] == 0x02) && (tpage[4] == 0x00) &&
1160 	    (tpage[5] == 0x00)) {
1161 		if (tpage[9] == 0xFF) {
1162 			diskp->current_temp = DISK_INVALID_TEMP;
1163 			return (-1);
1164 		} else {
1165 			diskp->current_temp = tpage[9];
1166 		}
1167 	}
1168 
1169 	/*
1170 	 * For the reference temperature verify that the parameter
1171 	 * length is 0x02 and the parameter code is 0x01
1172 	 * Temperature value of 255(0xFF) is considered INVALID.
1173 	 */
1174 	if ((tpage[13] == 0x02) && (tpage[10] == 0x00) &&
1175 	    (tpage[11] == 0x01)) {
1176 		if (tpage[15] == 0xFF) {
1177 			diskp->ref_temp = DISK_INVALID_TEMP;
1178 		} else {
1179 			diskp->ref_temp = tpage[15];
1180 		}
1181 	}
1182 	return (0);
1183 }
1184 
1185 /* ARGSUSED */
1186 static void *
1187 disk_temp_thr(void *args)
1188 {
1189 	char syscmd[BUFSIZ];
1190 	char msgbuf[BUFSIZ];
1191 	timespec_t	to;
1192 	int	ret, i;
1193 	env_disk_t	*diskp;
1194 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1195 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1196 	pm_state_change_t	pmstate;
1197 	int	idle_time;
1198 	int	disk_pm_fd;
1199 	time_t	ct;
1200 
1201 	if ((disk_pm_fd = open(PM_DEVICE, O_RDWR)) == -1) {
1202 		envd_log(LOG_ERR, DISK_TEMP_THREAD_EXITING,
1203 		    errno, strerror(errno));
1204 		return (NULL);
1205 	}
1206 
1207 	for (;;) {
1208 		/*
1209 		 * Sleep for specified seconds before issuing IOCTL
1210 		 * again.
1211 		 */
1212 		(void) pthread_mutex_lock(&env_monitor_mutex);
1213 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1214 		    &env_monitor_mutex, &to);
1215 
1216 		to.tv_sec = disk_scan_interval;
1217 		to.tv_nsec = 0;
1218 
1219 		if (ret != ETIMEDOUT) {
1220 			(void) pthread_mutex_unlock(
1221 			    &env_monitor_mutex);
1222 			continue;
1223 		}
1224 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1225 
1226 		for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
1227 			if (diskp->present == B_FALSE)
1228 				continue;
1229 			if (diskp->tpage_supported == B_FALSE)
1230 				continue;
1231 		/*
1232 		 * If the disk temperature is above the warning threshold
1233 		 * continue monitoring until the temperature drops below
1234 		 * warning threshold.
1235 		 * if the temperature is in the NORMAL range monitor only
1236 		 * when the disk is BUSY.
1237 		 * We do not want to read the disk temperature if the disk is
1238 		 * is idling. The reason for this is disk will never get into
1239 		 * lowest power mode if we scan the disk temperature
1240 		 * peridoically. To avoid this situation we first determine
1241 		 * the idle_time of the disk. If the disk has been IDLE since
1242 		 * we scanned the temperature last time we will not read the
1243 		 * temperature.
1244 		 */
1245 		if (!DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp, diskp)) {
1246 			pmstate.physpath = diskp->physpath;
1247 			pmstate.size = strlen(diskp->physpath);
1248 			pmstate.component = 0;
1249 			if ((idle_time =
1250 			    ioctl(disk_pm_fd, PM_GET_TIME_IDLE,
1251 			    &pmstate)) == -1) {
1252 
1253 				if (errno != EINTR) {
1254 					if (env_debug)
1255 						envd_log(LOG_ERR,
1256 			"ioctl PM_GET_TIME_IDLE failed for DISK0. errno=0x%x\n",
1257 						    errno);
1258 					continue;
1259 				}
1260 				continue;
1261 			}
1262 			if (idle_time >= (disk_scan_interval/2)) {
1263 				if (env_debug) {
1264 					envd_log(LOG_ERR, "%s idle time = %d\n",
1265 					    diskp->name, idle_time);
1266 				}
1267 				continue;
1268 			}
1269 		}
1270 		ret = get_disk_temp(diskp);
1271 		if (ret != 0)
1272 			continue;
1273 		if (env_debug) {
1274 			envd_log(LOG_ERR, "%s temp = %d ref. temp = %d\n",
1275 			    diskp->name, diskp->current_temp, diskp->ref_temp);
1276 		}
1277 		/*
1278 		 * If this disk already triggered system shutdown, don't
1279 		 * log any more shutdown/warning messages for it.
1280 		 */
1281 		if (diskp->shutdown_initiated)
1282 			continue;
1283 
1284 		/*
1285 		 * Check for the temperature in warning and shutdown range
1286 		 * and take appropriate action.
1287 		 */
1288 		if (DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp, diskp)) {
1289 			/*
1290 			 * Check if the temperature has been in warning
1291 			 * range during last disk_warning_duration interval.
1292 			 * If so, the temperature is truly in warning
1293 			 * range and we need to log a warning message,
1294 			 * but no more than once every disk_warning_interval
1295 			 * seconds.
1296 			 */
1297 			time_t	wtstamp = diskp->warning_tstamp;
1298 
1299 			ct = (time_t)(gethrtime() / NANOSEC);
1300 			if (diskp->warning_start == 0)
1301 				diskp->warning_start = ct;
1302 			if (((ct - diskp->warning_start) >=
1303 			    disk_warning_duration) && (wtstamp == 0 ||
1304 			    (ct - wtstamp) >= disk_warning_interval)) {
1305 				envd_log(LOG_CRIT, ENV_WARNING_MSG,
1306 				    diskp->name, diskp->current_temp,
1307 				    diskp->low_warning,
1308 				    diskp->high_warning);
1309 				diskp->warning_tstamp = ct;
1310 			}
1311 		} else if (diskp->warning_start != 0)
1312 			diskp->warning_start = 0;
1313 
1314 		if (!shutdown_override &&
1315 		    DISK_TEMP_IN_SHUTDOWN_RANGE(diskp->current_temp, diskp)) {
1316 			ct = (time_t)(gethrtime() / NANOSEC);
1317 			if (diskp->shutdown_tstamp == 0)
1318 				diskp->shutdown_tstamp = ct;
1319 
1320 			/*
1321 			 * Shutdown the system if the temperature remains
1322 			 * in the shutdown range for over disk_shutdown_interval
1323 			 * seconds.
1324 			 */
1325 			if ((ct - diskp->shutdown_tstamp) >=
1326 			    disk_shutdown_interval) {
1327 				/* log error */
1328 				diskp->shutdown_initiated = B_TRUE;
1329 				(void) snprintf(msgbuf, sizeof (msgbuf),
1330 				    ENV_SHUTDOWN_MSG, diskp->name,
1331 				    diskp->current_temp, diskp->low_shutdown,
1332 				    diskp->high_shutdown);
1333 				envd_log(LOG_ALERT, msgbuf);
1334 
1335 				/* shutdown the system (only once) */
1336 				if (system_shutdown_started == B_FALSE) {
1337 					(void) snprintf(syscmd, sizeof (syscmd),
1338 					    "%s \"%s\"", shutdown_cmd, msgbuf);
1339 					envd_log(LOG_ALERT, syscmd);
1340 					system_shutdown_started = B_TRUE;
1341 					(void) system(syscmd);
1342 				}
1343 			}
1344 		} else if (diskp->shutdown_tstamp != 0)
1345 			diskp->shutdown_tstamp = 0;
1346 		}
1347 	}	/* end of forever loop */
1348 }
1349 
1350 static void *
1351 fan_thr(void *args)
1352 {
1353 	char msgbuf[BUFSIZ];
1354 	timespec_t	to;
1355 	int	ret, i;
1356 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1357 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1358 	env_fan_t	*fanp;
1359 
1360 #ifdef	__lint
1361 	args = args;
1362 #endif
1363 
1364 	for (;;) {
1365 		/*
1366 		 * Sleep for specified seconds before issuing IOCTL
1367 		 * again.
1368 		 */
1369 		(void) pthread_mutex_lock(&env_monitor_mutex);
1370 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1371 		    &env_monitor_mutex, &to);
1372 		to.tv_sec = fan_scan_interval;
1373 		to.tv_nsec = 0;
1374 		if (ret != ETIMEDOUT) {
1375 			(void) pthread_mutex_unlock(&env_monitor_mutex);
1376 			continue;
1377 		}
1378 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1379 
1380 		for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
1381 			if (fanp->present == B_FALSE)
1382 				continue;
1383 			/*
1384 			 * We initiate shutdown if fan status indicates
1385 			 * failure. Also, don't warn repeatedly.
1386 			 */
1387 			if (has_fan_failed(fanp) == B_TRUE) {
1388 				if (fanp->last_status == FAN_FAILED)
1389 					continue;
1390 				fanp->last_status = FAN_FAILED;
1391 				(void) snprintf(msgbuf, sizeof (msgbuf),
1392 				    ENV_FAN_FAILURE_WARNING_MSG, fanp->name,
1393 				    fan_rpm_string, fan_status_string);
1394 				envd_log(LOG_ALERT, msgbuf);
1395 			} else {
1396 				if (fanp->last_status == FAN_OK)
1397 					continue;
1398 				fanp->last_status = FAN_OK;
1399 				(void) snprintf(msgbuf, sizeof (msgbuf),
1400 				    ENV_FAN_OK_MSG, fanp->name);
1401 				envd_log(LOG_ALERT, msgbuf);
1402 			}
1403 		}
1404 	}
1405 
1406 	/*NOTREACHED*/
1407 	return (NULL);
1408 }
1409 
1410 /*
1411  * Setup envrionmental monitor state and start threads to monitor
1412  * temperature, fan, disk and power management state.
1413  * Returns -1 on error, 0 if successful.
1414  */
1415 static int
1416 envd_setup(void)
1417 {
1418 
1419 	if (getenv("SUNW_piclenvd_debug") != NULL)
1420 		env_debug = 1;
1421 
1422 	if (pthread_attr_init(&thr_attr) != 0 ||
1423 	    pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0) {
1424 		return (-1);
1425 	}
1426 
1427 	if (envd_setup_sensors() < 0) {
1428 		if (env_debug)
1429 			envd_log(LOG_ERR, "Failed to setup sensors\n");
1430 		system_temp_monitor = 0;
1431 	}
1432 
1433 	if (envd_setup_fans() < 0) {
1434 		if (env_debug)
1435 			envd_log(LOG_ERR, "Failed to setup fans\n");
1436 		fan_monitor = 0;
1437 		pm_monitor = 0;
1438 	}
1439 
1440 	if (envd_setup_disks() < 0) {
1441 		if (env_debug)
1442 			envd_log(LOG_ERR, "Failed to setup disks\n");
1443 		disk_temp_monitor = 0;
1444 	}
1445 
1446 	if ((system_temp_monitor) && (system_temp_thr_created == B_FALSE)) {
1447 		if (pthread_create(&system_temp_thr_id, &thr_attr,
1448 		    system_temp_thr, NULL) != 0) {
1449 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
1450 		} else {
1451 			system_temp_thr_created = B_TRUE;
1452 			if (env_debug)
1453 				envd_log(LOG_ERR,
1454 			"Created thread to monitor system temperatures\n");
1455 		}
1456 	}
1457 
1458 	if ((fan_monitor) && (fan_thr_created == B_FALSE)) {
1459 		if (pthread_create(&fan_thr_id, &thr_attr, fan_thr, NULL) != 0)
1460 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
1461 		else {
1462 			fan_thr_created = B_TRUE;
1463 			if (env_debug) {
1464 				envd_log(LOG_ERR,
1465 				    "Created thread to monitor system fans\n");
1466 			}
1467 		}
1468 	}
1469 
1470 	if ((pm_monitor) && (pmthr_created == B_FALSE)) {
1471 		if (pthread_create(&pmthr_tid, &thr_attr, pmthr, NULL) != 0)
1472 			envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED);
1473 		else {
1474 			pmthr_created = B_TRUE;
1475 			if (env_debug)
1476 				envd_log(LOG_ERR,
1477 			"Created thread to monitor system power state\n");
1478 		}
1479 	}
1480 
1481 	if ((disk_temp_monitor) && (disk_temp_thr_created == B_FALSE)) {
1482 		if (pthread_create(&disk_temp_thr_id, &thr_attr,
1483 		    disk_temp_thr, NULL) != 0) {
1484 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
1485 		} else {
1486 			disk_temp_thr_created = B_TRUE;
1487 			if (env_debug)
1488 				envd_log(LOG_ERR,
1489 			"Created thread for disk temperatures\n");
1490 		}
1491 	}
1492 
1493 	return (0);
1494 }
1495 
1496 static void
1497 piclenvd_register(void)
1498 {
1499 	picld_plugin_register(&my_reg_info);
1500 }
1501 
1502 static void
1503 piclenvd_init(void)
1504 {
1505 
1506 	(void) env_picl_setup_tuneables();
1507 
1508 	/*
1509 	 * Setup the environmental data structures
1510 	 */
1511 	if (envd_setup() != 0) {
1512 		envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED);
1513 		return;
1514 	}
1515 
1516 	/*
1517 	 * Now setup/populate PICL tree
1518 	 */
1519 	env_picl_setup();
1520 }
1521 
1522 static void
1523 piclenvd_fini(void)
1524 {
1525 
1526 	/*
1527 	 * Invoke env_picl_destroy() to remove any PICL nodes/properties
1528 	 * (including volatile properties) we created. Once this call
1529 	 * returns, there can't be any more calls from the PICL framework
1530 	 * to get current temperature or fan speed.
1531 	 */
1532 	env_picl_destroy();
1533 	envd_close_sensors();
1534 	envd_close_fans();
1535 }
1536 
1537 /*VARARGS2*/
1538 void
1539 envd_log(int pri, const char *fmt, ...)
1540 {
1541 	va_list	ap;
1542 
1543 	va_start(ap, fmt);
1544 	vsyslog(pri, fmt, ap);
1545 	va_end(ap);
1546 }
1547 
1548 /*
1549  * Tunables support functions
1550  */
1551 static env_tuneable_t *
1552 tuneable_lookup(picl_prophdl_t proph)
1553 {
1554 	int i;
1555 	env_tuneable_t	*tuneablep = NULL;
1556 
1557 	for (i = 0; i < ntuneables; i++) {
1558 		tuneablep = &tuneables[i];
1559 		if (tuneablep->proph == proph)
1560 			return (tuneablep);
1561 	}
1562 
1563 	return (NULL);
1564 }
1565 
1566 static int
1567 get_string_val(ptree_rarg_t *parg, void *buf)
1568 {
1569 	picl_prophdl_t	proph;
1570 	env_tuneable_t	*tuneablep;
1571 
1572 	proph = parg->proph;
1573 
1574 	tuneablep = tuneable_lookup(proph);
1575 
1576 	if (tuneablep == NULL)
1577 		return (PICL_FAILURE);
1578 
1579 	(void) memcpy(buf, (caddr_t)tuneablep->value,
1580 	    tuneablep->nbytes);
1581 
1582 	return (PICL_SUCCESS);
1583 }
1584 
1585 static int
1586 set_string_val(ptree_warg_t *parg, const void *buf)
1587 {
1588 	picl_prophdl_t	proph;
1589 	env_tuneable_t	*tuneablep;
1590 
1591 	if (parg->cred.dc_euid != 0)
1592 		return (PICL_PERMDENIED);
1593 
1594 	proph = parg->proph;
1595 
1596 	tuneablep = tuneable_lookup(proph);
1597 
1598 	if (tuneablep == NULL)
1599 		return (PICL_FAILURE);
1600 
1601 	(void) memcpy((caddr_t)tuneables->value, (caddr_t)buf,
1602 	    tuneables->nbytes);
1603 
1604 
1605 	return (PICL_SUCCESS);
1606 }
1607 
1608 static int
1609 get_int_val(ptree_rarg_t *parg, void *buf)
1610 {
1611 	picl_prophdl_t	proph;
1612 	env_tuneable_t	*tuneablep;
1613 
1614 	proph = parg->proph;
1615 
1616 	tuneablep = tuneable_lookup(proph);
1617 
1618 	if (tuneablep == NULL)
1619 		return (PICL_FAILURE);
1620 
1621 	(void) memcpy((int *)buf, (int *)tuneablep->value,
1622 	    tuneablep->nbytes);
1623 
1624 	return (PICL_SUCCESS);
1625 }
1626 
1627 static int
1628 set_int_val(ptree_warg_t *parg, const void *buf)
1629 {
1630 	picl_prophdl_t	proph;
1631 	env_tuneable_t	*tuneablep;
1632 
1633 	if (parg->cred.dc_euid != 0)
1634 		return (PICL_PERMDENIED);
1635 
1636 	proph = parg->proph;
1637 
1638 	tuneablep = tuneable_lookup(proph);
1639 
1640 	if (tuneablep == NULL)
1641 		return (PICL_FAILURE);
1642 
1643 	(void) memcpy((int *)tuneablep->value, (int *)buf,
1644 	    tuneablep->nbytes);
1645 
1646 	return (PICL_SUCCESS);
1647 }
1648 
1649 boolean_t
1650 has_fan_failed(env_fan_t *fanp)
1651 {
1652 	fanspeed_t	fan_speed;
1653 	uchar_t		status;
1654 	uint8_t		tach;
1655 	int		real_tach;
1656 	int		ret, ntries;
1657 
1658 	if (fanp->fd == -1)
1659 		return (B_TRUE);
1660 
1661 	/*
1662 	 * Read RF_FAN_STATUS bit of the fan fault register, retry if
1663 	 * the PIC is busy, with a 1 second delay to allow it to update.
1664 	 */
1665 	for (ntries = 0; ntries < MAX_RETRIES_FOR_FAN_FAULT; ntries++) {
1666 		ret = ioctl(fanp->fd, PIC_GET_FAN_STATUS, &status);
1667 		if ((ret == 0) && ((status & 0x1) == 0))
1668 			break;
1669 		(void) sleep(1);
1670 	}
1671 
1672 	if (ntries > 0) {
1673 		if (env_debug) {
1674 			envd_log(LOG_ERR,
1675 			    "%d retries attempted in reading fan status.\n",
1676 			    ntries);
1677 		}
1678 	}
1679 
1680 	if (ntries == MAX_RETRIES_FOR_FAN_FAULT) {
1681 		(void) strncpy(fan_status_string, NOT_AVAILABLE,
1682 		    sizeof (fan_status_string));
1683 		(void) strncpy(fan_rpm_string, NOT_AVAILABLE,
1684 		    sizeof (fan_rpm_string));
1685 		return (B_TRUE);
1686 	}
1687 
1688 	if (env_debug)
1689 		envd_log(LOG_ERR, "fan status = 0x%x\n", status);
1690 
1691 	/*
1692 	 * ST_FFAULT bit isn't implemented yet and we're reading only
1693 	 * individual fan status
1694 	 */
1695 	if (status & 0x1) {
1696 		(void) snprintf(fan_status_string, sizeof (fan_status_string),
1697 		    "0x%x", status);
1698 		if (ioctl(fanp->fd, PIC_GET_FAN_SPEED, &tach) != 0) {
1699 			(void) strncpy(fan_rpm_string, NOT_AVAILABLE,
1700 			    sizeof (fan_rpm_string));
1701 		} else {
1702 			real_tach = tach << 8;
1703 			fan_speed = TACH_TO_RPM(real_tach);
1704 			(void) snprintf(fan_rpm_string, sizeof (fan_rpm_string),
1705 			    "%d", fan_speed);
1706 		}
1707 		return (B_TRUE);
1708 	}
1709 
1710 	return (B_FALSE);
1711 }
1712