diff options
Diffstat (limited to 'hw/fsp/fsp-surveillance.c')
-rw-r--r-- | hw/fsp/fsp-surveillance.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/hw/fsp/fsp-surveillance.c b/hw/fsp/fsp-surveillance.c new file mode 100644 index 00000000..c1d19b64 --- /dev/null +++ b/hw/fsp/fsp-surveillance.c @@ -0,0 +1,209 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <skiboot.h> +#include <fsp.h> +#include <lock.h> +#include <processor.h> +#include <timebase.h> +#include <fsp-sysparam.h> +#include <fsp-elog.h> + +static bool fsp_surv_state = false; +static bool fsp_surv_ack_pending = false; +static u64 surv_timer; +static u64 surv_ack_timer; +static u32 surv_state_param; +static struct lock surv_lock = LOCK_UNLOCKED; + +#define FSP_SURV_ACK_TIMEOUT 120 /* surv ack timeout in seconds */ + +DEFINE_LOG_ENTRY(OPAL_RC_SURVE_INIT, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, + OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, + OPAL_MISCELLANEOUS_INFO_ONLY, NULL); + +DEFINE_LOG_ENTRY(OPAL_RC_SURVE_STATUS, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, + OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, + OPAL_MISCELLANEOUS_INFO_ONLY, NULL); + +static void fsp_surv_ack(struct fsp_msg *msg) +{ + uint8_t val; + + if (!msg->resp) + return; + + val = (msg->resp->word1 >> 8) & 0xff; + if (val == 0) { + /* reset the pending flag */ + printf("SURV: Received heartbeat acknowledge from FSP\n"); + lock(&surv_lock); + fsp_surv_ack_pending = false; + unlock(&surv_lock); + } else + prerror("SURV: Heartbeat Acknowledgment error from FSP\n"); + + fsp_freemsg(msg); +} + +static void fsp_surv_check_timeout(void) +{ + u64 now = mftb(); + + /* + * We just checked fsp_surv_ack_pending to be true in fsp_surv_hbeat + * and we haven't dropped the surv_lock between then and now. So, we + * just go ahead and check timeouts. + */ + if (tb_compare(now, surv_ack_timer) == TB_AAFTERB) { + /* XXX: We should be logging a PEL to the host, assuming + * the FSP is dead, pending a R/R. + */ + prerror("SURV: [%16llx] Surv ACK timed out; initiating R/R\n", + now); + + /* Reset the pending trigger too */ + fsp_surv_ack_pending = false; + fsp_trigger_reset(); + } + + return; +} + +/* Send surveillance heartbeat based on a timebase trigger */ +static void fsp_surv_hbeat(void) +{ + u64 now = mftb(); + + /* Check if an ack is pending... if so, don't send the ping just yet */ + if (fsp_surv_ack_pending) { + fsp_surv_check_timeout(); + return; + } + + /* add timebase callbacks */ + /* + * XXX This packet needs to be pushed to FSP in an interval + * less than 120s that's advertised to FSP. + * + * Verify if the command building format and call is fine. + */ + if (surv_timer == 0 || + (tb_compare(now, surv_timer) == TB_AAFTERB) || + (tb_compare(now, surv_timer) == TB_AEQUALB)) { + printf("SURV: [%16llx] Sending the hearbeat command to FSP\n", + now); + fsp_queue_msg(fsp_mkmsg(FSP_CMD_SURV_HBEAT, 1, 120), + fsp_surv_ack); + + fsp_surv_ack_pending = true; + surv_timer = now + secs_to_tb(60); + surv_ack_timer = now + secs_to_tb(FSP_SURV_ACK_TIMEOUT); + } +} + +static void fsp_surv_poll(void *data __unused) +{ + if (!fsp_surv_state) + return; + lock(&surv_lock); + fsp_surv_hbeat(); + unlock(&surv_lock); +} + +static void fsp_surv_got_param(uint32_t param_id __unused, int err_len, + void *data __unused) +{ + if (err_len != 4) { + log_simple_error(&e_info(OPAL_RC_SURVE_STATUS), + "SURV: Error retreiving surveillance status: %d\n", + err_len); + return; + } + + printf("SURV: Status from FSP: %d\n", surv_state_param); + if (!(surv_state_param & 0x01)) + return; + + lock(&surv_lock); + fsp_surv_state = true; + + /* Also send one heartbeat now. The next one will not happen + * until we hit the OS. + */ + fsp_surv_hbeat(); + unlock(&surv_lock); +} + +void fsp_surv_query(void) +{ + int rc; + + printf("SURV: Querying FSP's surveillance status\n"); + + /* Reset surveillance settings */ + lock(&surv_lock); + fsp_surv_state = false; + surv_timer = 0; + surv_ack_timer = 0; + unlock(&surv_lock); + + /* Query FPS for surveillance state */ + rc = fsp_get_sys_param(SYS_PARAM_SURV, &surv_state_param, 4, + fsp_surv_got_param, NULL); + if (rc) { + log_simple_error(&e_info(OPAL_RC_SURVE_INIT), + "SURV: Error %d queueing param request\n", rc); + } +} + +static bool fsp_surv_msg_rr(u32 cmd_sub_mod, struct fsp_msg *msg) +{ + assert(msg == NULL); + + switch (cmd_sub_mod) { + case FSP_RESET_START: + printf("SURV: Disabling surveillance\n"); + fsp_surv_state = false; + fsp_surv_ack_pending = false; + return true; + case FSP_RELOAD_COMPLETE: + fsp_surv_query(); + return true; + } + return false; +} + +static struct fsp_client fsp_surv_client_rr = { + .message = fsp_surv_msg_rr, +}; + +/* This is called at boot time */ +void fsp_init_surveillance(void) +{ + /* Always register the poller, so we don't have to add/remove + * it on reset-reload or change of surveillance state. Also the + * poller list has no locking so we don't want to play with it + * at runtime. + */ + opal_add_poller(fsp_surv_poll, NULL); + + /* Register for the reset/reload event */ + fsp_register_client(&fsp_surv_client_rr, FSP_MCLASS_RR_EVENT); + + /* Send query to FSP */ + fsp_surv_query(); +} + |