summaryrefslogtreecommitdiffstats
path: root/hw/fsp/fsp-surveillance.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/fsp/fsp-surveillance.c')
-rw-r--r--hw/fsp/fsp-surveillance.c209
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();
+}
+
OpenPOWER on IntegriCloud