diff options
Diffstat (limited to 'gxp-debug-dump.c')
-rw-r--r-- | gxp-debug-dump.c | 308 |
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; } |