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
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.
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <sys/systeminfo.h>
39 #include <libnvpair.h>
44 #include <sys/sysevent/dr.h>
45 #include "piclenvmond.h"
49 #define RESET_CPU "/usr/sbin/shutdown -y -g 0 -i6"
50 #define SHUTDOWN_CPU "/usr/sbin/shutdown -y -g 0 -i0"
51 #define RCM_ABSTRACT_RESOURCE "SUNW_snowbird/board0/CPU1"
52 #define CPU_SENSOR_GEO_ADDR 0xe
53 #define IS_HEALTHY 0x01
54 #define PICL_NODE_SYSMGMT "sysmgmt"
55 #define SYSMGMT_PATH PLATFORM_PATH"/pci/pci/isa/sysmgmt"
58 /* external functions */
59 extern picl_errno_t
env_create_property(int, int, size_t, char *,
60 int (*readfn
)(ptree_rarg_t
*, void *),
61 int (*writefn
)(ptree_warg_t
*, const void *),
62 picl_nodehdl_t
, picl_prophdl_t
*, void *);
63 extern picl_errno_t
post_dr_req_event(picl_nodehdl_t
, char *, uint8_t);
64 extern picl_errno_t
post_dr_ap_state_change_event(picl_nodehdl_t
, char *,
66 extern boolean_t
env_admin_lock_enabled(picl_nodehdl_t
);
67 extern picl_errno_t
env_create_temp_sensor_node(picl_nodehdl_t
, uint8_t);
68 extern void env_handle_sensor_event(void *);
69 extern int env_open_smc();
71 /* external variables */
73 extern uint8_t cpu_geo_addr
;
74 extern picl_nodehdl_t rooth
, platformh
, sysmgmth
, sensorh
;
75 extern picl_nodehdl_t chassis_nodehdl
, cpu_nodehdl
, cpu_lnodehdl
;
78 static pthread_mutex_t env_dmc_mutex
= PTHREAD_MUTEX_INITIALIZER
;
79 static pthread_cond_t env_dmc_cond
= PTHREAD_COND_INITIALIZER
;
80 static boolean_t env_reset_cpu
= B_FALSE
;
81 static boolean_t env_shutdown_system
= B_FALSE
;
82 static env_state_event_t env_chassis_state
= FRU_STATE_UNKNOWN
;
83 static char *rcm_abstr_cp2300_name
= RCM_ABSTRACT_RESOURCE
;
84 static boolean_t env_got_dmc_msg
= B_FALSE
;
85 static long env_dmc_wait_time
= 15;
86 static pthread_t dmc_thr_tid
;
89 * issue halt or reboot based on the reset_cpu flag
93 shutdown_cpu(boolean_t force
)
95 if (env_shutdown_system
) {
97 (void) pclose(popen(RESET_CPU
, "w"));
99 (void) pclose(popen(SHUTDOWN_CPU
, "w"));
105 * inform RCM framework that the remove op is successful
108 confirm_rcm(char *abstr_name
, rcm_handle_t
*rhandle
)
110 rcm_notify_remove(rhandle
, abstr_name
, 0, NULL
);
114 * inform RCM framework that the remove op is failed
117 fail_rcm(char *abstr_name
, rcm_handle_t
*rhandle
)
119 (void) rcm_notify_online(rhandle
, abstr_name
, 0, NULL
);
123 * check RCM framework if it is ok to offline a device
126 check_rcm(char *rcm_abstr_cp2300_name
, uint_t flags
)
129 rcm_handle_t
*rhandle
;
132 if (rcm_alloc_handle(NULL
, 0, NULL
, &rhandle
) != RCM_SUCCESS
) {
133 return (RCM_FAILURE
);
136 rv
= rcm_request_offline(rhandle
, rcm_abstr_cp2300_name
,
139 if (rv
== RCM_FAILURE
) {
140 rcm_free_info(rinfo
);
141 fail_rcm(rcm_abstr_cp2300_name
, rhandle
);
142 rcm_free_handle(rhandle
);
143 return (RCM_FAILURE
);
145 if (rv
== RCM_CONFLICT
) {
146 rcm_free_info(rinfo
);
147 rcm_free_handle(rhandle
);
148 return (RCM_CONFLICT
);
151 confirm_rcm(rcm_abstr_cp2300_name
, rhandle
);
152 rcm_free_info(rinfo
);
153 rcm_free_handle(rhandle
);
154 return (RCM_SUCCESS
);
158 * utility routine to send response to an IPMI message
161 send_response2remote_device(uint8_t ipmb_addr
, uint8_t cmd
, uint8_t reqseq_lun
,
164 int rc
= SMC_SUCCESS
;
167 uint8_t data
= cc
; /* completion code */
169 /* make a call to ctsmc lib */
170 (void) smc_init_ipmi_msg(&req_pkt
, cmd
, DEFAULT_FD
, 1, &data
,
171 (reqseq_lun
>> 2), ipmb_addr
, SMC_NETFN_APP_RSP
,
172 (reqseq_lun
& 0x03));
173 rc
= smc_send_msg(DEFAULT_FD
, &req_pkt
, &rsp_pkt
,
176 if (rc
!= SMC_SUCCESS
)
177 syslog(LOG_ERR
, gettext("SUNW_envmond:Error in sending response"
178 " to %x, error = %d"), ipmb_addr
, rc
);
183 * do all the checks like adminlock check, rcm check and initiate
188 initiate_shutdown(boolean_t force
)
192 struct timespec rqtp
, rmtp
;
194 if (!env_shutdown_system
) {
198 /* check the adminlock prop */
199 if ((!force
) && (env_admin_lock_enabled(cpu_nodehdl
))) {
200 syslog(LOG_ERR
, gettext("SUNW_envmond: "
201 "CPU in use! Cannot shutdown"));
206 rcmflags
= RCM_FORCE
;
209 /* check with rcm framework */
210 rv
= check_rcm(rcm_abstr_cp2300_name
, rcmflags
);
212 if ((rv
== RCM_FAILURE
) || (rv
== RCM_CONFLICT
)) {
213 syslog(LOG_ERR
, gettext("SUNW_envmond: RCM error %d, Cannot"
219 * force events on chassis node
222 if (post_dr_req_event(chassis_nodehdl
, DR_REQ_OUTGOING_RES
,
223 NO_WAIT
) == PICL_SUCCESS
) {
224 /* wait a little for clean up of frutree */
227 (void) nanosleep(&rqtp
, &rmtp
);
230 * If force option is set, do it right here for now
231 * since there is no way to pass this info via events
232 * to frutree framework.
238 if (post_dr_req_event(chassis_nodehdl
, DR_REQ_OUTGOING_RES
, NO_WAIT
)
240 syslog(LOG_ERR
, gettext("SUNW_envmond:cannot shutdown "
248 * get the HEALTHY# line state
249 * Return -1 for Error
250 * 0 for HEALTHY# down
254 env_get_healthy_status()
260 /* initialize the request packet */
261 (void) smc_init_smc_msg(&req_pkt
, SMC_GET_EXECUTION_STATE
,
264 /* make a call to smc library to send cmd */
265 if (smc_send_msg(DEFAULT_FD
, &req_pkt
, &rsp_pkt
,
266 POLL_TIMEOUT
) != SMC_SUCCESS
) {
269 return (rsp_pkt
.data
[0] & IS_HEALTHY
);
278 picl_errno_t rc
= PICL_SUCCESS
;
281 if (ptree_get_root(&rooth
) != PICL_SUCCESS
) {
286 if (chassis_nodehdl
== 0) {
287 if ((rc
= ptree_get_node_by_path(PICL_FRUTREE_CHASSIS
,
288 &chassis_nodehdl
)) != PICL_SUCCESS
) {
292 if (post_dr_req_event(chassis_nodehdl
, DR_REQ_INCOMING_RES
,
293 NO_WAIT
) != PICL_SUCCESS
) {
294 syslog(LOG_ERR
, gettext("SUNW_envmond: Error in "
295 "Posting configure event for Chassis node"));
302 * release all the resources
308 rooth
= platformh
= sysmgmth
= 0;
309 chassis_nodehdl
= cpu_nodehdl
= cpu_lnodehdl
= 0;
310 env_chassis_state
= FRU_STATE_UNKNOWN
;
311 (void) ptree_delete_node(sensorh
);
312 (void) ptree_destroy_node(sensorh
);
316 * handle chassis state change
319 env_handle_chassis_state_event(char *state
)
321 if (strcmp(state
, PICLEVENTARGVAL_CONFIGURING
) == 0) {
322 env_chassis_state
= FRU_STATE_CONFIGURING
;
326 if (strcmp(state
, PICLEVENTARGVAL_UNCONFIGURED
) == 0) {
327 if (env_chassis_state
== FRU_STATE_CONFIGURING
||
328 env_chassis_state
== FRU_STATE_UNKNOWN
) {
329 /* picl intialization is failed, dont issue shutdown */
330 env_chassis_state
= FRU_STATE_UNCONFIGURED
;
333 env_chassis_state
= FRU_STATE_UNCONFIGURED
;
335 (void) pclose(popen(RESET_CPU
, "w"));
337 (void) pclose(popen(SHUTDOWN_CPU
, "w"));
342 if (strcmp(state
, PICLEVENTARGVAL_CONFIGURED
) == 0) {
343 env_chassis_state
= FRU_STATE_CONFIGURED
;
348 * event handler for watchdog state change event
351 env_handle_watchdog_expiry(picl_nodehdl_t wd_nodehdl
)
353 picl_errno_t rc
= PICL_SUCCESS
;
354 char class[PICL_CLASSNAMELEN_MAX
];
355 char value
[PICL_PROPNAMELEN_MAX
];
358 if ((rc
= ptree_get_propval_by_name(wd_nodehdl
,
359 PICL_PROP_CLASSNAME
, class,
360 PICL_CLASSNAMELEN_MAX
)) != PICL_SUCCESS
) {
364 /* if the event is not of watchdog-timer, return */
365 if (strcmp(class, PICL_CLASS_WATCHDOG_TIMER
) != 0) {
366 return (PICL_INVALIDARG
);
369 if ((rc
= ptree_get_propval_by_name(wd_nodehdl
,
370 PICL_PROP_WATCHDOG_ACTION
, value
, sizeof (value
))) !=
375 /* if action is none, dont do anything */
376 if (strcmp(value
, PICL_PROPVAL_WD_ACTION_ALARM
) != 0) {
377 return (PICL_SUCCESS
);
380 (void) strncpy(cond
, PICLEVENTARGVAL_FAILED
, sizeof (cond
));
381 /* update CPU condition to failed */
382 if ((rc
= ptree_update_propval_by_name(cpu_nodehdl
,
383 PICL_PROP_CONDITION
, cond
, sizeof (cond
))) != PICL_SUCCESS
) {
387 /* post dr ap state change event */
388 rc
= post_dr_ap_state_change_event(cpu_nodehdl
,
389 DR_RESERVED_ATTR
, NO_COND_TIMEDWAIT
);
394 * rotine that handles all the picl state and condition change events
397 env_platmod_handle_event(const char *ename
, const void *earg
, size_t size
)
400 picl_nodehdl_t nodeh
= 0;
401 picl_prophdl_t proph
;
404 boolean_t state_event
;
405 env_state_event_t event
;
406 char result
[PICL_PROPNAMELEN_MAX
];
407 uint64_t status_time
, cond_time
;
413 if (strcmp(ename
, PICLEVENT_STATE_CHANGE
) == 0) {
414 state_event
= B_TRUE
;
415 } else if (strcmp(ename
, PICLEVENT_CONDITION_CHANGE
) == 0) {
416 state_event
= B_FALSE
;
418 syslog(LOG_ERR
, gettext("SUNW_envmond: unknown event:%s\n"),
423 /* unpack the nvlist and get the information */
424 if (nvlist_unpack((char *)earg
, size
, &nvlp
, NULL
)) {
427 if (nvlist_lookup_uint64(nvlp
, PICLEVENTARG_NODEHANDLE
, &nodeh
) == -1) {
431 if (nvlist_lookup_string(nvlp
, (state_event
) ?
433 PICLEVENTARG_CONDITION
, &value
) != 0) {
438 if (env_debug
& PICLEVENTS
) {
439 if (ptree_get_propval_by_name(nodeh
, PICL_PROP_NAME
,
440 result
, sizeof (result
)) != PICL_SUCCESS
) {
441 syslog(LOG_ERR
, " SUNW_envmond: error in getting"
446 syslog(LOG_INFO
, "SUNW_envmond: %s (%s) on %s",
447 ename
, value
, result
);
450 if (chassis_nodehdl
== 0 && state_event
) {
451 if (ptree_get_propval_by_name(nodeh
, PICL_PROP_NAME
,
452 result
, sizeof (result
)) != PICL_SUCCESS
) {
456 if (strcmp(result
, PICL_NODE_CHASSIS
) == 0) {
457 chassis_nodehdl
= nodeh
;
461 if (nodeh
== chassis_nodehdl
&& state_event
) {
462 env_handle_chassis_state_event(value
);
467 if (strcmp(PICLEVENTARGVAL_DISCONNECTED
, value
) == 0) {
468 event
= LOC_STATE_DISCONNECTED
;
469 } else if (strcmp(PICLEVENTARGVAL_CONNECTED
, value
) == 0) {
470 event
= LOC_STATE_CONNECTED
;
471 } else if (strcmp(PICLEVENTARGVAL_EMPTY
, value
) == 0) {
472 event
= LOC_STATE_EMPTY
;
473 } else if (strcmp(PICLEVENTARGVAL_CONFIGURED
, value
) == 0) {
474 event
= FRU_STATE_CONFIGURED
;
475 } else if (strcmp(PICLEVENTARGVAL_UNCONFIGURED
, value
) == 0) {
476 event
= FRU_STATE_UNCONFIGURED
;
477 } else if (strcmp(PICL_PROPVAL_WD_STATE_EXPIRED
, value
) == 0) {
478 /* watchdog expiry event */
479 if ((rc
= env_handle_watchdog_expiry(nodeh
)) != PICL_SUCCESS
) {
480 syslog(LOG_ERR
, gettext("SUNW_envmond:Error in handling"
481 "watchdog expiry event"));
491 case LOC_STATE_EMPTY
:
494 case LOC_STATE_DISCONNECTED
:
495 if (nodeh
== cpu_lnodehdl
) {
496 (void) initiate_shutdown(B_FALSE
);
499 case LOC_STATE_CONNECTED
:
500 if (nodeh
!= cpu_lnodehdl
) {
503 if (ptree_get_propval_by_name(cpu_lnodehdl
,
504 PICL_PROP_CHILD
, &cpu_nodehdl
,
505 sizeof (picl_nodehdl_t
)) != PICL_SUCCESS
) {
506 syslog(LOG_ERR
, gettext("SUNW_envmond:Cannot "
507 "initialize CPU node handle %llx"), nodeh
);
511 case FRU_STATE_CONFIGURED
:
512 if (nodeh
!= cpu_nodehdl
) {
515 if (ptree_get_prop_by_name(cpu_nodehdl
,
516 PICL_PROP_STATUS_TIME
, &proph
) != PICL_SUCCESS
) {
517 status_time
= (uint64_t)time(NULL
);
518 (void) env_create_property(PICL_PTYPE_TIMESTAMP
,
519 PICL_READ
, sizeof (status_time
),
520 PICL_PROP_STATUS_TIME
, NULLREAD
,
521 NULLWRITE
, cpu_nodehdl
, &proph
,
524 if (ptree_get_prop_by_name(cpu_nodehdl
,
525 PICL_PROP_CONDITION_TIME
, &proph
) != PICL_SUCCESS
) {
526 cond_time
= (uint64_t)time(NULL
);
527 (void) env_create_property(PICL_PTYPE_TIMESTAMP
,
528 PICL_READ
, sizeof (cond_time
),
529 PICL_PROP_CONDITION_TIME
, NULLREAD
,
530 NULLWRITE
, cpu_nodehdl
, &proph
,
533 env_shutdown_system
= B_FALSE
;
534 /* if HEALTHY# is UP update the condition to "ok" */
535 switch (env_get_healthy_status()) {
537 /* update CPU condition to failed */
538 (void) strncpy(cond
, PICLEVENTARGVAL_FAILED
, sizeof (cond
));
541 /* update CPU condition to ok */
542 (void) strncpy(cond
, PICLEVENTARGVAL_OK
, sizeof (cond
));
544 case -1: /*FALLTHRU*/
546 /* update the condition to unknown */
547 (void) strncpy(cond
, PICLEVENTARGVAL_UNKNOWN
, sizeof (cond
));
548 syslog(LOG_ERR
, gettext("SUNW_envmond:Error in "
549 "reading HEALTHY# status"));
552 if ((rc
= ptree_update_propval_by_name(cpu_nodehdl
,
553 PICL_PROP_CONDITION
, cond
, sizeof (cond
))) !=
555 syslog(LOG_ERR
, gettext("SUNW_envmond:Error in "
556 "updating CPU condition, error = %d"), rc
);
559 case FRU_STATE_UNCONFIGURED
:
560 if (env_reset_cpu
&& nodeh
== cpu_nodehdl
) {
561 (void) initiate_shutdown(B_FALSE
);
566 } /* end of switch */
571 * This thread waits for dmc message to come, as it has to send
572 * response ACK back to DMC. Otherwise DMC may think that message
573 * is lost and issues poweroff on a node. So there is a chance for
574 * CPU to be powered off in the middle of shutdown process. If the
575 * DMC message didnt come, then process the local shutdown request.
579 env_wait_for_dmc_msg(void *args
)
584 (void) pthread_mutex_lock(&env_dmc_mutex
);
585 if (env_got_dmc_msg
== B_TRUE
) {
586 (void) pthread_mutex_unlock(&env_dmc_mutex
);
591 * wait for specified time to check if dmc sends the
594 (void) gettimeofday(&ct
, NULL
);
595 to
.tv_sec
= ct
.tv_sec
+ env_dmc_wait_time
;
597 (void) pthread_cond_timedwait(&env_dmc_cond
,
598 &env_dmc_mutex
, &to
);
599 if (env_got_dmc_msg
== B_TRUE
) {
600 (void) pthread_mutex_unlock(&env_dmc_mutex
);
603 (void) pthread_mutex_unlock(&env_dmc_mutex
);
605 env_shutdown_system
= B_TRUE
;
606 env_reset_cpu
= B_FALSE
;
607 (void) initiate_shutdown(B_FALSE
);
612 * Handle the Latch open event(shutdown the node)
615 env_platmod_handle_latch_open()
618 * create a thread to process local event after waiting for DMC CPU
619 * node state offline message
621 if (pthread_create(&dmc_thr_tid
, NULL
, &env_wait_for_dmc_msg
,
623 syslog(LOG_ERR
, gettext("SUNW_envmond:Error in creating "
625 return (PICL_FAILURE
);
627 return (PICL_SUCCESS
);
631 * For Sanibel, hotswap initialization is not reqd.
634 env_platmod_setup_hotswap()
636 return (PICL_SUCCESS
);
640 * For sanibel this supoort is not required
643 env_platmod_sp_monitor()
645 return (PICL_SUCCESS
);
649 * For sanibel this supoort is not required
652 env_platmod_create_hotswap_prop()
654 return (PICL_SUCCESS
);
658 * For sanibel this supoort is not required
662 process_platmod_sp_heartbeat(uint8_t data
)
667 * For sanibel this supoort is not required
671 process_platmod_async_msg_notif(void *resdatap
)
677 * For sanibel this supoort is not required
681 process_platmod_change_cpci_state(void *res_datap
)
687 * handle request from service processor for shutdown/online
690 process_platmod_change_cpu_node_state(void *res_datap
)
692 int rc
= SMC_SUCCESS
;
693 uint8_t state
= BYTE_7(res_datap
);
694 boolean_t force_flag
= B_FALSE
;
697 case CPU_NODE_STATE_OFFLINE
:
698 (void) pthread_mutex_lock(&env_dmc_mutex
);
699 env_got_dmc_msg
= B_TRUE
;
700 (void) pthread_cond_signal(&env_dmc_cond
);
701 (void) pthread_mutex_unlock(&env_dmc_mutex
);
702 env_shutdown_system
= B_TRUE
;
703 if ((state
>> 2) & 1)
704 env_reset_cpu
= B_TRUE
;
705 if (state
>> 1 & 1) { /* force flag set? */
708 force_flag
= B_FALSE
;
711 if (initiate_shutdown(force_flag
) == 0) {
712 if ((rc
= send_response2remote_device(SMC_BMC_ADDR
,
713 EVENT_MSG_CHANGE_CPU_NODE_STATE
,
714 BYTE_5(res_datap
), 0x0)) != SMC_SUCCESS
) {
718 if ((rc
= send_response2remote_device(SMC_BMC_ADDR
,
719 EVENT_MSG_CHANGE_CPU_NODE_STATE
,
720 BYTE_5(res_datap
), 0xFF)) != SMC_SUCCESS
) {
723 env_shutdown_system
= B_FALSE
;
724 if ((state
>> 2) & 1)
725 env_reset_cpu
= B_FALSE
;
728 case CPU_NODE_STATE_ONLINE
:
729 if ((rc
= send_response2remote_device(SMC_BMC_ADDR
,
730 EVENT_MSG_CHANGE_CPU_NODE_STATE
,
731 BYTE_5(res_datap
), 0x0)) != SMC_SUCCESS
) {
742 * Handle change in state of service processor
745 process_platmod_sp_state_change_notif(void *res_datap
)
747 int rc
= SMC_SUCCESS
;
748 uint8_t state
= BYTE_7(res_datap
);
749 uint8_t rq_addr
= BYTE_4(res_datap
);
751 if (rq_addr
!= SMC_BMC_ADDR
) {
752 return (PICL_FAILURE
);
756 case CPU_NODE_STATE_ONLINE
:
757 /* Send ACK to service processor */
758 if ((rc
= send_response2remote_device(SMC_BMC_ADDR
,
759 EVENT_MSG_AC_STATE_CHANGE
,
760 BYTE_5(res_datap
), 0x0)) != SMC_SUCCESS
) {
765 case CPU_NODE_STATE_OFFLINE
:
766 /* Send ACK to service processor */
767 if ((rc
= send_response2remote_device(SMC_BMC_ADDR
,
768 EVENT_MSG_AC_STATE_CHANGE
,
769 BYTE_5(res_datap
), 0x0)) != SMC_SUCCESS
) {
775 if ((rc
= send_response2remote_device(SMC_BMC_ADDR
,
776 EVENT_MSG_AC_STATE_CHANGE
,
777 BYTE_5(res_datap
), 0xFF)) != SMC_SUCCESS
) {
786 * For sanibel this supoort is not required
790 env_platmod_handle_bus_if_change(uint8_t data
)
792 return (PICL_SUCCESS
);
796 * create the temperature sensor nodes
799 env_platmod_create_sensors()
801 picl_errno_t rc
= PICL_SUCCESS
;
804 if ((rc
= ptree_get_root(&rooth
)) != PICL_SUCCESS
) {
809 if (platformh
== 0) {
810 if ((rc
= ptree_get_node_by_path(PLATFORM_PATH
,
811 &platformh
)) != PICL_SUCCESS
) {
817 if ((rc
= ptree_get_node_by_path(SYSMGMT_PATH
,
818 &sysmgmth
)) != PICL_SUCCESS
) {
823 rc
= env_create_temp_sensor_node(sysmgmth
, CPU_SENSOR_GEO_ADDR
);
828 * handler for sensor event
831 env_platmod_handle_sensor_event(void *res_datap
)
833 if (BYTE_4(res_datap
) != CPU_SENSOR_GEO_ADDR
) {
836 env_handle_sensor_event(res_datap
);