blob: a56808c33682591de8400eb563c6d09900d2be0d [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Implements chip specific details of debug dump memory initialization and SSCD registration.
*
* Copyright (C) 2021 Google, Inc.
*/
#if IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP) || IS_ENABLED(CONFIG_EDGETPU_TEST)
#include <linux/platform_device.h>
#include "abrolhos-platform.h"
#include "mobile-debug-dump.c"
static void sscd_release(struct device *dev)
{
pr_debug(DRIVER_NAME " release\n");
}
static struct sscd_platform_data sscd_pdata;
static struct platform_device sscd_dev = {
.name = DRIVER_NAME,
.driver_override = SSCD_NAME,
.id = -1,
.dev = {
.platform_data = &sscd_pdata,
.release = sscd_release,
},
};
static int abrolhos_sscd_generate_coredump(void *p_etdev, void *p_dump_setup)
{
struct edgetpu_dev *etdev;
struct edgetpu_debug_dump_setup *dump_setup;
struct abrolhos_platform_dev *pdev;
struct sscd_platform_data *pdata;
struct platform_device *sscd_dev;
struct sscd_segment *segs;
struct edgetpu_debug_dump *debug_dump;
struct edgetpu_crash_reason *crash_reason;
struct edgetpu_dump_segment *dump_seg;
struct edgetpu_device_group *group;
struct edgetpu_device_group **groups;
struct edgetpu_list_group *g;
struct mobile_sscd_mappings_dump *mappings_dump = NULL;
char crash_info[128];
int sscd_dump_segments_num;
int i, ret;
size_t num_groups = 0, num_queues = 0;
u64 offset;
if (!p_etdev || !p_dump_setup)
return -EINVAL;
etdev = (struct edgetpu_dev *)p_etdev;
dump_setup = (struct edgetpu_debug_dump_setup *)p_dump_setup;
pdev = to_abrolhos_dev(etdev);
pdata = (struct sscd_platform_data *)pdev->sscd_info.pdata;
sscd_dev = (struct platform_device *)pdev->sscd_info.dev;
if (!pdata->sscd_report) {
etdev_err(etdev, "failed to generate coredump");
return -ENOENT;
}
debug_dump = (struct edgetpu_debug_dump *)(dump_setup + 1);
/* Populate crash reason */
crash_reason = (struct edgetpu_crash_reason *)((u8 *)dump_setup +
debug_dump->crash_reason_offset);
scnprintf(crash_info, sizeof(crash_info),
"[edgetpu_coredump] error code: %#llx", crash_reason->code);
mutex_lock(&etdev->groups_lock);
groups = kmalloc_array(etdev->n_groups, sizeof(*groups), GFP_KERNEL);
if (!groups) {
mutex_unlock(&etdev->groups_lock);
return -ENOMEM;
}
etdev_for_each_group(etdev, g, group) {
if (edgetpu_device_group_is_disbanded(group))
continue;
groups[num_groups++] = edgetpu_device_group_get(group);
}
mutex_unlock(&etdev->groups_lock);
/* Allocate memory for dump segments */
sscd_dump_segments_num = debug_dump->dump_segments_num;
sscd_dump_segments_num += 2 * num_groups; /* VII cmd and resp queues */
sscd_dump_segments_num += num_groups ? 1 : 0; /* Mappings info */
sscd_dump_segments_num += 2; /* KCI cmd and resp queues */
segs = kmalloc_array(sscd_dump_segments_num,
sizeof(struct sscd_segment),
GFP_KERNEL);
if (!segs) {
ret = -ENOMEM;
goto out_sscd_generate_coredump;
}
/* Populate sscd segments */
dump_seg = (struct edgetpu_dump_segment *)((u8 *)dump_setup +
debug_dump->dump_segments_offset);
offset = debug_dump->dump_segments_offset;
for (i = 0; i < debug_dump->dump_segments_num; i++) {
segs[i].addr = dump_seg;
segs[i].size = sizeof(struct edgetpu_dump_segment) + dump_seg->size;
segs[i].paddr = (void *)(etdev->debug_dump_mem.tpu_addr +
offset);
segs[i].vaddr = (void *)(etdev->debug_dump_mem.vaddr +
offset);
offset += sizeof(struct edgetpu_dump_segment) + dump_seg->size;
dump_seg = (struct edgetpu_dump_segment *)
((u8 *)dump_setup + ALIGN(offset, sizeof(uint64_t)));
}
if (num_groups) {
mappings_dump = mobile_sscd_collect_mappings_segment(groups, num_groups, &segs[i]);
if (!mappings_dump) {
ret = -ENOMEM;
goto out_sscd_generate_coredump;
}
i++;
}
num_queues = mobile_sscd_collect_cmd_resp_queues(etdev, groups, num_groups, &segs[i]);
/* Adjust num of segments as some groups may have a detached mailbox */
sscd_dump_segments_num -= (2 * num_groups + 2); /* Subtract number of VII and KCI queues
* according to num_groups.
*/
sscd_dump_segments_num += num_queues; /* Add actual number of valid VII and KCI queues */
/* Pass dump data to SSCD daemon */
etdev_dbg(etdev, "report: %d segments", sscd_dump_segments_num);
ret = pdata->sscd_report(sscd_dev, segs, sscd_dump_segments_num,
SSCD_FLAGS_ELFARM64HDR, crash_info);
out_sscd_generate_coredump:
for (i = 0; i < num_groups; i++)
edgetpu_device_group_put(groups[i]);
kfree(mappings_dump);
kfree(segs);
kfree(groups);
return ret;
}
int edgetpu_debug_dump_init(struct edgetpu_dev *etdev)
{
size_t size;
int ret;
struct edgetpu_debug_dump_setup *dump_setup;
struct abrolhos_platform_dev *pdev;
pdev = to_abrolhos_dev(etdev);
size = EDGETPU_DEBUG_DUMP_MEM_SIZE;
/* Register SSCD platform device */
ret = platform_device_register(&sscd_dev);
if (ret) {
etdev_err(etdev, "SSCD platform device registration failed: %d", ret);
return ret;
}
/*
* Allocate a buffer for various dump segments
*/
ret = edgetpu_alloc_coherent(etdev, size, &etdev->debug_dump_mem,
EDGETPU_CONTEXT_KCI);
if (ret) {
etdev_err(etdev, "Debug dump seg alloc failed");
etdev->debug_dump_mem.vaddr = NULL;
goto out_unregister_platform;
}
dump_setup =
(struct edgetpu_debug_dump_setup *)etdev->debug_dump_mem.vaddr;
memset(dump_setup, 0, size);
dump_setup->dump_mem_size = size;
/*
* Allocate memory for debug dump handlers
*/
etdev->debug_dump_handlers = kcalloc(DUMP_REASON_NUM,
sizeof(*etdev->debug_dump_handlers),
GFP_KERNEL);
if (!etdev->debug_dump_handlers)
return -ENOMEM;
etdev->debug_dump_handlers[DUMP_REASON_REQ_BY_USER] =
abrolhos_sscd_generate_coredump;
pdev->sscd_info.pdata = &sscd_pdata;
pdev->sscd_info.dev = &sscd_dev;
return ret;
out_unregister_platform:
platform_device_unregister(&sscd_dev);
return ret;
}
void edgetpu_debug_dump_exit(struct edgetpu_dev *etdev)
{
if (!etdev->debug_dump_mem.vaddr) {
etdev_dbg(etdev, "Debug dump not allocated");
return;
}
/*
* Free the memory assigned for debug dump
*/
edgetpu_free_coherent(etdev, &etdev->debug_dump_mem,
EDGETPU_CONTEXT_KCI);
kfree(etdev->debug_dump_handlers);
platform_device_unregister(&sscd_dev);
}
#else /* IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP) || IS_ENABLED(CONFIG_EDGETPU_TEST) */
#include "edgetpu-debug-dump.c"
int edgetpu_debug_dump_init(struct edgetpu_dev *etdev)
{
return 0;
}
void edgetpu_debug_dump_exit(struct edgetpu_dev *etdev)
{
}
#endif /* IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP) || IS_ENABLED(CONFIG_EDGETPU_TEST) */