[Skiboot] [RFC UNTESTED PATCH] ast-sf-ctrl: Try to harden vs. BMC collisions & reboots
Benjamin Herrenschmidt
benh at kernel.crashing.org
Fri Jun 3 10:20:31 AEST 2016
We try to detect collisions and reboots via changes to the control
register. When they happen, we offline ourselves for 2mn and restore
all settings when done.
Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
---
hw/ast-bmc/ast-sf-ctrl.c | 172 +++++++++++++++++++++++++++++++++++++----------
include/timebase.h | 11 +++
libflash/errors.h | 1 +
libflash/libflash.c | 2 +-
4 files changed, 148 insertions(+), 38 deletions(-)
diff --git a/hw/ast-bmc/ast-sf-ctrl.c b/hw/ast-bmc/ast-sf-ctrl.c
index bf42d32..df85934 100644
--- a/hw/ast-bmc/ast-sf-ctrl.c
+++ b/hw/ast-bmc/ast-sf-ctrl.c
@@ -20,6 +20,12 @@
#include <stdio.h>
#include <string.h>
+#ifdef __SKIBOOT__
+#include <timebase.h>
+/* 2 minutes delay when something bad detected with BMC */
+#define OFFLINE_DELAY secs_to_tb(2 * 60)
+#endif
+
#include <libflash/libflash.h>
#include <libflash/libflash-priv.h>
@@ -54,6 +60,9 @@ struct ast_sf_ctrl {
/* Current 4b mode */
bool mode_4b;
+ /* offline target for when BMC reboots */
+ uint64_t offline_target;
+
/* Callbacks */
struct spi_flash_ctrl ops;
};
@@ -68,8 +77,114 @@ static const uint32_t ast_ct_hclk_divs[] = {
0xd, /* HCLK/5 */
};
+static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ uint32_t ce_ctrl = 0;
+
+ if (ct->type == AST_SF_TYPE_BMC && ct->ops.finfo->size > 0x1000000)
+ ce_ctrl = ast_ahb_readl(BMC_SPI_FCTL_CE_CTRL);
+ else if (ct->type != AST_SF_TYPE_PNOR)
+ return enable ? FLASH_ERR_4B_NOT_SUPPORTED : 0;
+
+ /*
+ * We update the "old" value as well since when quitting
+ * we don't restore the mode of the flash itself so we need
+ * to leave the controller in a compatible setup
+ */
+ if (enable) {
+ ct->ctl_val |= 0x2000;
+ ct->ctl_read_val |= 0x2000;
+ ce_ctrl |= 0x1;
+ } else {
+ ct->ctl_val &= ~0x2000;
+ ct->ctl_read_val &= ~0x2000;
+ ce_ctrl &= ~0x1;
+ }
+ ct->mode_4b = enable;
+
+ /* Update read mode */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ if (ce_ctrl && ct->type == AST_SF_TYPE_BMC)
+ ast_ahb_writel(ce_ctrl, BMC_SPI_FCTL_CE_CTRL);
+
+ return 0;
+}
+
+static bool ast_sf_check_bmc(struct ast_sf_ctrl *ct)
+{
+#ifdef __SKIBOOT__
+ uint32_t ctrl;
+
+ /* If BMC is marked offline, just return */
+ if (time_before(ct->offline_target))
+ return false;
+
+ /* Read control register */
+ ctrl = ast_ahb_readl(ct->ctl_reg);
+
+ /* If we didn't have an offline target, check the BMC state */
+ /* We had an offline target, it's now expired, check if the BMC
+ * is responding and restore registers.
+ */
+ if (!ct->offline_target) {
+ /* Expected value, we are good */
+ if (((ct->ctl_reg ^ ctrl) & ~0x7) == 0)
+ return true;
+
+ /* Unexpected value, we mark ourselves offline for a couple
+ * of minutes as the BMC might be rebooting
+ */
+ prlog(PR_WARNING, "AST: Flash controller settings changed, "
+ "BMC rebooted ?\n");
+ prlog(PR_WARNING, "AST: Flash access off for a while...\n");
+ prlog(PR_WARNING, "AST: ctrl reg was: 0x%08x\n", ctrl);
+ ct->offline_target = mftb() + OFFLINE_DELAY;
+
+ /* We also whack the register to clear CE# since a collision
+ * did occur
+ */
+ ast_ahb_writel(ctrl | 7, ct->ctl_reg);
+ ast_ahb_writel(ctrl & ~7, ct->ctl_reg);
+ return false;
+ }
+
+ /* We had an offline target and we passed it, check if the BMC
+ * is responding (also clear CE# just in case)
+ */
+ ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
+ ctrl = ast_ahb_readl(ct->ctl_reg);
+ if (((ct->ctl_reg ^ ctrl) & ~0x7) != 0) {
+ prlog(PR_WARNING, "AST: Flash controller still not"
+ " responding, waiting longer...\n");
+ ct->offline_target = mftb() + OFFLINE_DELAY;
+ return false;
+ }
+
+ /* Ok, we are good now, reconfigure all */
+ ct->offline_target = 0;
+
+ /* Restore timing calibration */
+ ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg);
+
+ /* Update read mode */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ /* Restore 4b mode */
+ ast_sf_set_4b(&ct->ops, ct->mode_4b);
+#endif /* __SKIBOOT__ */
+
+ return true;
+
+}
+
static int ast_sf_start_cmd(struct ast_sf_ctrl *ct, uint8_t cmd)
{
+ /* Check for a BMC reboot having lost settings */
+ if (!ast_sf_check_bmc(ct))
+ return FLASH_ERR_OFFLINE;
+
/* Switch to user mode, CE# dropped */
ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
@@ -80,13 +195,21 @@ static int ast_sf_start_cmd(struct ast_sf_ctrl *ct, uint8_t cmd)
return ast_copy_to_ahb(ct->flash, &cmd, 1);
}
-static void ast_sf_end_cmd(struct ast_sf_ctrl *ct)
+static bool ast_sf_end_cmd(struct ast_sf_ctrl *ct)
{
+ bool bmc_lost;
+
+ /* Check for a BMC reboot having lost settings */
+ if (!ast_sf_check_bmc(ct))
+ return FLASH_ERR_OFFLINE;
+
/* clear CE# */
ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
/* Switch back to read mode */
ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ return bmc_lost;
}
static int ast_sf_send_addr(struct ast_sf_ctrl *ct, uint32_t addr)
@@ -149,51 +272,26 @@ static int ast_sf_cmd_wr(struct spi_flash_ctrl *ctrl, uint8_t cmd,
return rc;
}
-static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable)
-{
- struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
- uint32_t ce_ctrl = 0;
-
- if (ct->type == AST_SF_TYPE_BMC && ct->ops.finfo->size > 0x1000000)
- ce_ctrl = ast_ahb_readl(BMC_SPI_FCTL_CE_CTRL);
- else if (ct->type != AST_SF_TYPE_PNOR)
- return enable ? FLASH_ERR_4B_NOT_SUPPORTED : 0;
-
- /*
- * We update the "old" value as well since when quitting
- * we don't restore the mode of the flash itself so we need
- * to leave the controller in a compatible setup
- */
- if (enable) {
- ct->ctl_val |= 0x2000;
- ct->ctl_read_val |= 0x2000;
- ce_ctrl |= 0x1;
- } else {
- ct->ctl_val &= ~0x2000;
- ct->ctl_read_val &= ~0x2000;
- ce_ctrl &= ~0x1;
- }
- ct->mode_4b = enable;
-
- /* Update read mode */
- ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
-
- if (ce_ctrl && ct->type == AST_SF_TYPE_BMC)
- ast_ahb_writel(ce_ctrl, BMC_SPI_FCTL_CE_CTRL);
-
- return 0;
-}
-
static int ast_sf_read(struct spi_flash_ctrl *ctrl, uint32_t pos,
void *buf, uint32_t len)
{
struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ int rc;
+ if (!ast_sf_check_bmc(ct))
+ return FLASH_ERR_OFFLINE;
/*
* We are in read mode by default. We don't yet support fancy
* things like fast read or X2 mode
*/
- return ast_copy_from_ahb(buf, ct->flash + pos, len);
+ rc = ast_copy_from_ahb(buf, ct->flash + pos, len);
+ if (rc)
+ return rc;
+
+ if (!ast_sf_check_bmc(ct))
+ return FLASH_ERR_OFFLINE;
+
+ return 0;
}
static void ast_get_ahb_freq(void)
diff --git a/include/timebase.h b/include/timebase.h
index 7f5afe1..a1d8a1c 100644
--- a/include/timebase.h
+++ b/include/timebase.h
@@ -23,6 +23,7 @@
#define __TIME_H
#include <time.h>
+#include <stdbool.h>
#ifndef __TEST__
static inline unsigned long mftb(void)
@@ -51,6 +52,16 @@ static inline enum tb_cmpval tb_compare(unsigned long a,
return ((long)(b - a)) > 0 ? TB_ABEFOREB : TB_AAFTERB;
}
+static inline bool time_after(unsigned long target)
+{
+ return tb_compare(mftb(), target) == TB_AAFTERB;
+}
+
+static inline bool time_before(unsigned long target)
+{
+ return tb_compare(mftb(), target) == TB_ABEFOREB;
+}
+
/* Architected timebase */
static const unsigned long tb_hz = 512000000;
diff --git a/libflash/errors.h b/libflash/errors.h
index 99dcfc2..3381f48 100644
--- a/libflash/errors.h
+++ b/libflash/errors.h
@@ -31,5 +31,6 @@
#define FLASH_ERR_CTRL_TIMEOUT 13
#define FLASH_ERR_ECC_INVALID 14
#define FLASH_ERR_BAD_READ 15
+#define FLASH_ERR_OFFLINE 16
#endif /* __LIBFLASH_ERRORS_H */
diff --git a/libflash/libflash.c b/libflash/libflash.c
index d3e594a..ec93284 100644
--- a/libflash/libflash.c
+++ b/libflash/libflash.c
@@ -286,7 +286,7 @@ int flash_erase_chip(struct flash_chip *c)
return FLASH_ERR_CHIP_ER_NOT_SUPPORTED;
FL_DBG("LIBFLASH: Erasing chip...\n");
-
+
/* Use controller erase if supported */
if (ct->erase)
return ct->erase(ct, 0, 0xffffffff);
More information about the Skiboot
mailing list