From abe7daea56644af56a9bd8bab5d5b03e575db59c Mon Sep 17 00:00:00 2001 From: winde_liu Date: Tue, 10 May 2022 19:08:48 +0800 Subject: [PATCH] kuanghong blackbox requirement adaptation --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/blackbox/Kconfig | 108 ++++ drivers/staging/blackbox/Makefile | 5 + drivers/staging/blackbox/blackbox_common.c | 276 +++++++++ drivers/staging/blackbox/blackbox_core.c | 591 ++++++++++++++++++++ drivers/staging/blackbox/blackbox_storage.c | 203 +++++++ fs/pstore/Kconfig | 20 + fs/pstore/internal.h | 3 + fs/pstore/platform.c | 118 +++- fs/pstore/ram.c | 74 ++- include/linux/blackbox.h | 72 +++ include/linux/blackbox_common.h | 44 ++ include/linux/blackbox_storage.h | 22 + include/linux/pstore.h | 7 + include/linux/pstore_ram.h | 1 + 16 files changed, 1540 insertions(+), 7 deletions(-) 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/Kconfig b/drivers/staging/Kconfig index 830a4b656fe8..f54802de5289 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -130,4 +130,6 @@ source "drivers/staging/hilog/Kconfig" source "drivers/staging/hievent/Kconfig" +source "drivers/staging/blackbox/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 84216c89fe12..3f8ae47086a1 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ obj-$(CONFIG_EROFS_FS) += erofs/ obj-$(CONFIG_HILOG) += hilog/ obj-$(CONFIG_HIEVENT) += hievent/ +obj-$(CONFIG_BLACKBOX) += blackbox/ 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..8416517eb583 --- /dev/null +++ b/drivers/staging/blackbox/blackbox_common.c @@ -0,0 +1,276 @@ +// 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 + +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), 0); + 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 = vmalloc(PATH_MAX_LEN + 1); + if (!cur_path) { + bbox_print_err("vmalloc 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); + vfree(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); + + if (IS_ERR(filp)) + return NULL; + + 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..f90a69c733be --- /dev/null +++ b/drivers/staging/blackbox/blackbox_core.c @@ -0,0 +1,591 @@ +// 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 +#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 = vmalloc(HISTORY_LOG_MAX_LEN + 1); + 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); + vfree(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 */ + 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("vmalloc failed!\n"); + } + invoke_module_ops(log_dir, info, ops); + save_history_log(CONFIG_BLACKBOX_LOG_ROOT_PATH, info, timestamp, 0); + if (log_dir) + vfree(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 = vmalloc(PATH_MAX_LEN); + 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); + vfree(log_dir); +} + +static void save_last_log(void) +{ + unsigned long flags; + struct error_info *info = NULL; + struct bbox_ops *ops = NULL; + + info = vmalloc(sizeof(*info)); + 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); + vfree(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); + + vfree(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 = vmalloc(sizeof(*new_ops)); + 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); + vfree(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 = vmalloc(sizeof(*info)); + 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); + } + + vfree(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 = vmalloc(sizeof(*temp_error_info)); + 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)) { + vfree(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..635e3aa036ab --- /dev/null +++ b/drivers/staging/blackbox/blackbox_storage.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include +#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 = vmalloc(PATH_MAX_LEN); + 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); + vfree(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/fs/pstore/Kconfig b/fs/pstore/Kconfig index 503086f7f7c1..63d70cc3a461 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -137,6 +137,18 @@ config PSTORE_FTRACE If unsure, say N. +config PSTORE_BLACKBOX + bool "Store customised fault log" + depends on PSTORE + depends on BLACKBOX + help + Enable storing the customised fault log for BlackBox. + + With the option enabled, pstore will store the customised kernel + fault log for BlackBox when oops or panic happened. + + If unsure, say N. + config PSTORE_RAM tristate "Log panic/oops to a RAM buffer" depends on PSTORE @@ -153,3 +165,11 @@ config PSTORE_RAM "ramoops.ko". For more information, see Documentation/admin-guide/ramoops.rst. +config PSTORE_BLACKBOX_STACK_SIZE + int "Default stack size for BlackBox" if EXPERT + depends on PSTORE + depends on PSTORE_BLACKBOX + default 1024 + help + Defines default size of pstore stack size for blackbox. + Can be enlarged if needed. not recommended to shrink it. \ No newline at end of file diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 7062ea4bc57c..18e681cfcfd0 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -41,4 +41,7 @@ extern void pstore_record_init(struct pstore_record *record, int __init pstore_init_fs(void); void __exit pstore_exit_fs(void); +#ifdef CONFIG_PSTORE_BLACKBOX +extern bool pstore_ready; /* flag which pstore_blk is ready */ +#endif #endif diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 2197bf68f278..55e143f954d6 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -28,6 +28,10 @@ #include #include #include +#ifdef CONFIG_PSTORE_BLACKBOX +#include +#include +#endif #if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS) #include #endif @@ -374,9 +378,117 @@ void pstore_record_init(struct pstore_record *record, } /* - * callback from kmsg_dump. (s2,l2) has the most recently - * written bytes, older bytes are in (s1,l1). Save as much - * as we can from the end of the buffer. + * Store the customised fault log + */ +#ifdef CONFIG_PSTORE_BLACKBOX +#define PSTORE_FLAG "PSTORE" +#define CALLSTACK_MAX_ENTRIES 20 +static void dump_stacktrace(char *pbuf, size_t buf_size, bool is_panic) +{ + int i; + size_t stack_len = 0; + size_t com_len = 0; + unsigned long entries[CALLSTACK_MAX_ENTRIES]; + unsigned int nr_entries; + char tmp_buf[ERROR_DESC_MAX_LEN]; + bool find_panic = false; + + if (unlikely(!pbuf || !buf_size)) + return; + + memset(pbuf, 0, buf_size); + memset(tmp_buf, 0, sizeof(tmp_buf)); + nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 0); + com_len = scnprintf(pbuf, buf_size, "Comm:%s,CPU:%d,Stack:", + current->comm, raw_smp_processor_id()); + for (i = 0; i < nr_entries; i++) { + if (stack_len >= sizeof(tmp_buf)) { + tmp_buf[sizeof(tmp_buf) - 1] = '\0'; + break; + } + stack_len += scnprintf(tmp_buf + stack_len, sizeof(tmp_buf) - stack_len, + "%pS-", (void *)entries[i]); + if (!find_panic && is_panic) { + if (strncmp(tmp_buf, "panic", strlen("panic")) == 0) + find_panic = true; + else + (void)memset(tmp_buf, 0, sizeof(tmp_buf)); + } + } + if (com_len >= buf_size) + return; + stack_len = min(buf_size - com_len, strlen(tmp_buf)); + memcpy(pbuf + com_len, tmp_buf, stack_len); + *(pbuf + buf_size - 1) = '\0'; +} + +void pstore_blackbox_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) +{ + struct fault_log_info *pfault_log_info; + struct pstore_record record; + size_t dst_size; + const char *why; + char *dst; + int ret; + +#if defined(CONFIG_PSTORE_BLK) || defined(CONFIG_PSTORE_RAM) + if (!pstore_ready) + return; +#endif + + why = kmsg_dump_reason_str(reason); + + if (down_trylock(&psinfo->buf_lock)) { + /* Failed to acquire lock: give up if we cannot wait. */ + if (pstore_cannot_wait(reason)) { + pr_err("dump skipped in %s path: may corrupt error record\n", + in_nmi() ? "NMI" : why); + return; + } + if (down_interruptible(&psinfo->buf_lock)) { + pr_err("could not grab semaphore?!\n"); + return; + } + } + + pfault_log_info = (struct fault_log_info *)psinfo->buf; + + memset(pfault_log_info, 0, sizeof(*pfault_log_info)); + + pstore_record_init(&record, psinfo); + + record.type = PSTORE_TYPE_BLACKBOX; + record.reason = reason; + + memcpy(pfault_log_info->flag, LOG_FLAG, strlen(LOG_FLAG)); + strncpy(pfault_log_info->info.event, why, + min(strlen(why), sizeof(pfault_log_info->info.event) - 1)); + strncpy(pfault_log_info->info.module, PSTORE_FLAG, + min(strlen(PSTORE_FLAG), sizeof(pfault_log_info->info.module) - 1)); + get_timestamp(pfault_log_info->info.error_time, TIMESTAMP_MAX_LEN); + dump_stacktrace(pfault_log_info->info.error_desc, sizeof(pfault_log_info->info.error_desc), false); + + record.buf = psinfo->buf; + + dst = psinfo->buf; + dst_size = psinfo->bufsize; + + dst_size -= sizeof(struct fault_log_info); + + (void)kmsg_dump_get_buffer(dumper, true, dst + sizeof(struct fault_log_info), dst_size, + &(pfault_log_info->len)); + + record.size = sizeof(struct fault_log_info) + pfault_log_info->len; + ret = psinfo->write(&record); + + up(&psinfo->buf_lock); +} +EXPORT_SYMBOL_GPL(pstore_blackbox_dump); +#endif + +/* + * callback from kmsg_dump. Save as much as we can (up to kmsg_bytes) from the + * end of the buffer. */ static void pstore_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index bafbab2dd039..6b34db711bb5 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -35,6 +35,7 @@ #include #include #include +#include "internal.h" #define RAMOOPS_KERNMSG_HDR "====" #define MIN_MEM_SIZE 4096UL @@ -56,6 +57,14 @@ static ulong ramoops_pmsg_size = MIN_MEM_SIZE; module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400); MODULE_PARM_DESC(pmsg_size, "size of user space message log"); +static ulong ramoops_blackbox_size = MIN_MEM_SIZE; +module_param_named(blackbox_size, ramoops_blackbox_size, ulong, 0400); +MODULE_PARM_DESC(blackbox_size, "size of blackbox log"); +#if IS_ENABLED(CONFIG_PSTORE_BLACKBOX) +bool pstore_ready; +#endif + + static unsigned long long mem_address; module_param_hw(mem_address, ullong, other, 0400); MODULE_PARM_DESC(mem_address, @@ -88,6 +97,7 @@ struct ramoops_context { struct persistent_ram_zone *cprz; /* Console zone */ struct persistent_ram_zone **fprzs; /* Ftrace zones */ struct persistent_ram_zone *mprz; /* PMSG zone */ + struct persistent_ram_zone *bprz; /* BLACKBOX zone */ phys_addr_t phys_addr; unsigned long size; unsigned int memtype; @@ -95,7 +105,7 @@ struct ramoops_context { size_t console_size; size_t ftrace_size; size_t pmsg_size; - int dump_oops; + size_t blackbox_size; u32 flags; struct persistent_ram_ecc_info ecc_info; unsigned int max_dump_cnt; @@ -106,6 +116,7 @@ struct ramoops_context { unsigned int max_ftrace_cnt; unsigned int ftrace_read_cnt; unsigned int pmsg_read_cnt; + unsigned int blackbox_read_cnt; struct pstore_info pstore; }; @@ -120,6 +131,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) cxt->console_read_cnt = 0; cxt->ftrace_read_cnt = 0; cxt->pmsg_read_cnt = 0; + cxt->blackbox_read_cnt = 0; return 0; } @@ -283,6 +295,10 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record) prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, 1, &record->id, &record->type, PSTORE_TYPE_PMSG, 0); + if (!prz_ok(prz) && !cxt->blackbox_read_cnt++) + prz = ramoops_get_next_prz(&cxt->bprz, &cxt->blackbox_read_cnt, + 1, &record->id, &record->type, + PSTORE_TYPE_BLACKBOX, 0); /* ftrace is last since it may want to dynamically allocate memory. */ if (!prz_ok(prz)) { @@ -406,6 +422,11 @@ static int notrace ramoops_pstore_write(struct pstore_record *record) } else if (record->type == PSTORE_TYPE_PMSG) { pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__); return -EINVAL; + } else if (record->type == PSTORE_TYPE_BLACKBOX) { + if (!cxt->bprz) + return -ENOMEM; + persistent_ram_write(cxt->bprz, record->buf, record->size); + return 0; } if (record->type != PSTORE_TYPE_DMESG) @@ -496,6 +517,9 @@ static int ramoops_pstore_erase(struct pstore_record *record) case PSTORE_TYPE_PMSG: prz = cxt->mprz; break; + case PSTORE_TYPE_BLACKBOX: + prz = cxt->bprz; + break; default: return -EINVAL; } @@ -714,10 +738,32 @@ static int ramoops_parse_dt(struct platform_device *pdev, parse_size("console-size", pdata->console_size); parse_size("ftrace-size", pdata->ftrace_size); parse_size("pmsg-size", pdata->pmsg_size); + parse_size("blackbox-size", pdata->blackbox_size); parse_size("ecc-size", pdata->ecc_info.ecc_size); parse_size("flags", pdata->flags); #undef parse_size + /* + * Some old Chromebooks relied on the kernel setting the + * console_size and pmsg_size to the record size since that's + * what the downstream kernel did. These same Chromebooks had + * "ramoops" straight under the root node which isn't + * according to the current upstream bindings (though it was + * arguably acceptable under a prior version of the bindings). + * Let's make those old Chromebooks work by detecting that + * we're not a child of "reserved-memory" and mimicking the + * expected behavior. + */ + parent_node = of_get_parent(of_node); + if (!of_node_name_eq(parent_node, "reserved-memory") && + !pdata->console_size && !pdata->ftrace_size && + !pdata->pmsg_size && !pdata->ecc_info.ecc_size && + !pdata->blackbox_size) { + pdata->console_size = pdata->record_size; + pdata->pmsg_size = pdata->record_size; + pdata->blackbox_size = pdata->record_size; + } + of_node_put(parent_node); return 0; } @@ -757,7 +803,7 @@ static int ramoops_probe(struct platform_device *pdev) } if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && - !pdata->ftrace_size && !pdata->pmsg_size)) { + !pdata->ftrace_size && !pdata->pmsg_size && !pdata->blackbox_size)) { pr_err("The memory size and the record/console size must be " "non-zero\n"); goto fail_out; @@ -771,6 +817,8 @@ static int ramoops_probe(struct platform_device *pdev) pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size)) pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size); + if (pdata->blackbox_size && !is_power_of_2(pdata->blackbox_size)) + pdata->blackbox_size = rounddown_pow_of_two(pdata->blackbox_size); cxt->size = pdata->mem_size; cxt->phys_addr = pdata->mem_address; @@ -780,14 +828,21 @@ static int ramoops_probe(struct platform_device *pdev) cxt->ftrace_size = pdata->ftrace_size; cxt->pmsg_size = pdata->pmsg_size; cxt->dump_oops = pdata->dump_oops; + cxt->blackbox_size = pdata->blackbox_size; cxt->flags = pdata->flags; cxt->ecc_info = pdata->ecc_info; paddr = cxt->phys_addr; dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size - - cxt->pmsg_size; - err = ramoops_init_przs("dump", dev, cxt, &cxt->dprzs, &paddr, + - cxt->pmsg_size - cxt->blackbox_size; + + err = ramoops_init_prz("blackbox", dev, cxt, &cxt->bprz, &paddr, + cxt->blackbox_size, 0); + if (err) + goto fail_init_bprz; + + err = ramoops_init_przs("dmesg", dev, cxt, &cxt->dprzs, &paddr, dump_mem_sz, cxt->record_size, &cxt->max_dump_cnt, 0, 0); if (err) @@ -830,6 +885,8 @@ static int ramoops_probe(struct platform_device *pdev) cxt->pstore.flags |= PSTORE_FLAGS_FTRACE; if (cxt->pmsg_size) cxt->pstore.flags |= PSTORE_FLAGS_PMSG; + if (cxt->blackbox_size) + cxt->pstore.flags |= PSTORE_FLAGS_BLACKBOX; /* * Since bufsize is only used for dmesg crash dumps, it @@ -863,6 +920,7 @@ static int ramoops_probe(struct platform_device *pdev) ramoops_console_size = pdata->console_size; ramoops_pmsg_size = pdata->pmsg_size; ramoops_ftrace_size = pdata->ftrace_size; + ramoops_blackbox_size = pdata->blackbox_size; pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n", cxt->size, (unsigned long long)cxt->phys_addr, @@ -878,6 +936,8 @@ static int ramoops_probe(struct platform_device *pdev) fail_init_mprz: fail_init_fprz: persistent_ram_free(cxt->cprz); +fail_init_bprz: + persistent_ram_free(cxt->bprz); fail_init_cprz: ramoops_free_przs(cxt); fail_out: @@ -895,6 +955,7 @@ static int ramoops_remove(struct platform_device *pdev) persistent_ram_free(cxt->mprz); persistent_ram_free(cxt->cprz); + persistent_ram_free(cxt->bprz); ramoops_free_przs(cxt); return 0; @@ -948,6 +1009,7 @@ static void __init ramoops_register_dummy(void) dummy_data->console_size = ramoops_console_size; dummy_data->ftrace_size = ramoops_ftrace_size; dummy_data->pmsg_size = ramoops_pmsg_size; + dummy_data->blackbox_size = ramoops_blackbox_size; dummy_data->dump_oops = dump_oops; dummy_data->flags = RAMOOPS_FLAG_FTRACE_PER_CPU; @@ -975,6 +1037,10 @@ static int __init ramoops_init(void) ret = platform_driver_register(&ramoops_driver); if (ret != 0) ramoops_unregister_dummy(); +#if IS_ENABLED(CONFIG_PSTORE_BLACKBOX) + if (!ret) + pstore_ready = true; +#endif return ret; } diff --git a/include/linux/blackbox.h b/include/linux/blackbox.h new file mode 100644 index 000000000000..e6d963264025 --- /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..973a9214184d --- /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 */ diff --git a/include/linux/pstore.h b/include/linux/pstore.h index de9093d6e660..10a7c2d7f6f1 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -44,6 +44,7 @@ enum pstore_type_id { PSTORE_TYPE_PPC_COMMON = 6, PSTORE_TYPE_PMSG = 7, PSTORE_TYPE_PPC_OPAL = 8, + PSTORE_TYPE_BLACKBOX = 9, PSTORE_TYPE_UNKNOWN = 255 }; @@ -196,6 +197,7 @@ struct pstore_info { #define PSTORE_FLAGS_CONSOLE (1 << 1) #define PSTORE_FLAGS_FTRACE (1 << 2) #define PSTORE_FLAGS_PMSG (1 << 3) +#define PSTORE_FLAGS_BLACKBOX (1 << 4) extern int pstore_register(struct pstore_info *); extern void pstore_unregister(struct pstore_info *); @@ -276,4 +278,9 @@ pstore_ftrace_write_timestamp(struct pstore_ftrace_record *rec, u64 val) } #endif +#ifdef CONFIG_PSTORE_BLACKBOX +extern void pstore_blackbox_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason); +#endif + #endif /*_LINUX_PSTORE_H*/ diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index e6d226464838..9b786b11692d 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -97,6 +97,7 @@ struct ramoops_platform_data { unsigned long console_size; unsigned long ftrace_size; unsigned long pmsg_size; + unsigned long blackbox_size; int dump_oops; u32 flags; struct persistent_ram_ecc_info ecc_info; -- Gitee