aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hw/fsp/fsp-sysdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-sysdump.c')
-rw-r--r--roms/skiboot/hw/fsp/fsp-sysdump.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/roms/skiboot/hw/fsp/fsp-sysdump.c b/roms/skiboot/hw/fsp/fsp-sysdump.c
new file mode 100644
index 000000000..cd8744062
--- /dev/null
+++ b/roms/skiboot/hw/fsp/fsp-sysdump.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Sapphire dump design:
+ * - During initialization we setup Memory Dump Source Table (MDST) table
+ * which contains address, size pair.
+ * - We send MDST table update notification to FSP via MBOX command.
+ * - During Sapphire checkstop:
+ * - FSP retrieves HWDUMP.
+ * - FSP retrieves CEC memory based on MDST table.
+ * - Once Sapphire reboot FSP sends new dump avialable notification via HDAT
+ *
+ * Copyright 2013-2016 IBM Corp.
+ */
+
+#include <fsp.h>
+#include <psi.h>
+#include <opal.h>
+#include <lock.h>
+#include <skiboot.h>
+#include <errorlog.h>
+#include <opal-dump.h>
+
+/*
+ * Sapphire dump size
+ * This is the maximum memory that FSP can retrieve during checkstop.
+ *
+ * Note:
+ * Presently we are hardcoding this parameter. Eventually we need
+ * new System parameter so that we can get max size dynamically.
+ */
+#define MAX_SAPPHIRE_DUMP_SIZE 0x1000000
+
+DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP,
+ OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT,
+ OPAL_NA);
+
+DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_UPDATE, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP,
+ OPAL_PLATFORM_FIRMWARE,
+ OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT,
+ OPAL_NA);
+
+DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_ADD, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP,
+ OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA);
+
+DEFINE_LOG_ENTRY(OPAL_RC_DUMP_MDST_REMOVE, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP,
+ OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA);
+
+
+static struct mdst_table *mdst_table;
+static struct mdst_table *dump_mem_region;
+
+static int cur_mdst_entry;
+static int max_mdst_entry;
+static int cur_dump_size;
+/*
+ * Presently both sizes are same.. But if someday FSP gives more space
+ * than our TCE mapping then we need this validation..
+ *
+ * Also once FSP implements MAX_SAPPHIRE_DUMP_SIZE system param, we can
+ * move this validation to separate function.
+ */
+static int max_dump_size = MIN(MAX_SAPPHIRE_DUMP_SIZE, PSI_DMA_HYP_DUMP_SIZE);
+
+/* Protect MDST table entries */
+static struct lock mdst_lock = LOCK_UNLOCKED;
+
+static inline uint32_t get_dump_region_map_size(uint64_t addr, uint32_t size)
+{
+ uint64_t start, end;
+
+ start = addr & ~TCE_MASK;
+ end = addr + size;
+ end = ALIGN_UP(end, TCE_PSIZE);
+
+ return (end - start);
+}
+
+static int dump_region_tce_map(void)
+{
+ int i;
+ uint32_t t_size = 0, size;
+ uint64_t addr;
+
+ for (i = 0; i < cur_mdst_entry; i++) {
+
+ addr = be64_to_cpu(dump_mem_region[i].addr) & ~TCE_MASK;
+ size = get_dump_region_map_size(be64_to_cpu(dump_mem_region[i].addr),
+ be32_to_cpu(dump_mem_region[i].size));
+
+ if (t_size + size > max_dump_size)
+ break;
+
+ /* TCE mapping */
+ fsp_tce_map(PSI_DMA_HYP_DUMP + t_size, (void *)addr, size);
+
+ /* Add entry to MDST table */
+ mdst_table[i].data_region = dump_mem_region[i].data_region;
+ mdst_table[i].size = dump_mem_region[i].size;
+ mdst_table[i].addr = cpu_to_be64(PSI_DMA_HYP_DUMP + t_size);
+
+ /* TCE alignment adjustment */
+ mdst_table[i].addr = cpu_to_be64(be64_to_cpu(mdst_table[i].addr) +
+ (be64_to_cpu(dump_mem_region[i].addr) & 0xfff));
+
+ t_size += size;
+ }
+
+ return i;
+}
+
+static inline void dump_region_tce_unmap(void)
+{
+ fsp_tce_unmap(PSI_DMA_HYP_DUMP, PSI_DMA_HYP_DUMP_SIZE);
+}
+
+static void update_mdst_table_complete(struct fsp_msg *msg)
+{
+ uint8_t status = (msg->resp->word1 >> 8) & 0xff;
+
+ if (status)
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_UPDATE),
+ "MDST: Update table MBOX command failed: "
+ "0x%x\n", status);
+ else
+ printf("MDST: Table updated.\n");
+
+ fsp_freemsg(msg);
+}
+
+/* Send MDST table to FSP */
+static int64_t fsp_update_mdst_table(void)
+{
+ struct fsp_msg *msg;
+ int count;
+ int rc = OPAL_SUCCESS;
+
+ if (cur_mdst_entry <= 0) {
+ printf("MDST: Table is empty\n");
+ return OPAL_INTERNAL_ERROR;
+ }
+
+ lock(&mdst_lock);
+
+ /* Unmap previous mapping */
+ dump_region_tce_unmap();
+ count = dump_region_tce_map();
+
+ msg = fsp_mkmsg(FSP_CMD_HYP_MDST_TABLE, 4, 0,
+ PSI_DMA_MDST_TABLE,
+ sizeof(*mdst_table) * count,
+ sizeof(*mdst_table));
+ unlock(&mdst_lock);
+
+ if (!msg) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_UPDATE),
+ "MDST: Message allocation failed.!\n");
+ rc = OPAL_INTERNAL_ERROR;
+ } else if (fsp_queue_msg(msg, update_mdst_table_complete)) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_UPDATE),
+ "MDST: Failed to queue MDST table message.\n");
+ fsp_freemsg(msg);
+ rc = OPAL_INTERNAL_ERROR;
+ }
+ return rc;
+}
+
+static int dump_region_del_entry(uint32_t id)
+{
+ int i;
+ uint32_t size;
+ bool found = false;
+ int rc = OPAL_SUCCESS;
+
+ lock(&mdst_lock);
+
+ for (i = 0; i < cur_mdst_entry; i++) {
+ if (dump_mem_region[i].data_region != id)
+ continue;
+
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ rc = OPAL_PARAMETER;
+ goto del_out;
+ }
+
+ /* Adjust current dump size */
+ size = get_dump_region_map_size(be64_to_cpu(dump_mem_region[i].addr),
+ be32_to_cpu(dump_mem_region[i].size));
+ cur_dump_size -= size;
+
+ for ( ; i < cur_mdst_entry - 1; i++)
+ dump_mem_region[i] = dump_mem_region[i + 1];
+
+ dump_mem_region[i].data_region = 0;
+ cur_mdst_entry--;
+
+del_out:
+ unlock(&mdst_lock);
+ return rc;
+}
+
+/* Add entry to MDST table */
+static int __dump_region_add_entry(uint32_t id, uint64_t addr, uint32_t size)
+{
+ int rc = OPAL_INTERNAL_ERROR;
+ uint32_t act_size;
+
+ /* Delete function takes lock before modifying table */
+ dump_region_del_entry(id);
+
+ lock(&mdst_lock);
+
+ if (cur_mdst_entry >= max_mdst_entry) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD),
+ "MDST: Table is full.\n");
+ goto out;
+ }
+
+ /* TCE alignment adjustment */
+ act_size = get_dump_region_map_size(addr, size);
+
+ /* Make sure we don't cross dump size limit */
+ if (cur_dump_size + act_size > max_dump_size) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD),
+ "MDST: 0x%x is crossing max dump size (0x%x) limit.\n",
+ cur_dump_size + act_size, max_dump_size);
+ goto out;
+ }
+
+ /* Add entry to dump memory region table */
+ dump_mem_region[cur_mdst_entry].data_region = (u8)id;
+ dump_mem_region[cur_mdst_entry].addr = cpu_to_be64(addr);
+ dump_mem_region[cur_mdst_entry].size = cpu_to_be32(size);
+
+ /* Update dump region count and dump size */
+ cur_mdst_entry++;
+ cur_dump_size += act_size;
+
+ printf("MDST: Addr = 0x%llx [size : 0x%x bytes] added to MDST table.\n",
+ (uint64_t)addr, size);
+
+ rc = OPAL_SUCCESS;
+
+out:
+ unlock(&mdst_lock);
+ return rc;
+}
+
+static int dump_region_add_entries(void)
+{
+ int rc;
+
+ /* Add console buffer */
+ rc = __dump_region_add_entry(DUMP_REGION_CONSOLE,
+ INMEM_CON_START, INMEM_CON_LEN);
+ if (rc)
+ return rc;
+
+ /* Add HBRT buffer */
+ rc = __dump_region_add_entry(DUMP_REGION_HBRT_LOG,
+ HBRT_CON_START, HBRT_CON_LEN);
+
+ return rc;
+}
+
+static int64_t fsp_opal_register_dump_region(uint32_t id,
+ uint64_t addr, uint64_t size)
+{
+ int rc = OPAL_SUCCESS;
+
+ if (!fsp_present())
+ return OPAL_UNSUPPORTED;
+
+ /* Validate memory region id */
+ if (id < DUMP_REGION_HOST_START || id > DUMP_REGION_HOST_END) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD),
+ "MDST: Invalid dump region id : 0x%x\n", id);
+ return OPAL_PARAMETER;
+ }
+
+ if (size <= 0) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_ADD),
+ "MDST: Invalid size : 0x%llx\n", size);
+ return OPAL_PARAMETER;
+ }
+
+ rc = __dump_region_add_entry(id, addr, size);
+ if (rc)
+ return rc;
+
+ /* Send updated MDST to FSP */
+ rc = fsp_update_mdst_table();
+
+ return rc;
+}
+
+static int64_t fsp_opal_unregister_dump_region(uint32_t id)
+{
+ int rc = OPAL_SUCCESS;
+
+ if (!fsp_present())
+ return OPAL_UNSUPPORTED;
+
+ /* Validate memory region id */
+ if (id < DUMP_REGION_HOST_START || id > DUMP_REGION_HOST_END) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_REMOVE),
+ "MDST: Invalid dump region id : 0x%x\n", id);
+ return OPAL_PARAMETER;
+ }
+
+ rc = dump_region_del_entry(id);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_REMOVE),
+ "MDST: dump region id : 0x%x not found\n", id);
+ return OPAL_PARAMETER;
+ }
+
+ /* Send updated MDST to FSP */
+ rc = fsp_update_mdst_table();
+
+ return rc;
+}
+
+/* TCE mapping */
+static inline void mdst_table_tce_map(void)
+{
+ fsp_tce_map(PSI_DMA_MDST_TABLE, mdst_table, PSI_DMA_MDST_TABLE_SIZE);
+}
+
+/* Initialize MDST table */
+static int mdst_table_init(void)
+{
+ dump_mem_region = memalign(TCE_PSIZE, PSI_DMA_MDST_TABLE_SIZE);
+ if (!dump_mem_region) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_INIT),
+ "MDST: Failed to allocate memory for dump "
+ "memory region table.\n");
+ return -ENOMEM;
+ }
+
+ memset(dump_mem_region, 0, PSI_DMA_MDST_TABLE_SIZE);
+
+ mdst_table = memalign(TCE_PSIZE, PSI_DMA_MDST_TABLE_SIZE);
+ if (!mdst_table) {
+ log_simple_error(&e_info(OPAL_RC_DUMP_MDST_INIT),
+ "MDST: Failed to allocate memory for MDST table.\n");
+ return -ENOMEM;
+ }
+
+ memset(mdst_table, 0, PSI_DMA_MDST_TABLE_SIZE);
+ mdst_table_tce_map();
+
+ max_mdst_entry = PSI_DMA_MDST_TABLE_SIZE / sizeof(*mdst_table);
+ printf("MDST: Max entries in MDST table : %d\n", max_mdst_entry);
+
+ return OPAL_SUCCESS;
+}
+
+/*
+ * Handle FSP R/R event.
+ */
+static bool fsp_mdst_update_rr(uint32_t cmd_sub_mod,
+ struct fsp_msg *msg __unused)
+{
+ switch (cmd_sub_mod) {
+ case FSP_RESET_START:
+ return true;
+ case FSP_RELOAD_COMPLETE: /* Send MDST to FSP */
+ fsp_update_mdst_table();
+ return true;
+ }
+ return false;
+}
+
+static struct fsp_client fsp_mdst_client_rr = {
+ .message = fsp_mdst_update_rr,
+};
+
+/* Initialize MDST table and send notification to FSP */
+void fsp_mdst_table_init(void)
+{
+ if (!fsp_present())
+ return;
+
+ /* OPAL interface */
+ opal_register(OPAL_REGISTER_DUMP_REGION,
+ fsp_opal_register_dump_region, 3);
+ opal_register(OPAL_UNREGISTER_DUMP_REGION,
+ fsp_opal_unregister_dump_region, 1);
+
+ /* Initiate MDST */
+ if (mdst_table_init() != OPAL_SUCCESS)
+ return;
+
+ /*
+ * Ignore return code from mdst_table_add_entries so that
+ * we can atleast capture partial dump.
+ */
+ dump_region_add_entries();
+ fsp_update_mdst_table();
+
+ /* Register for Class AA (FSP R/R) */
+ fsp_register_client(&fsp_mdst_client_rr, FSP_MCLASS_RR_EVENT);
+}