/* * Copyright Meta Platforms, Inc. and its affiliates. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #define DT_DRV_COMPAT zephyr_coredump enum COREDUMP_TYPE { COREDUMP_TYPE_MEMCPY = 0, COREDUMP_TYPE_CALLBACK = 1, }; struct coredump_config { /* Type of coredump device */ enum COREDUMP_TYPE type; /* Length of memory_regions array */ int length; /* Memory regions specified in device tree */ size_t memory_regions[]; }; struct coredump_data { /* Memory regions registered at run time */ sys_slist_t region_list; /* Callback to be invoked at time of dump */ coredump_dump_callback_t dump_callback; }; static void coredump_impl_dump(const struct device *dev) { const struct coredump_config *config = dev->config; struct coredump_data *data = dev->data; if (config->type == COREDUMP_TYPE_CALLBACK) { if (data->dump_callback) { uintptr_t start_address = config->memory_regions[0]; size_t size = config->memory_regions[1]; /* Invoke callback to allow consumer to fill array with desired data */ data->dump_callback(start_address, size); coredump_memory_dump(start_address, start_address + size); } } else { /* COREDUMP_TYPE_MEMCPY */ /* * Add each memory region specified in device tree to the core dump, * the memory_regions array should contain two entries per region * containing the start address and size. */ if ((config->length > 0) && ((config->length % 2) == 0)) { for (int i = 0; i < config->length; i += 2) { uintptr_t start_address = config->memory_regions[i]; size_t size = config->memory_regions[i+1]; coredump_memory_dump(start_address, start_address + size); } } sys_snode_t *node; /* Add each memory region registered at runtime to the core dump */ SYS_SLIST_FOR_EACH_NODE(&data->region_list, node) { struct coredump_mem_region_node *region; region = CONTAINER_OF(node, struct coredump_mem_region_node, node); coredump_memory_dump(region->start, region->start + region->size); } } } static bool coredump_impl_register_memory(const struct device *dev, struct coredump_mem_region_node *region) { const struct coredump_config *config = dev->config; if (config->type == COREDUMP_TYPE_CALLBACK) { return false; } struct coredump_data *data = dev->data; sys_slist_append(&data->region_list, ®ion->node); return true; } static bool coredump_impl_unregister_memory(const struct device *dev, struct coredump_mem_region_node *region) { const struct coredump_config *config = dev->config; if (config->type == COREDUMP_TYPE_CALLBACK) { return false; } struct coredump_data *data = dev->data; return sys_slist_find_and_remove(&data->region_list, ®ion->node); } static bool coredump_impl_register_callback(const struct device *dev, coredump_dump_callback_t callback) { const struct coredump_config *config = dev->config; if (config->type == COREDUMP_TYPE_MEMCPY) { return false; } struct coredump_data *data = dev->data; data->dump_callback = callback; return true; } static int coredump_init(const struct device *dev) { struct coredump_data *data = dev->data; sys_slist_init(&data->region_list); return 0; } static const struct coredump_driver_api coredump_api = { .dump = coredump_impl_dump, .register_memory = coredump_impl_register_memory, .unregister_memory = coredump_impl_unregister_memory, .register_callback = coredump_impl_register_callback, }; #define INIT_REGION(node_id, prop, idx) DT_PROP_BY_IDX(node_id, prop, idx), #define DT_INST_COREDUMP_IF_TYPE_CALLBACK(n, a, b) \ COND_CODE_1(DT_INST_ENUM_IDX(n, coredump_type), a, b) #define CREATE_COREDUMP_DEVICE(n) \ /* Statially allocate desired memory for the callback type device */ \ DT_INST_COREDUMP_IF_TYPE_CALLBACK(n, \ ( \ BUILD_ASSERT(DT_INST_PROP_LEN(n, memory_regions) == 2, \ "Allow exactly one entry (address and size) in memory_regions"); \ BUILD_ASSERT(DT_INST_PROP_BY_IDX(n, memory_regions, 0) == 0, \ "Verify address is set to 0"); \ static uint8_t coredump_bytes[DT_INST_PROP_BY_IDX(n, memory_regions, 1)] \ __aligned(4); \ ), ()) \ static struct coredump_data coredump_data_##n; \ static const struct coredump_config coredump_config##n = { \ .type = DT_INST_STRING_TOKEN_OR(n, coredump_type, COREDUMP_TYPE_MEMCPY), \ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, memory_regions), \ ( \ .length = DT_INST_PROP_LEN(n, memory_regions), \ DT_INST_COREDUMP_IF_TYPE_CALLBACK(n, \ ( \ /* Callback type device has one entry in memory_regions array */ \ .memory_regions = { \ (size_t)&coredump_bytes[0], \ DT_INST_PROP_BY_IDX(n, memory_regions, 1), \ }, \ ), \ ( \ .memory_regions = { \ DT_INST_FOREACH_PROP_ELEM(n, memory_regions, INIT_REGION) \ }, \ )) \ ), \ ( \ .length = 0, \ )) \ }; \ DEVICE_DT_INST_DEFINE(n, \ coredump_init, \ NULL, \ &coredump_data_##n, \ &coredump_config##n, \ PRE_KERNEL_1, \ CONFIG_COREDUMP_DEVICE_INIT_PRIORITY, \ &coredump_api); DT_INST_FOREACH_STATUS_OKAY(CREATE_COREDUMP_DEVICE)