summaryrefslogtreecommitdiff
path: root/gxp-debug-dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'gxp-debug-dump.c')
-rw-r--r--gxp-debug-dump.c308
1 files changed, 177 insertions, 131 deletions
diff --git a/gxp-debug-dump.c b/gxp-debug-dump.c
index 5c7ae61..87d5539 100644
--- a/gxp-debug-dump.c
+++ b/gxp-debug-dump.c
@@ -25,6 +25,7 @@
#define GXP_COREDUMP_PENDING 0xF
#define KERNEL_INIT_DUMP_TIMEOUT (10000 * GXP_TIME_DELAY_FACTOR)
+#define SSCD_MSG_LENGTH 64
/* Enum indicating the debug dump request reason. */
enum gxp_debug_dump_init_type {
@@ -223,10 +224,13 @@ gxp_get_lpm_registers(struct gxp_dev *gxp, struct gxp_seg_header *seg_header,
dev_dbg(gxp->dev, "Done getting LPM registers\n");
}
-static void gxp_get_common_dump(struct gxp_dev *gxp,
- struct gxp_seg_header *common_seg_header,
- struct gxp_common_dump_data *common_dump_data)
+static void gxp_get_common_dump(struct gxp_dev *gxp)
{
+ struct gxp_common_dump *common_dump = gxp->debug_dump_mgr->common_dump;
+ struct gxp_seg_header *common_seg_header = common_dump->seg_header;
+ struct gxp_common_dump_data *common_dump_data =
+ &common_dump->common_dump_data;
+
gxp_get_common_registers(gxp,
&common_seg_header[GXP_COMMON_REGISTERS_IDX],
&common_dump_data->common_regs);
@@ -241,168 +245,199 @@ static void gxp_get_common_dump(struct gxp_dev *gxp,
common_dump_data->common_regs.aurora_revision);
}
-static void gxp_handle_debug_dump(struct gxp_dev *gxp,
- struct gxp_core_dump *core_dump,
- struct gxp_common_dump *common_dump,
- enum gxp_debug_dump_init_type init_type,
- uint core_bits)
-{
- struct gxp_core_dump_header *core_dump_header;
- struct gxp_core_header *core_header;
- int i;
#if IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP)
+static void gxp_send_to_sscd(struct gxp_dev *gxp, void *segs, int seg_cnt,
+ const char *info) {
+
+ struct gxp_debug_dump_manager *mgr = gxp->debug_dump_mgr;
struct sscd_platform_data *pdata =
- (struct sscd_platform_data *)gxp->debug_dump_mgr->sscd_pdata;
- struct sscd_segment *segs;
- int segs_num = GXP_NUM_COMMON_SEGMENTS;
- int seg_idx = 0;
- int core_dump_num = 0;
- int j;
- void *data_addr;
+ (struct sscd_platform_data *)mgr->sscd_pdata;
- for (i = 0; i < GXP_NUM_CORES; i++) {
- if (core_bits & BIT(i))
- core_dump_num++;
+ if (!pdata->sscd_report) {
+ dev_err(gxp->dev, "Failed to generate coredump\n");
+ return;
+ }
+
+ if (pdata->sscd_report(gxp->debug_dump_mgr->sscd_dev, segs, seg_cnt,
+ SSCD_FLAGS_ELFARM64HDR, info)) {
+ dev_err(gxp->dev, "Unable to send the report to SSCD daemon\n");
+ return;
}
/*
- * segs_num include the common segments, core segments for each core,
- * core header for each core
+ * This delay is needed to ensure there's sufficient time
+ * in between sscd_report() being called, as the file name of
+ * the core dump files generated by the SSCD daemon includes a
+ * time format with a seconds precision.
*/
- if (init_type == DEBUG_DUMP_FW_INIT)
- segs_num += GXP_NUM_CORE_SEGMENTS + 1;
- else
- segs_num += GXP_NUM_CORE_SEGMENTS * core_dump_num +
- core_dump_num;
+ msleep(1000);
+}
+#endif
- segs = kmalloc_array(segs_num, sizeof(struct sscd_segment),
- GFP_KERNEL);
- if (!segs)
- goto out;
+static void gxp_handle_debug_dump(struct gxp_dev *gxp, uint32_t core_id)
+{
+ struct gxp_core_dump_header *core_dump_header;
+ struct gxp_core_header *core_header;
+ struct gxp_debug_dump_manager *mgr = gxp->debug_dump_mgr;
+ struct gxp_core_dump *core_dump = mgr->core_dump;
+ struct gxp_common_dump *common_dump = mgr->common_dump;
+ int i;
+#if IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP)
+ int seg_idx = 0;
+ void *data_addr;
+ char sscd_msg[SSCD_MSG_LENGTH];
/* Common */
data_addr = &common_dump->common_dump_data.common_regs;
for (i = 0; i < GXP_NUM_COMMON_SEGMENTS; i++) {
- segs[seg_idx].addr = data_addr;
- segs[seg_idx].size = common_dump->seg_header[i].size;
- data_addr += segs[seg_idx].size;
+ mgr->segs[core_id][seg_idx].addr = data_addr;
+ mgr->segs[core_id][seg_idx].size =
+ common_dump->seg_header[i].size;
+ data_addr += mgr->segs[core_id][seg_idx].size;
seg_idx++;
}
-#endif // CONFIG_SUBSYSTEM_COREDUMP
+#endif
/* Core */
- for (i = 0; i < GXP_NUM_CORES; i++) {
- if ((core_bits & BIT(i)) == 0)
- continue;
-
- core_dump_header = &core_dump->core_dump_header[i];
- core_header = &core_dump_header->core_header;
- if (!core_header->dump_available) {
- dev_err(gxp->dev,
- "Core dump should have been available\n");
- goto out;
- }
-
+ core_dump_header = &core_dump->core_dump_header[core_id];
+ core_header = &core_dump_header->core_header;
+ if (!core_header->dump_available) {
+ dev_err(gxp->dev,
+ "Core dump should have been available\n");
+ return;
+ }
#if IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP)
- /* Core Header */
- segs[seg_idx].addr = core_header;
- segs[seg_idx].size = sizeof(struct gxp_core_header);
+ /* Core Header */
+ mgr->segs[core_id][seg_idx].addr = core_header;
+ mgr->segs[core_id][seg_idx].size = sizeof(struct gxp_core_header);
+ seg_idx++;
+
+ data_addr = &core_dump->dump_data[core_id *
+ core_header->core_dump_size /
+ sizeof(u32)];
+
+ for (i = 0; i < GXP_NUM_CORE_SEGMENTS - 1; i++) {
+ mgr->segs[core_id][seg_idx].addr = data_addr;
+ mgr->segs[core_id][seg_idx].size =
+ core_dump_header->seg_header[i].size;
+ data_addr += mgr->segs[core_id][seg_idx].size;
seg_idx++;
+ }
- data_addr = &core_dump->dump_data[i *
- core_header->core_dump_size /
- sizeof(u32)];
+ dev_dbg(gxp->dev, "Passing dump data to SSCD daemon\n");
+ snprintf(sscd_msg, SSCD_MSG_LENGTH - 1,
+ "gxp debug dump - dump data (core %0x)", core_id);
+ gxp_send_to_sscd(gxp, mgr->segs[core_id], seg_idx, sscd_msg);
+#endif
+ /* This bit signals that core dump has been processed */
+ core_header->dump_available = 0;
- for (j = 0; j < GXP_NUM_CORE_SEGMENTS; j++) {
- segs[seg_idx].addr = data_addr;
- segs[seg_idx].size =
- core_dump_header->seg_header[j].size;
- data_addr += segs[seg_idx].size;
- seg_idx++;
- }
+ for (i = 0; i < GXP_NUM_COMMON_SEGMENTS; i++)
+ common_dump->seg_header[i].valid = 0;
- dev_notice(gxp->dev, "Passing dump data to SSCD daemon\n");
- if (!pdata->sscd_report) {
- dev_err(gxp->dev,
- "Failed to generate coredump\n");
- goto out;
- }
+ for (i = 0; i < GXP_NUM_CORE_SEGMENTS; i++)
+ core_dump_header->seg_header[i].valid = 0;
- mutex_lock(&gxp->debug_dump_mgr->sscd_lock);
- if (pdata->sscd_report(gxp->debug_dump_mgr->sscd_dev, segs,
- segs_num, SSCD_FLAGS_ELFARM64HDR,
- "gxp debug dump")) {
- dev_err(gxp->dev,
- "Unable to send the report to SSCD daemon\n");
- mutex_unlock(&gxp->debug_dump_mgr->sscd_lock);
- goto out;
- }
+ return;
+}
- /*
- * This delay is needed to ensure there's sufficient time
- * in between sscd_report() being called, as the file name of
- * the core dump files generated by the SSCD daemon includes a
- * time format with a seconds precision.
- */
- msleep(1000);
- mutex_unlock(&gxp->debug_dump_mgr->sscd_lock);
-#endif // CONFIG_SUBSYSTEM_COREDUMP
-
- /* This bit signals that core dump has been processed */
- core_header->dump_available = 0;
-
- if (init_type == DEBUG_DUMP_FW_INIT)
- goto out;
+static void gxp_free_segments(struct gxp_dev *gxp) {
+#if IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP)
+ int core_id;
+
+ for (core_id = 0; core_id < GXP_NUM_CORES; core_id++)
+ kfree(gxp->debug_dump_mgr->segs[core_id]);
+#endif
+ kfree(gxp->debug_dump_mgr->common_dump);
+}
+
+static int gxp_init_segments(struct gxp_dev *gxp) {
+#if !IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP)
+ return 0;
+#else
+ struct gxp_debug_dump_manager *mgr = gxp->debug_dump_mgr;
+ int segs_num = GXP_NUM_COMMON_SEGMENTS;
+ int core_id = 0;
+
+ /*
+ * segs_num include the common segments, core segments for each core,
+ * core header for each core
+ */
+ segs_num += GXP_NUM_CORE_SEGMENTS + 1;
+ for (core_id = 0; core_id < GXP_NUM_CORES; core_id++) {
+ mgr->segs[core_id] = kmalloc_array(segs_num,
+ sizeof(struct sscd_segment),
+ GFP_KERNEL);
+ if (!mgr->segs[core_id])
+ goto err_out;
}
-out:
+ mgr->common_dump = kmalloc(sizeof(*mgr->common_dump), GFP_KERNEL);
+ if (!mgr->common_dump)
+ goto err_out;
+
+ return 0;
+err_out:
+ gxp_free_segments(gxp);
+
+ return -ENOMEM;
+#endif
+}
+
+static void gxp_handle_dram_dump(struct gxp_dev *gxp, uint32_t core_id) {
+ struct gxp_debug_dump_manager *mgr = gxp->debug_dump_mgr;
+ struct gxp_core_dump_header *core_dump_header =
+ &mgr->core_dump->core_dump_header[core_id];
+ struct gxp_seg_header *dram_seg_header =
+ &core_dump_header->seg_header[GXP_CORE_DRAM_SEGMENT_IDX];
#if IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP)
- kfree(segs);
+ struct sscd_segment *sscd_seg =
+ &mgr->segs[core_id][GXP_DEBUG_DUMP_DRAM_SEGMENT_IDX];
+ char sscd_msg[SSCD_MSG_LENGTH];
+
+ sscd_seg->addr = gxp->fwbufs[core_id].vaddr;
+ sscd_seg->size = gxp->fwbufs[core_id].size;
+
+ dev_dbg(gxp->dev, "Passing dram data to SSCD daemon\n");
+ snprintf(sscd_msg, SSCD_MSG_LENGTH - 1,
+ "gxp debug dump - dram data (core %0x)", core_id);
+ gxp_send_to_sscd(gxp, sscd_seg, 1, sscd_msg);
#endif
- return;
+ dram_seg_header->valid = 1;
}
-static int gxp_generate_coredump(struct gxp_dev *gxp,
- enum gxp_debug_dump_init_type init_type,
- uint core_bits)
-{
+static bool gxp_is_segment_valid(struct gxp_dev *gxp, uint32_t core_id,
+ int seg_idx) {
struct gxp_core_dump *core_dump;
- struct gxp_common_dump *common_dump;
- struct gxp_seg_header *common_seg_header;
- struct gxp_common_dump_data *common_dump_data;
+ struct gxp_core_dump_header *core_dump_header;
+ struct gxp_seg_header *seg_header;
+
+ core_dump = gxp->debug_dump_mgr->core_dump;
+ core_dump_header = &core_dump->core_dump_header[core_id];
+ seg_header = &core_dump_header->seg_header[seg_idx];
+
+ return seg_header->valid;
+}
+static int gxp_generate_coredump(struct gxp_dev *gxp, uint32_t core_id)
+{
if (!gxp->debug_dump_mgr->core_dump) {
dev_err(gxp->dev, "Core dump not allocated\n");
return -EINVAL;
}
- if (core_bits == 0) {
- dev_err(gxp->dev, "The number of core dumps requested is 0.\n");
- return -EINVAL;
- } else if (core_bits > GXP_COREDUMP_PENDING) {
- dev_err(gxp->dev,
- "The number of core dumps requested (%0x) is greater than expected (%0x)\n",
- core_bits, GXP_COREDUMP_PENDING);
- return -EINVAL;
- }
-
gxp_debug_dump_cache_invalidate(gxp);
- core_dump = gxp->debug_dump_mgr->core_dump;
- common_dump = kmalloc(sizeof(*common_dump), GFP_KERNEL);
- if (!common_dump)
- return -ENOMEM;
-
- common_seg_header = common_dump->seg_header;
- common_dump_data = &common_dump->common_dump_data;
-
- gxp_get_common_dump(gxp, common_seg_header, common_dump_data);
+ mutex_lock(&gxp->debug_dump_mgr->debug_dump_lock);
- gxp_handle_debug_dump(gxp, core_dump, common_dump, init_type,
- core_bits);
+ if (!gxp_is_segment_valid(gxp, core_id, GXP_CORE_DRAM_SEGMENT_IDX)) {
+ gxp_handle_dram_dump(gxp, core_id);
+ } else {
+ gxp_get_common_dump(gxp);
+ gxp_handle_debug_dump(gxp, core_id);
+ }
- /* Mark the common segments as read */
- common_seg_header->valid = 0;
+ mutex_unlock(&gxp->debug_dump_mgr->debug_dump_lock);
gxp_debug_dump_cache_flush(gxp);
@@ -414,6 +449,8 @@ static void gxp_wait_kernel_init_dump_work(struct work_struct *work)
struct gxp_debug_dump_manager *mgr =
container_of(work, struct gxp_debug_dump_manager,
wait_kernel_init_dump_work);
+ u32 core_bits;
+ int i;
wait_event_timeout(mgr->kernel_init_dump_waitq,
mgr->kernel_init_dump_pending ==
@@ -421,8 +458,12 @@ static void gxp_wait_kernel_init_dump_work(struct work_struct *work)
msecs_to_jiffies(KERNEL_INIT_DUMP_TIMEOUT));
mutex_lock(&mgr->lock);
- gxp_generate_coredump(mgr->gxp, DEBUG_DUMP_KERNEL_INIT,
- mgr->kernel_init_dump_pending);
+ core_bits = mgr->kernel_init_dump_pending;
+ for (i = 0; i < GXP_NUM_CORES; i++) {
+ if (!(core_bits & BIT(i)))
+ continue;
+ gxp_generate_coredump(mgr->gxp, i);
+ }
mgr->kernel_init_dump_pending = 0;
mutex_unlock(&mgr->lock);
}
@@ -460,7 +501,7 @@ void gxp_debug_dump_process_dump(struct work_struct *work)
switch (core_header->dump_req_reason) {
case DEBUG_DUMP_FW_INIT:
- gxp_generate_coredump(gxp, DEBUG_DUMP_FW_INIT, BIT(core_id));
+ gxp_generate_coredump(gxp, core_id);
break;
case DEBUG_DUMP_KERNEL_INIT:
mutex_lock(&mgr->lock);
@@ -489,7 +530,7 @@ int gxp_debug_dump_init(struct gxp_dev *gxp, void *sscd_dev, void *sscd_pdata)
struct resource r;
struct gxp_debug_dump_manager *mgr;
struct gxp_core_dump_header *core_dump_header;
- int core;
+ int core, i;
mgr = devm_kzalloc(gxp->dev, sizeof(*mgr), GFP_KERNEL);
if (!mgr)
@@ -524,6 +565,8 @@ int gxp_debug_dump_init(struct gxp_dev *gxp, void *sscd_dev, void *sscd_pdata)
for (core = 0; core < GXP_NUM_CORES; core++) {
core_dump_header = &mgr->core_dump->core_dump_header[core];
core_dump_header->core_header.dump_available = 0;
+ for (i = 0; i < GXP_NUM_CORE_SEGMENTS; i++)
+ core_dump_header->seg_header[i].valid = 0;
mgr->debug_dump_works[core].gxp = gxp;
mgr->debug_dump_works[core].core_id = core;
@@ -531,13 +574,15 @@ int gxp_debug_dump_init(struct gxp_dev *gxp, void *sscd_dev, void *sscd_pdata)
gxp_debug_dump_process_dump);
}
+ gxp_init_segments(gxp);
+
/* No need for a DMA handle since the carveout is coherent */
mgr->debug_dump_dma_handle = 0;
mgr->kernel_init_dump_pending = 0;
mgr->sscd_dev = sscd_dev;
mgr->sscd_pdata = sscd_pdata;
mutex_init(&mgr->lock);
- mutex_init(&mgr->sscd_lock);
+ mutex_init(&mgr->debug_dump_lock);
INIT_WORK(&mgr->wait_kernel_init_dump_work,
gxp_wait_kernel_init_dump_work);
@@ -557,11 +602,12 @@ void gxp_debug_dump_exit(struct gxp_dev *gxp)
}
cancel_work_sync(&mgr->wait_kernel_init_dump_work);
+ gxp_free_segments(gxp);
/* TODO (b/200169232) Remove this once we're using devm_memremap */
memunmap(gxp->coredumpbuf.vaddr);
mutex_destroy(&mgr->lock);
- mutex_destroy(&mgr->sscd_lock);
+ mutex_destroy(&mgr->debug_dump_lock);
devm_kfree(mgr->gxp->dev, mgr);
gxp->debug_dump_mgr = NULL;
}