diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 4dbdc87e809ce0a06c967d56838aa1f26e6b38a7..181015757ec3e49bc3ef80e20564e9ac51661539 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -122,4 +122,5 @@ 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 b33dfbdcce7e32c754edc97a73e24fea8afa7f52..a8e657b6d933806ff5e05557d5a5049fdfeb7846 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -51,3 +51,4 @@ obj-$(CONFIG_WFX) += wfx/ obj-y += hikey9xx/ obj-$(CONFIG_HILOG) += hilog/ obj-$(CONFIG_HIEVENT) += hievent/ +obj-$(CONFIG_BLACKBOX) += blackbox/ diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 8efe60487b486be7f3861090147ba554d170eaaa..75f8c3e6932a258817c2cac972613b10b0ab0a42 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -138,6 +138,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 @@ -165,7 +177,7 @@ config PSTORE_BLK tristate "Log panic/oops to a block device" depends on PSTORE depends on BLOCK - depends on BROKEN + depends on BLACKBOX_USE_PSTORE_BLK_DEBUG || BROKEN select PSTORE_ZONE default n help @@ -263,3 +275,24 @@ config PSTORE_BLK_FTRACE_SIZE NOTE that, both Kconfig and module parameters can configure pstore/blk, but module parameters have priority over Kconfig. + +config PSTORE_BLK_BLACKBOX_SIZE + int "Size in Kbytes of fault log for BlackBox to store" + depends on PSTORE_BLK + depends on PSTORE_BLACKBOX + default 64 + help + This just sets size of fault log (blackbox_size) for pstore/blk. + The size is in KB and must be a multiple of 4 + + NOTE that, both Kconfig and module parameters can configure + pstore/blk, but module parameters have priority over Kconfig. + +config PSTORE_BLK_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. diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 7fb219042f13d149b10502f2969fb7603f27e62a..4442d440a228d7e4283401e6438062b48cbfabfe 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -50,4 +50,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 b1ebf7b61732c396f731cb988f5e07addee065b8..1ca52b54bc08dbd493f1ba282b2f033c6070438a 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -16,6 +16,10 @@ #include #include #include +#ifdef CONFIG_PSTORE_BLACKBOX +#include +#include +#endif #if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS) #include #endif @@ -58,6 +62,7 @@ static const char * const pstore_type_names[] = { "powerpc-common", "pmsg", "powerpc-opal", + "blackbox", }; static int pstore_new_entry; @@ -378,6 +383,111 @@ void pstore_record_init(struct pstore_record *record, record->time = ns_to_timespec64(ktime_get_real_fast_ns()); } +/* + * 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_pbuf, 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 (int 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("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 = PSORE_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. diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index ca6d8a86728566520c896cda6b7b7986c3e35e36..e02e9bb858c23049334e4e5b70d79d2c99db0ff8 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -6,6 +6,8 @@ * Copyright (C) 2011 Kees Cook */ +#include +#include #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include @@ -43,6 +45,13 @@ 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_PARAM_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, @@ -80,6 +89,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; @@ -87,6 +97,7 @@ struct ramoops_context { size_t console_size; size_t ftrace_size; size_t pmsg_size; + size_t blackbox_size; u32 flags; struct persistent_ram_ecc_info ecc_info; unsigned int max_dump_cnt; @@ -97,6 +108,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; }; @@ -110,6 +122,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; } @@ -213,6 +226,9 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record) if (!prz_ok(prz) && !cxt->pmsg_read_cnt++) prz = ramoops_get_next_prz(&cxt->mprz, 0 /* single */, record); + if (!prz_ok(prz) && !cxt->blackbox_read_cnt++) + prz = ramoops_get_next_prz(&cxt->bprz, 0 /* single */, record); + /* ftrace is last since it may want to dynamically allocate memory. */ if (!prz_ok(prz)) { if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU) && @@ -334,6 +350,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 (!ctx->bprz) + return -ENOMEM; + persistent_ram_write(cxt->bprz, record->buf, record->size); + return 0; } if (record->type != PSTORE_TYPE_DMESG) @@ -425,6 +446,9 @@ static int ramoops_pstore_erase(struct pstore_record *record) case PSTORE_TYPE_PMSG: prz = cxt->mprz; break; + case PSTORE_TYPE_BLACKBOX: + prz = cxt->mprz; + break; default: return -EINVAL; } @@ -670,6 +694,7 @@ static int ramoops_parse_dt(struct platform_device *pdev, parse_u32("console-size", pdata->console_size, 0); parse_u32("ftrace-size", pdata->ftrace_size, 0); parse_u32("pmsg-size", pdata->pmsg_size, 0); + parse_u32("blackbox-size", pdata->blackbox_size, 0); parse_u32("ecc-size", pdata->ecc_info.ecc_size, 0); parse_u32("flags", pdata->flags, 0); parse_u32("max-reason", pdata->max_reason, pdata->max_reason); @@ -690,9 +715,11 @@ static int ramoops_parse_dt(struct platform_device *pdev, 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->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); @@ -734,7 +761,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; @@ -748,6 +775,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; @@ -756,13 +785,23 @@ static int ramoops_probe(struct platform_device *pdev) cxt->console_size = pdata->console_size; cxt->ftrace_size = pdata->ftrace_size; cxt->pmsg_size = pdata->pmsg_size; + 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; + - cxt->pmsg_size - cxt->blackbox_size; + err = ramoops_init_przs("blackbox", dev, cxt, &cxt->dprzs, &paddr, + cxt->blackbox_size, 0); + if (err) + goto fail_init_bprz; +#if IS_ENABLED(CONFIG_PSTORE_BLACKBOX) + else + pstore_ready = true; +#endif + err = ramoops_init_przs("dmesg", dev, cxt, &cxt->dprzs, &paddr, dump_mem_sz, cxt->record_size, &cxt->max_dump_cnt, 0, 0); @@ -808,6 +847,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 @@ -841,6 +882,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("using 0x%lx@0x%llx, ecc: %d\n", cxt->size, (unsigned long long)cxt->phys_addr, @@ -856,6 +898,8 @@ fail_clear: 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: @@ -873,6 +917,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; @@ -920,6 +965,7 @@ static void __init ramoops_register_dummy(void) pdata.console_size = ramoops_console_size; pdata.ftrace_size = ramoops_ftrace_size; pdata.pmsg_size = ramoops_pmsg_size; + pdata.blackbox_size = ramoops_blackbox_size; /* If "max_reason" is set, its value has priority over "dump_oops". */ if (ramoops_max_reason >= 0) pdata.max_reason = ramoops_max_reason; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index eb93a54cff31fa21d49b77012f14888b51e01bca..4551e94c8a15188d9093e19d20c19ac6083226fe 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -39,6 +39,8 @@ enum pstore_type_id { PSTORE_TYPE_PMSG = 7, PSTORE_TYPE_PPC_OPAL = 8, + PSTORE_TYPE_BLACKBOX = 9, + /* End of the list */ PSTORE_TYPE_MAX }; @@ -202,6 +204,7 @@ struct pstore_info { #define PSTORE_FLAGS_CONSOLE BIT(1) #define PSTORE_FLAGS_FTRACE BIT(2) #define PSTORE_FLAGS_PMSG BIT(3) +#define PSTORE_FLAGS_BLACKBOX BIT(4) extern int pstore_register(struct pstore_info *); extern void pstore_unregister(struct pstore_info *); @@ -282,4 +285,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, + ebun kmsg_dump_reason reason) +#endif + #endif /*_LINUX_PSTORE_H*/ diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 9f16afec7290d6e9c20e9c03192c19488162afca..72fa93bc539b7f74b26efa2739746e6815c3f25e 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -133,6 +133,7 @@ struct ramoops_platform_data { unsigned long console_size; unsigned long ftrace_size; unsigned long pmsg_size; + unsigned long blackbox_size; int max_reason; u32 flags; struct persistent_ram_ecc_info ecc_info;