From 145000c5598922171293da4740582216f9a82349 Mon Sep 17 00:00:00 2001 From: liuwei Date: Thu, 18 Aug 2022 16:10:25 +0800 Subject: [PATCH] add config about blackbox in linux_5.10 Signed-off-by: liuwei --- drivers/staging/blackbox/Kconfig | 108 ++++ drivers/staging/blackbox/Makefile | 5 + drivers/staging/blackbox/blackbox_common.c | 272 +++++++++ drivers/staging/blackbox/blackbox_core.c | 589 ++++++++++++++++++++ drivers/staging/blackbox/blackbox_storage.c | 202 +++++++ include/linux/blackbox.h | 72 +++ include/linux/blackbox_common.h | 44 ++ include/linux/blackbox_storage.h | 22 + 8 files changed, 1314 insertions(+) create mode 100644 drivers/staging/blackbox/Kconfig create mode 100644 drivers/staging/blackbox/Makefile create mode 100644 drivers/staging/blackbox/blackbox_common.c create mode 100644 drivers/staging/blackbox/blackbox_core.c create mode 100644 drivers/staging/blackbox/blackbox_storage.c create mode 100644 include/linux/blackbox.h create mode 100644 include/linux/blackbox_common.h create mode 100644 include/linux/blackbox_storage.h diff --git a/drivers/staging/blackbox/Kconfig b/drivers/staging/blackbox/Kconfig new file mode 100644 index 000000000000..0e985823c2e8 --- /dev/null +++ b/drivers/staging/blackbox/Kconfig @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "Blackbox Options" + +config BLACKBOX + bool "Support for blackbox" + select STORAGE if BLACKBOX_STORAGE_MATERIAL + default y + help + The blackbox is a fault log collecting framework for registered modules + of chips. When a fault occurs, blackbox will invoke the registered + function to save the log and reset the module. + +config BLACKBOX_LOG_ROOT_PATH + string "root path of the blackbox log" + depends on BLACKBOX + help + define the root path of the blackbox log + +config BLACKBOX_LOG_PART_REPRESENTATIVE + string "representative of the blackbox log part" + depends on BLACKBOX + help + define the representative of the blackbox log part + +config BLACKBOX_STORAGE_BY_MEMORY + tristate "blackbox fault log storage by memory directly" + depends on BLACKBOX + select STORAGE_BY_MEMORY + help + This option enables saving fault logs with memory by blackbox when a + panic occurs. It depends on supporting warm reset and disabling erase + ddr when warm reset. + +config BLACKBOX_USE_PSTORE_BLK_DEBUG + bool "blackbox use pstore blk for debug" + depends on BLACKBOX + default n + help + If Y, this enables pstore blk for blackbox. + +config BLACKBOX_STORAGE_BY_PSTORE_BLK + tristate "blackbox fault log storage by pstore blk" + depends on BLACKBOX + depends on PSTORE_BLK + depends on PSTORE_BLACKBOX + select STORAGE_BY_PSTORE_BLK + help + This option enables saving fault logs with pstore blk by blackbox when a + panic occurs. It depends on supporting pstore blk. Especially, flash + driver's panic_write implementation is needed. Othersize, if a panic + happen, then fault log can not be saved. + +config BLACKBOX_STORAGE_BY_PSTORE_RAM + tristate "blackbox fault log storage by pstore ram" + depends on BLACKBOX + depends on PSTORE_RAM + depends on PSTORE_BLACKBOX + select STORAGE_BY_PSTORE_RAM + help + This option enables saving fault logs with pstore ram by blackbox when a + panic occurs. It depends on supporting pstore ram. + +config BLACKBOX_STORAGE_BY_RAW_PARTITION + tristate "blackbox fault log storage by RAW partition" + depends on BLACKBOX + select STORAGE_BY_RAW_PARTITION + help + This option enables saving fault logs with RAW partition by blackbox when a + panic occurs. It depends on reserving partition for blackbox. + +config BLACKBOX_STORAGE_MATERIAL + def_bool y + depends on BLACKBOX + depends on BLACKBOX_STORAGE_BY_MEMORY || BLACKBOX_STORAGE_BY_PSTORE_BLK || \ + BLACKBOX_STORAGE_BY_PSTORE_RAM || BLACKBOX_STORAGE_BY_RAW_PARTITION + +choice + prompt "Default storage material for fault log when a panic occurs." + depends on BLACKBOX_STORAGE_MATERIAL + help + This option choose the default fault log material for blackbox when a + panic occurs. + + The default materail is ram directly. It's easy, but not work offen. + + config DEF_BLACKBOX_STORAGE_BY_MEMORY + bool "memory" if BLACKBOX_STORAGE_BY_MEMORY + + config DEF_BLACKBOX_STORAGE_BY_PSTORE_BLK + bool "pstore_blk" if BLACKBOX_STORAGE_BY_PSTORE_BLK + + config DEF_BLACKBOX_STORAGE_BY_PSTORE_RAM + bool "pstore_ram" if BLACKBOX_STORAGE_BY_PSTORE_RAM + + config DEF_BLACKBOX_STORAGE_BY_RAW_PARTITION + bool "raw_partition" if BLACKBOX_STORAGE_BY_RAW_PARTITION + +endchoice + +config DEF_BLACKBOX_STORAGE + string + depends on BLACKBOX_STORAGE_MATERIAL + default "memory" if DEF_BLACKBOX_STORAGE_BY_MEMORY + default "pstore_blk" if DEF_BLACKBOX_STORAGE_BY_PSTORE_BLK + default "pstore_ram" if DEF_BLACKBOX_STORAGE_BY_PSTORE_RAM + default "raw_partition" if DEF_BLACKBOX_STORAGE_BY_RAW_PARTITION + +endmenu diff --git a/drivers/staging/blackbox/Makefile b/drivers/staging/blackbox/Makefile new file mode 100644 index 000000000000..9befa81a176e --- /dev/null +++ b/drivers/staging/blackbox/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_BLACKBOX) += blackbox_core.o \ + blackbox_storage.o \ + blackbox_common.o diff --git a/drivers/staging/blackbox/blackbox_common.c b/drivers/staging/blackbox/blackbox_common.c new file mode 100644 index 000000000000..72ee0c6e00b7 --- /dev/null +++ b/drivers/staging/blackbox/blackbox_common.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void sys_reset(void) +{ + bbox_print_info("reset the system now!\n"); + emergency_restart(); + bbox_print_info("reset the system failed!\n"); +} + +void change_own(char *path, int uid, int gid) +{ + mm_segment_t old_fs; + int ret = -1; + + if (unlikely(!path || uid == -1 || gid == -1)) { + bbox_print_err("path or uid or gid error.\n"); + return; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = ksys_chown(path, uid, gid); + if (ret != 0) + bbox_print_err("ksys_chown [%s] failed, ret: %d\n", path, ret); + + set_fs(old_fs); +} + +int full_write_file(const char *pfile_path, char *buf, + size_t buf_size, bool is_append) +{ + struct file *filp = NULL; + char *pathname = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + int ret = -1; + + if (unlikely(!pfile_path || !buf)) { + bbox_print_err("pfile_path or buf is NULL!\n"); + return -EINVAL; + } + + filp = file_open(pfile_path, O_CREAT | O_RDWR | + (is_append ? O_APPEND : O_TRUNC), BBOX_FILE_LIMIT); + if (IS_ERR(filp)) { + bbox_print_err("open %s failed! [%ld]\n", pfile_path, PTR_ERR(filp)); + return -EBADF; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + ret = vfs_write(filp, buf, buf_size, &pos); + + set_fs(old_fs); + + file_close(filp); + + if (ret < 0) { + pathname = getfullpath(filp); + bbox_print_err("write [%s] failed! [%d]\n", pathname ? pathname : "", ret); + return ret; + } + + return 0; +} + +int file_exists(const char *name) +{ + struct path path; + int ret; + + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (ret) + return ret; + + ret = inode_permission(d_inode(path.dentry), MAY_ACCESS); + path_put(&path); + return ret; +} + +static int create_new_dir(char *name) +{ + struct dentry *dentry; + struct path path; + int ret; + + if (unlikely(!name)) { + bbox_print_err("name is NULL!\n"); + return -EINVAL; + } + + ret = file_exists(name); + if (ret) { + dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + ret = vfs_mkdir(d_inode(path.dentry), dentry, BBOX_DIR_LIMIT); + if (ret && ret != -EEXIST) + bbox_print_err("Create dir [%s] failed! ret: %d\n", name, ret); + else + change_own(name, AID_ROOT, AID_SYSTEM); + + done_path_create(&path, dentry); + } + + return 0; +} + +int create_log_dir(const char *path) +{ + char *cur_path = NULL; + int index = 0; + + if (unlikely(!path)) { + bbox_print_err("path is NULL!\n"); + return -EINVAL; + } + + if (*path != '/') + return -EINVAL; + cur_path = kmalloc(PATH_MAX_LEN + 1, GFP_KERNEL); + if (!cur_path) { + bbox_print_err("kmalloc failed!\n"); + return -ENOMEM; + } + memset(cur_path, 0, PATH_MAX_LEN + 1); + cur_path[index++] = *path++; + while (*path != '\0') { + if (*path == '/') + create_new_dir(cur_path); + cur_path[index] = *path; + path++; + index++; + } + create_new_dir(cur_path); + kfree(cur_path); + + return 0; +} + +void get_timestamp(char *buf, size_t buf_size) +{ + struct rtc_time tm; + struct timespec64 tv; + + if (unlikely(!buf || buf_size == 0)) { + bbox_print_err("buf: %p, buf_size: %u\n", buf, (unsigned int)buf_size); + return; + } + + memset(buf, 0, buf_size); + memset(&tm, 0, sizeof(tm)); + + memset(&tv, 0, sizeof(tv)); + ktime_get_real_ts64(&tv); + tv.tv_sec -= (long)sys_tz.tz_minuteswest * SECONDS_PER_MINUTE; + rtc_time64_to_tm(tv.tv_sec, &tm); + + (void)scnprintf(buf, buf_size, TIMESTAMP_FORMAT, + tm.tm_year + YEAR_BASE, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, get_ticks()); + buf[buf_size - 1] = '\0'; +} +EXPORT_SYMBOL_GPL(get_timestamp); + +unsigned long long get_ticks(void) +{ + /* use only one int value to save time: */ + + struct timespec64 uptime; + + ktime_get_ts64(&uptime); + + ktime_get_boottime_ts64(&uptime); + + return (u64)uptime.tv_sec; +} + +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir = dget_parent(dentry); + + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + return dir; +} + +static inline void unlock_dir(struct dentry *dentry) +{ + inode_unlock(d_inode(dentry)); + dput(dentry); +} + +struct file *file_open(const char *filename, int open_mode, int mode) +{ + struct file *filp = NULL; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + filp = filp_open(filename, open_mode, mode); + set_fs(old_fs); + + return filp; +} + +void file_close(struct file *filp) +{ + if (likely(filp)) + filp_close(filp, NULL); +} + +int file_delete(struct file *filp) +{ + struct dentry *dentry = NULL; + struct dentry *parent = NULL; + int ret = 0; + + if (unlikely(!filp)) { + bbox_print_err("file is NULL!\n"); + return -EINVAL; + } + + dentry = file_dentry(filp); + parent = lock_parent(dentry); + + if (dentry->d_parent == parent) { + dget(dentry); + ret = vfs_unlink(d_inode(parent), dentry, NULL); + dput(dentry); + } + + unlock_dir(parent); + + return ret; +} + +char *getfullpath(struct file *filp) +{ + char *buf = NULL, *path = NULL; + + if (unlikely(!filp)) + return NULL; + + buf = kmalloc(PATH_MAX, GFP_KERNEL); + if (unlikely(!buf)) + return NULL; + memset(buf, 0, PATH_MAX); + + //get the path + path = d_path(&filp->f_path, buf, PATH_MAX); + + kfree(buf); + + return path; +} diff --git a/drivers/staging/blackbox/blackbox_core.c b/drivers/staging/blackbox/blackbox_core.c new file mode 100644 index 000000000000..35406e1f692d --- /dev/null +++ b/drivers/staging/blackbox/blackbox_core.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DFX_ZEROHUNG +#include +#endif +#include +#include + +/* ---- local macroes ---- */ +/* bbox/BBOX - blackbox */ +#define HISTORY_LOG_NAME "history.log" +#define LOG_PART_WAIT_TIME 1000 /* unit: ms */ +#define HISTORY_LOG_MAX_LEN 1024 +#define TOP_CATEGORY_SYSTEM_RESET "System Reset" +#define TOP_CATEGORY_FREEZE "System Freeze" +#define TOP_CATEGORY_SYSTEM_POWEROFF "POWEROFF" +#define TOP_CATEGORY_SUBSYSTEM_CRASH "Subsystem Crash" +#define CATEGORY_SYSTEM_REBOOT "SYSREBOOT" +#define CATEGORY_SYSTEM_POWEROFF "POWEROFF" +#define CATEGORY_SYSTEM_PANIC "PANIC" +#define CATEGORY_SYSTEM_OOPS "OOPS" +#define CATEGORY_SYSTEM_CUSTOM "CUSTOM" +#define CATEGORY_SYSTEM_WATCHDOG "HWWATCHDOG" +#define CATEGORY_SYSTEM_HUNGTASK "HUNGTASK" +#define CATEGORY_SUBSYSTEM_CUSTOM "CUSTOM" + +#ifndef CONFIG_BLACKBOX_LOG_ROOT_PATH +#error no blackbox log root path +#endif +#ifndef CONFIG_BLACKBOX_LOG_PART_REPRESENTATIVE +#error no representative of the blackbox log part +#endif + +/* ---- local prototypes ---- */ +struct bbox_ops { + struct list_head list; + struct module_ops ops; +}; + +struct error_info_to_category { + const char *module; + struct { + const char *event; + const char *category; + const char *top_category; + } map; +}; + +/* ---- local variables ---- */ +static LIST_HEAD(ops_list); +static DEFINE_SPINLOCK(ops_list_lock); +static DEFINE_SEMAPHORE(temp_error_info_sem); +static struct error_info_to_category error_info_categories[] = { + { + MODULE_SYSTEM, + {EVENT_SYSREBOOT, CATEGORY_SYSTEM_REBOOT, TOP_CATEGORY_SYSTEM_RESET} + }, + { + MODULE_SYSTEM, + {EVENT_LONGPRESS, CATEGORY_SYSTEM_REBOOT, TOP_CATEGORY_SYSTEM_RESET} + }, + { + MODULE_SYSTEM, + {EVENT_COMBINATIONKEY, CATEGORY_SYSTEM_REBOOT, TOP_CATEGORY_SYSTEM_RESET} + }, + { + MODULE_SYSTEM, + {EVENT_SUBSYSREBOOT, CATEGORY_SYSTEM_REBOOT, TOP_CATEGORY_SYSTEM_RESET} + }, + { + MODULE_SYSTEM, + {EVENT_POWEROFF, CATEGORY_SYSTEM_POWEROFF, TOP_CATEGORY_SYSTEM_POWEROFF} + }, + { + MODULE_SYSTEM, + {EVENT_PANIC, CATEGORY_SYSTEM_PANIC, TOP_CATEGORY_SYSTEM_RESET} + }, + { + MODULE_SYSTEM, + {EVENT_OOPS, CATEGORY_SYSTEM_OOPS, TOP_CATEGORY_SYSTEM_RESET} + }, + { + MODULE_SYSTEM, + {EVENT_SYS_WATCHDOG, CATEGORY_SYSTEM_WATCHDOG, TOP_CATEGORY_FREEZE} + }, + { + MODULE_SYSTEM, + {EVENT_HUNGTASK, CATEGORY_SYSTEM_HUNGTASK, TOP_CATEGORY_FREEZE} + }, +}; + +struct error_info *temp_error_info; + +/* ---- local function prototypes ---- */ +static const char *get_top_category(const char *module, const char *event); +static const char *get_category(const char *module, const char *event); +static void format_log_dir(char *buf, size_t buf_size, const char *log_root_dir, + const char *timestamp); +static void save_history_log(const char *log_root_dir, struct error_info *info, + const char *timestamp, int need_sys_reset); +static void save_invalid_log(const struct bbox_ops *ops, const struct error_info *info); +static void wait_for_log_part(void); +static void format_error_info(struct error_info *info, const char event[EVENT_MAX_LEN], + const char module[MODULE_MAX_LEN], + const char error_desc[ERROR_DESC_MAX_LEN]); +static void save_last_log(void); +static int save_error_log(void *pparam); + +/* ---- global function prototypes ---- */ + +/* ---- function definitions ---- */ +static const char *get_top_category(const char *module, const char *event) +{ + int i; + int count = (int)ARRAY_SIZE(error_info_categories); + + if (unlikely(!module || !event)) { + bbox_print_err("module: %p, event: %p\n", module, event); + return TOP_CATEGORY_SUBSYSTEM_CRASH; + } + + for (i = 0; i < count; i++) { + if (!strcmp(error_info_categories[i].module, module) && + !strcmp(error_info_categories[i].map.event, event)) { + return error_info_categories[i].map.top_category; + } + } + if (!strcmp(module, MODULE_SYSTEM)) + return TOP_CATEGORY_SYSTEM_RESET; + + return TOP_CATEGORY_SUBSYSTEM_CRASH; +} + +static const char *get_category(const char *module, const char *event) +{ + int i; + int count = (int)ARRAY_SIZE(error_info_categories); + + if (unlikely(!module || !event)) { + bbox_print_err("module: %p, event: %p\n", module, event); + return CATEGORY_SUBSYSTEM_CUSTOM; + } + + for (i = 0; i < count; i++) { + if (!strcmp(error_info_categories[i].module, module) && + !strcmp(error_info_categories[i].map.event, event)) { + return error_info_categories[i].map.category; + } + } + if (!strcmp(module, MODULE_SYSTEM)) + return CATEGORY_SYSTEM_CUSTOM; + + return CATEGORY_SUBSYSTEM_CUSTOM; +} + +static void format_log_dir(char *buf, size_t buf_size, const char *log_root_dir, + const char *timestamp) +{ + if (unlikely(!buf || buf_size == 0 || !log_root_dir || + !timestamp)) { + bbox_print_err("buf: %p, buf_size: %u, log_root_dir: %p, timestamp: %p\n", + buf, (unsigned int)buf_size, log_root_dir, timestamp); + return; + } + + memset(buf, 0, buf_size); + scnprintf(buf, buf_size - 1, "%s/%s", log_root_dir, timestamp); +} + +static void format_error_info(struct error_info *info, const char event[EVENT_MAX_LEN], + const char module[MODULE_MAX_LEN], + const char error_desc[ERROR_DESC_MAX_LEN]) +{ + if (unlikely(!info || !event || !module || !error_desc)) { + bbox_print_err("info: %p, event: %p, module: %p, error_desc: %p\n", + info, event, module, error_desc); + return; + } + + memset(info, 0, sizeof(*info)); + strncpy(info->event, event, min(strlen(event), + sizeof(info->event) - 1)); + strncpy(info->module, module, min(strlen(module), + sizeof(info->module) - 1)); + get_timestamp(info->error_time, TIMESTAMP_MAX_LEN); + strncpy(info->error_desc, error_desc, min(strlen(error_desc), + sizeof(info->error_desc) - 1)); +} + +static void save_history_log(const char *log_root_dir, struct error_info *info, + const char *timestamp, int need_sys_reset) +{ + char history_log_path[PATH_MAX_LEN]; + char *buf; + + if (unlikely(!log_root_dir || !info || !timestamp)) { + bbox_print_err("log_root_dir: %p, info: %p, timestamp: %p\n", + log_root_dir, info, timestamp); + return; + } + + buf = kmalloc(HISTORY_LOG_MAX_LEN + 1, GFP_KERNEL); + if (!buf) + return; + + memset(buf, 0, HISTORY_LOG_MAX_LEN + 1); + scnprintf(buf, HISTORY_LOG_MAX_LEN, HISTORY_LOG_FORMAT, + get_top_category(info->module, info->event), info->module, + get_category(info->module, info->event), info->event, timestamp, + need_sys_reset ? "true" : "false", info->error_desc); +#ifdef CONFIG_DFX_ZEROHUNG + zrhung_send_event("KERNEL_VENDOR", "PANIC", info->error_desc); +#endif + memset(history_log_path, 0, sizeof(history_log_path)); + scnprintf(history_log_path, sizeof(history_log_path) - 1, + "%s/%s", log_root_dir, HISTORY_LOG_NAME); + full_write_file(history_log_path, buf, strlen(buf), 1); + change_own(history_log_path, AID_ROOT, AID_SYSTEM); + kfree(buf); +} + +static void save_invalid_log(const struct bbox_ops *ops, const struct error_info *info) +{ + char invalid_log_path[PATH_MAX_LEN]; + char timestamp[TIMESTAMP_MAX_LEN]; + + if (unlikely(!ops || !info)) { + bbox_print_err("ops: %p, info: %p\n", ops, info); + return; + } + + get_timestamp(timestamp, sizeof(timestamp)); + format_log_dir(invalid_log_path, PATH_MAX_LEN, CONFIG_BLACKBOX_LOG_PART_REPRESENTATIVE, + timestamp); + create_log_dir(invalid_log_path); + if (ops->ops.save_last_log(invalid_log_path, (struct error_info *)info) != 0) + bbox_print_err("[%s] failed to save invalid log!\n", ops->ops.module); +} + +static bool is_log_part_mounted(void) +{ + return file_exists(CONFIG_BLACKBOX_LOG_PART_REPRESENTATIVE) == 0; +} + +static void wait_for_log_part(void) +{ + bbox_print_info("wait for log part [%s] begin!\n", + CONFIG_BLACKBOX_LOG_PART_REPRESENTATIVE); + while (!is_log_part_mounted()) + msleep(LOG_PART_WAIT_TIME); + + bbox_print_info("wait for log part [%s] end!\n", + CONFIG_BLACKBOX_LOG_PART_REPRESENTATIVE); +} + +static bool find_module_ops(struct error_info *info, struct bbox_ops **ops) +{ + struct bbox_ops *cur = NULL; + bool find_module = false; + + if (unlikely(!info || !ops)) { + bbox_print_err("info: %p, ops: %p!\n", info, ops); + return find_module; + } + + list_for_each_entry(cur, &ops_list, list) { + if (!strcmp(cur->ops.module, info->module)) { + *ops = cur; + find_module = true; + break; + } + } + if (!find_module) + bbox_print_err("[%s] hasn't been registered!\n", info->module); + + return find_module; +} + +static void invoke_module_ops(const char *log_dir, struct error_info *info, + struct bbox_ops *ops) +{ + if (unlikely(!info || !ops)) { + bbox_print_err("info: %p, ops: %p!\n", info, ops); + return; + } + + if (ops->ops.dump && log_dir) { + bbox_print_info("[%s] starts dumping data!\n", ops->ops.module); + ops->ops.dump(log_dir, info); + bbox_print_info("[%s] ends dumping data!\n", ops->ops.module); + } + if (ops->ops.reset) { + bbox_print_info("[%s] starts resetting!\n", ops->ops.module); + ops->ops.reset(info); + bbox_print_info("[%s] ends resetting!\n", ops->ops.module); + } +} + +static void save_log_without_reset(struct error_info *info) +{ + unsigned long flags; + struct bbox_ops *ops = NULL; + char *log_dir = NULL; + char timestamp[TIMESTAMP_MAX_LEN]; + + if (unlikely(!info)) { + bbox_print_err("info: %p!\n", info); + return; + } + + /* get timestamp */ + get_timestamp(timestamp, sizeof(timestamp)); + + /* get bbox ops */ + spin_lock_irqsave(&ops_list_lock, flags); + if (!find_module_ops(info, &ops)) { + spin_unlock_irqrestore(&ops_list_lock, flags); + return; + } + spin_unlock_irqrestore(&ops_list_lock, flags); + create_log_dir(CONFIG_BLACKBOX_LOG_ROOT_PATH); + if (ops->ops.dump) { + /* create log root path */ + log_dir = kmalloc(PATH_MAX_LEN, GFP_KERNEL); + if (log_dir) { + format_log_dir(log_dir, PATH_MAX_LEN, + CONFIG_BLACKBOX_LOG_ROOT_PATH, timestamp); + create_log_dir(log_dir); + } else + bbox_print_err("kmalloc failed!\n"); + } + invoke_module_ops(log_dir, info, ops); + save_history_log(CONFIG_BLACKBOX_LOG_ROOT_PATH, info, timestamp, 0); + kfree(log_dir); +} + +static void save_log_with_reset(struct error_info *info) +{ + struct bbox_ops *ops = NULL; + + if (unlikely(!info)) { + bbox_print_err("info: %p!\n", info); + return; + } + + if (!find_module_ops(info, &ops)) + return; + + invoke_module_ops("", info, ops); + if (strcmp(info->event, EVENT_SYSREBOOT)) + sys_reset(); +} + +static void save_temp_error_info(const char event[EVENT_MAX_LEN], + const char module[MODULE_MAX_LEN], + const char error_desc[ERROR_DESC_MAX_LEN]) +{ + if (unlikely(!event || !module || !error_desc)) { + bbox_print_err("event: %p, module: %p, error_desc: %p\n", + event, module, error_desc); + return; + } + + down(&temp_error_info_sem); + format_error_info(temp_error_info, event, module, error_desc); + up(&temp_error_info_sem); +} + +static void do_save_last_log(const struct bbox_ops *ops, const struct error_info *info) +{ + char *log_dir = NULL; + int ret; + + if (unlikely(!ops || !info)) { + bbox_print_err("ops: %p, info: %p\n", + ops, info); + return; + } + + memset((void *)info, 0, sizeof(*info)); + ret = ops->ops.get_last_log_info((struct error_info *)info); + if (ret) { + bbox_print_err("[%s] failed to get log info!\n", ops->ops.module); + if (ret == -ENOMSG) + save_invalid_log(ops, info); + return; + } + bbox_print_info("[%s] starts saving log!\n", ops->ops.module); + bbox_print_info("event: [%s] module: [%s], time is [%s]!\n", + info->event, info->module, info->error_time); + + log_dir = kmalloc(PATH_MAX_LEN, GFP_KERNEL); + if (!log_dir) + return; + + if (!strlen(info->error_time)) + get_timestamp((char *)info->error_time, TIMESTAMP_MAX_LEN); + + format_log_dir(log_dir, PATH_MAX_LEN, CONFIG_BLACKBOX_LOG_ROOT_PATH, + info->error_time); + create_log_dir(log_dir); + if (ops->ops.save_last_log(log_dir, (struct error_info *)info) == 0) + save_history_log(CONFIG_BLACKBOX_LOG_ROOT_PATH, + (struct error_info *)info, info->error_time, 1); + else + bbox_print_err("[%s] failed to save log!\n", ops->ops.module); + kfree(log_dir); +} + +static void save_last_log(void) +{ + unsigned long flags; + struct error_info *info = NULL; + struct bbox_ops *ops = NULL; + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return; + + spin_lock_irqsave(&ops_list_lock, flags); + list_for_each_entry(ops, &ops_list, list) { + if (ops->ops.get_last_log_info && + ops->ops.save_last_log) { + spin_unlock_irqrestore(&ops_list_lock, flags); + do_save_last_log(ops, info); + spin_lock_irqsave(&ops_list_lock, flags); + } else { + bbox_print_err("[%s] get_last_log_info: %p, %s: %p\n", + ops->ops.module, ops->ops.get_last_log_info, + __func__, ops->ops.save_last_log); + } + } + spin_unlock_irqrestore(&ops_list_lock, flags); + kfree(info); +} + +static void save_temp_error_log(void) +{ + down(&temp_error_info_sem); + if (!temp_error_info) { + bbox_print_err("temp_error_info: %p\n", temp_error_info); + up(&temp_error_info_sem); + return; + } + + if (strlen(temp_error_info->event) != 0) + save_log_without_reset(temp_error_info); + + kfree(temp_error_info); + temp_error_info = NULL; + up(&temp_error_info_sem); +} + +static int save_error_log(void *pparam) +{ + wait_for_log_part(); + save_last_log(); + save_temp_error_log(); + + return 0; +} + +int bbox_register_module_ops(struct module_ops *ops) +{ + struct bbox_ops *new_ops = NULL; + struct bbox_ops *temp = NULL; + unsigned long flags; + + if (unlikely(!ops)) { + bbox_print_err("ops: %p\n", ops); + return -EINVAL; + } + + new_ops = kmalloc(sizeof(*new_ops), GFP_KERNEL); + if (!new_ops) + return -ENOMEM; + memset(new_ops, 0, sizeof(*new_ops)); + memcpy(&new_ops->ops, ops, sizeof(*ops)); + spin_lock_irqsave(&ops_list_lock, flags); + if (list_empty(&ops_list)) + goto __out; + + list_for_each_entry(temp, &ops_list, list) { + if (!strcmp(temp->ops.module, ops->module)) { + spin_unlock_irqrestore(&ops_list_lock, flags); + kfree(new_ops); + bbox_print_info("[%s] has been registered!\n", temp->ops.module); + return -ENODATA; + } + } + +__out: + bbox_print_info("[%s] is registered successfully!\n", ops->module); + list_add_tail(&new_ops->list, &ops_list); + spin_unlock_irqrestore(&ops_list_lock, flags); + + return 0; +} + +int bbox_notify_error(const char event[EVENT_MAX_LEN], const char module[MODULE_MAX_LEN], + const char error_desc[ERROR_DESC_MAX_LEN], int need_sys_reset) +{ + struct error_info *info = NULL; + + if (unlikely(!event || !module || !error_desc)) { + bbox_print_err("event: %p, module: %p, error_desc: %p\n", event, + module, error_desc); + return -EINVAL; + } + + info = kmalloc(sizeof(*info), GFP_ATOMIC); + if (!info) + return -ENOMEM; + + format_error_info(info, event, module, error_desc); + show_stack(current, NULL, KERN_DEFAULT); + if (!need_sys_reset) { + /* handle the error which do not need reset */ + if (!is_log_part_mounted()) + save_temp_error_info(event, module, error_desc); + else + save_log_without_reset(info); + } else { + /* handle the error which need reset */ + save_log_with_reset(info); + } + + kfree(info); + + return 0; +} + +static void __init select_storage_material(void) +{ + const struct reboot_crashlog_storage *tmp = NULL; + + if (!storage_material) + return; + + for (tmp = storage_lastwords; tmp->material; tmp++) { + if (!strcmp(storage_material, tmp->material)) { + storage_lastword = tmp; + return; + } + } +} + +static int __init blackbox_core_init(void) +{ + struct task_struct *tsk = NULL; + + select_storage_material(); + + temp_error_info = kmalloc(sizeof(*temp_error_info), GFP_KERNEL); + if (!temp_error_info) + return -ENOMEM; + + memset(temp_error_info, 0, sizeof(*temp_error_info)); + + /* Create a kernel thread to save log */ + tsk = kthread_run(save_error_log, NULL, "save_error_log"); + if (IS_ERR(tsk)) { + kfree(temp_error_info); + temp_error_info = NULL; + bbox_print_err("kthread_run failed!\n"); + return -ESRCH; + } + + return 0; +} + +core_initcall(blackbox_core_init); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Blackbox core framework"); +MODULE_AUTHOR("OHOS"); diff --git a/drivers/staging/blackbox/blackbox_storage.c b/drivers/staging/blackbox/blackbox_storage.c new file mode 100644 index 000000000000..21543ad68e95 --- /dev/null +++ b/drivers/staging/blackbox/blackbox_storage.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +char *storage_material = +#ifdef CONFIG_DEF_BLACKBOX_STORAGE + CONFIG_DEF_BLACKBOX_STORAGE; +#else + NULL; +#endif +const struct reboot_crashlog_storage *storage_lastword __ro_after_init; + +#if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_MEMORY) +static DEFINE_SEMAPHORE(kmsg_sem); +static char *lastlog; +unsigned int lastlog_len; +static int get_log_by_memory(void *in, unsigned int inlen) +{ + return 0; +} + +static int storage_log_by_memory(void *out, unsigned int outlen) +{ + if (unlikely(!out)) + return -EINVAL; + + /* Initialized from caller. */ + lastlog = out; + lastlog_len = outlen; + return 0; +} + +/* Called after storage_log_by_memory successfully. */ +static void do_kmsg_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason) +{ + struct fault_log_info *pinfo; + + if (unlikely(!lastlog)) + return; + + /* get kernel log from kmsg dump module */ + if (down_trylock(&kmsg_sem) != 0) { + bbox_print_err("down_trylock failed!\n"); + return; + } + pinfo = (struct fault_log_info *)lastlog; + (void)kmsg_dump_get_buffer(dumper, true, lastlog + sizeof(*pinfo), + lastlog_len - sizeof(*pinfo), (size_t *)&pinfo->len); + up(&kmsg_sem); +} +#endif + +#if defined(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_BLK) || \ + defined(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_RAM) +#define LOG_FILE_WAIT_TIME 1000 /* unit: ms */ +#define RETRY_MAX_COUNT 10 +#define PSTORE_MOUNT_POINT "/sys/fs/pstore/" +#define FILE_LIMIT (0660) + +static bool is_pstore_part_ready(char *pstore_file) +{ + const char *cur_name = NULL; + struct dentry *root_dentry; + struct dentry *cur_dentry; + struct file *filp = NULL; + char *full_path = NULL; + bool is_ready = false; + + if (unlikely(!pstore_file)) + return -EINVAL; + memset(pstore_file, 0, sizeof(*pstore_file)); + + filp = file_open(PSTORE_MOUNT_POINT, O_RDONLY, 0); + if (IS_ERR(filp)) { + bbox_print_err("open %s failed! err is [%ld]\n", PSTORE_MOUNT_POINT, PTR_ERR(filp)); + return -EBADF; + } + + full_path = kmalloc(PATH_MAX_LEN, GFP_KERNEL); + if (!full_path) + goto __out; + + root_dentry = filp->f_path.dentry; + list_for_each_entry(cur_dentry, &root_dentry->d_subdirs, d_child) { + cur_name = cur_dentry->d_name.name; + + memset(full_path, 0, PATH_MAX_LEN); + snprintf(full_path, PATH_MAX_LEN - 1, "%s%s", PSTORE_MOUNT_POINT, cur_name); + + if (S_ISREG(d_inode(cur_dentry)->i_mode) && !strncmp(cur_name, "blackbox", + strlen("blackbox"))) { + is_ready = true; + if (strcmp(full_path, pstore_file) > 0) + strncpy(pstore_file, full_path, strlen(full_path)); + } + } + + if (is_ready && strlen(pstore_file)) + bbox_print_info("get pstore file name %s successfully!\n", pstore_file); + +__out: + file_close(filp); + kfree(full_path); + + return is_ready; +} + +static int get_log_by_pstore(void *in, unsigned int inlen) +{ + char pstore_file[PATH_MAX_LEN]; + struct file *filp = NULL; + char *pathname = NULL; + mm_segment_t old_fs; + void *pbuf = NULL; + loff_t pos = 0; + static int retry; + int ret = -1; + + memset(pstore_file, 0, PATH_MAX_LEN); + while (!is_pstore_part_ready((char *)&pstore_file)) { + msleep(LOG_FILE_WAIT_TIME); + retry++; + if (retry >= RETRY_MAX_COUNT) + return -ENOENT; + } + + if (likely(in)) { + filp = file_open(pstore_file, O_RDONLY, FILE_LIMIT); + if (IS_ERR(filp)) { + bbox_print_err("open %s failed! err is [%ld]\n", pstore_file, + PTR_ERR(filp)); + return -EBADF; + } + memset(in, 0, inlen); + pbuf = in; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + ret = vfs_read(filp, pbuf, inlen, &pos); + if (ret < 0) { + pathname = getfullpath(filp); + bbox_print_err("read %s failed! err is [%d]\n", pathname ? pathname : "", + ret); + goto __error; + } + + set_fs(old_fs); + file_close(filp); + file_delete(filp); + return 0; + } + + return -EBADF; +__error: + set_fs(old_fs); + file_close(filp); + return -EIO; +} +#endif + +const struct reboot_crashlog_storage storage_lastwords[] = { +#if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_MEMORY) + { + .get_log = get_log_by_memory, + .storage_log = storage_log_by_memory, + .blackbox_dump = do_kmsg_dump, + .material = "memory", + }, +#endif +#if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_BLK) + { + .get_log = get_log_by_pstore, + .blackbox_dump = pstore_blackbox_dump, + .material = "pstore_blk", + }, +#endif +#if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_RAM) + { + .get_log = get_log_by_pstore, + .blackbox_dump = pstore_blackbox_dump, + .material = "pstore_ram", + }, +#endif +#if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_RAW_PARTITION) + { + .material = "raw_partition", + }, +#endif + { } +}; + diff --git a/include/linux/blackbox.h b/include/linux/blackbox.h new file mode 100644 index 000000000000..a3bc4ce38400 --- /dev/null +++ b/include/linux/blackbox.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef BLACKBOX_H +#define BLACKBOX_H + +#include +#include + +#define PATH_MAX_LEN 256 +#define EVENT_MAX_LEN 32 +#define MODULE_MAX_LEN 32 +#define TIMESTAMP_MAX_LEN 24 +#define ERROR_DESC_MAX_LEN 512 +#define LOG_FLAG "VALIDLOG" + +/* module type */ +#define MODULE_SYSTEM "SYSTEM" + +/* fault event type */ +#define EVENT_SYSREBOOT "SYSREBOOT" +#define EVENT_LONGPRESS "LONGPRESS" +#define EVENT_COMBINATIONKEY "COMBINATIONKEY" +#define EVENT_SUBSYSREBOOT "SUBSYSREBOOT" +#define EVENT_POWEROFF "POWEROFF" +#define EVENT_PANIC "PANIC" +#define EVENT_OOPS "OOPS" +#define EVENT_SYS_WATCHDOG "SYSWATCHDOG" +#define EVENT_HUNGTASK "HUNGTASK" +#define EVENT_BOOTFAIL "BOOTFAIL" + +#define FILE_NAME(x) (strrchr(x, '/') ? (strrchr(x, '/') + 1) : x) +#define BBOX_DECORATOR_HILOG(level, fmt, args...) \ + pr_err("bbox:[%s][%s:%d] " fmt, level, FILE_NAME(__FILE__), __LINE__, ##args) + +#define bbox_print_fatal(fmt, args...) BBOX_DECORATOR_HILOG("fatal", fmt, ##args) +#define bbox_print_err(fmt, args...) BBOX_DECORATOR_HILOG("err", fmt, ##args) +#define bbox_print_warn(fmt, args...) BBOX_DECORATOR_HILOG("warn", fmt, ##args) +#define bbox_print_info(fmt, args...) BBOX_DECORATOR_HILOG("info", fmt, ##args) +#define bbox_print_debug(fmt, args...) BBOX_DECORATOR_HILOG("debug", fmt, ##args) + +struct error_info { + char event[EVENT_MAX_LEN]; + char module[MODULE_MAX_LEN]; + char error_time[TIMESTAMP_MAX_LEN]; + char error_desc[ERROR_DESC_MAX_LEN]; +}; + +struct fault_log_info { + char flag[8]; /* 8 is the length of the flag */ + size_t len; /* length of the kernel fault log */ + struct error_info info; +}; + +struct module_ops { + char module[MODULE_MAX_LEN]; + void (*dump)(const char *log_dir, struct error_info *info); + void (*reset)(struct error_info *info); + int (*get_last_log_info)(struct error_info *info); + int (*save_last_log)(const char *log_dir, struct error_info *info); +}; + +void get_timestamp(char *buf, size_t buf_size); +int bbox_register_module_ops(struct module_ops *ops); +int bbox_notify_error(const char event[EVENT_MAX_LEN], + const char module[MODULE_MAX_LEN], + const char error_desc[ERROR_DESC_MAX_LEN], + int need_sys_reset); + +#endif /* BLACKBOX_H */ diff --git a/include/linux/blackbox_common.h b/include/linux/blackbox_common.h new file mode 100644 index 000000000000..85898a4e91d3 --- /dev/null +++ b/include/linux/blackbox_common.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef BLACKBOX_COMMON_H +#define BLACKBOX_COMMON_H + +#include + +/* bbox/BBOX - blackbox */ +#define YEAR_BASE 1900 +#define SECONDS_PER_MINUTE 60 +#define AID_ROOT 0 +#define AID_SYSTEM 1000 +#define BBOX_DIR_LIMIT 0775 +#define BBOX_FILE_LIMIT 0664 +#define PATH_MAX_LEN 256 + +/* + * format: + * [topCategoryName],module[moduleName],category[categoryName],\ + * event[eventName],time[seconds from 1970-01-01 00:00:00 UTC-tick],\ + * sysreboot[true|false],errordesc[errorDescription]\n + */ +#define HISTORY_LOG_FORMAT "[%s],module[%s],category[%s],event[%s],"\ + "time[%s],sysreboot[%s],errdesc[%s]\n" +#define TIMESTAMP_FORMAT "%04d%02d%02d%02d%02d%02d-%08llu" + +void sys_reset(void); +void change_own(char *path, int uid, int gid); +int full_write_file(const char *pfile_path, char *buf, + size_t buf_size, bool read_file); +int file_exists(const char *name); +int create_log_dir(const char *path); +unsigned long long get_ticks(void); +struct file *file_open(const char *filename, int open_mode, int mode); +void file_close(struct file *filp); +ssize_t file_read(struct file *file, loff_t offset, unsigned char *data, + size_t size); +int file_delete(struct file *filp); +char *getfullpath(struct file *filp); + +#endif /* BLACKBOX_COMMON_H */ diff --git a/include/linux/blackbox_storage.h b/include/linux/blackbox_storage.h new file mode 100644 index 000000000000..52d67523d71c --- /dev/null +++ b/include/linux/blackbox_storage.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef BLACKBOX_STORAGE_H +#define BLACKBOX_STORAGE_H + +#include + +struct reboot_crashlog_storage { + int (*storage_log)(void *out, unsigned int outlen); + int (*get_log)(void *in, unsigned int inlen); + void (*blackbox_dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason); + const char *material; +}; + +extern char *storage_material; +extern const struct reboot_crashlog_storage *storage_lastword; +extern const struct reboot_crashlog_storage storage_lastwords[]; + +#endif /* BLACKBOX_STORAGE_H */ -- Gitee