diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ada110f59cc33336858a8877fe270b069dd350a5
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 830a4b656fe8f7a3b3b1da423efea5a8af4b01bb..f54802de5289ff9345bc3f6ae5d6bc54bebc409e 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 84216c89fe12417fa4f9bfabdce6aaac6abee266..3f8ae47086a10157907d6a52380fcce7e945373d 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 0000000000000000000000000000000000000000..0e985823c2e83539023f66d06041de502dbb7f0a
--- /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 0000000000000000000000000000000000000000..9befa81a176e41923da418450d3cb7cc27f99c00
--- /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 0000000000000000000000000000000000000000..bfaba7f746968533f021318acf0e1a98f47b7f4a
--- /dev/null
+++ b/drivers/staging/blackbox/blackbox_common.c
@@ -0,0 +1,277 @@
+// 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
+
+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 0000000000000000000000000000000000000000..0f919195f045425c4289e825fbee3e93e1b69c05
--- /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);
+ 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 0000000000000000000000000000000000000000..635e3aa036aba14ae09ff4c4625a20a737c2bc0e
--- /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 503086f7f7c1b1a2890a2db3fa3ab4bacea37051..63d70cc3a46150603cba6613b04ed5eabe234f03 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 7062ea4bc57c5b31cba5c19815f343e387dafa0c..18e681cfcfd01d4e56291a49fc0635f17d3e6749 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 2197bf68f2786534ad01bb8586b6398d69d2e1f7..55e143f954d68cb9d76142f90bd6dcaabb302fe3 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 bafbab2dd039245e4d02a9fd574201bb590e0d07..6b34db711bb59755b7747d62d9693ba661d936cb 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 0000000000000000000000000000000000000000..e6d96326402512aa654449848f7ad8e5fbd7b2ce
--- /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 0000000000000000000000000000000000000000..973a9214184dc69b07eb6e6e49e7b5ac4a1a4c8e
--- /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 0000000000000000000000000000000000000000..52d67523d71c8b916d0c5511b605277891c5c6ac
--- /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 de9093d6e6609a1431aed49322c1c12a5e2940a1..10a7c2d7f6f1e67834de62b73b55e0ef73e5d386 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 e6d226464838a1e2b67c77145207ae5e27cb589b..9b786b11692d868e7c26d7260ed91555d2e3ab92 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;