145051539SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2ca01d6ddSTony Luck /* 3ca01d6ddSTony Luck * Persistent Storage - platform driver interface parts. 4ca01d6ddSTony Luck * 5f29e5956SAnton Vorontsov * Copyright (C) 2007-2008 Google, Inc. 6ca01d6ddSTony Luck * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com> 7ca01d6ddSTony Luck */ 8ca01d6ddSTony Luck 9ef748853SFabian Frederick #define pr_fmt(fmt) "pstore: " fmt 10ef748853SFabian Frederick 11ca01d6ddSTony Luck #include <linux/atomic.h> 12ca01d6ddSTony Luck #include <linux/types.h> 13ca01d6ddSTony Luck #include <linux/errno.h> 14ca01d6ddSTony Luck #include <linux/init.h> 15ca01d6ddSTony Luck #include <linux/kmsg_dump.h> 16f29e5956SAnton Vorontsov #include <linux/console.h> 17ca01d6ddSTony Luck #include <linux/module.h> 18ca01d6ddSTony Luck #include <linux/pstore.h> 19cb3bee03SGeliang Tang #include <linux/crypto.h> 20ca01d6ddSTony Luck #include <linux/string.h> 216dda9266SLuck, Tony #include <linux/timer.h> 22ca01d6ddSTony Luck #include <linux/slab.h> 23ca01d6ddSTony Luck #include <linux/uaccess.h> 24a3f5f075SAnton Vorontsov #include <linux/jiffies.h> 256dda9266SLuck, Tony #include <linux/workqueue.h> 26ca01d6ddSTony Luck 27ca01d6ddSTony Luck #include "internal.h" 28ca01d6ddSTony Luck 29ca01d6ddSTony Luck /* 306dda9266SLuck, Tony * We defer making "oops" entries appear in pstore - see 316dda9266SLuck, Tony * whether the system is actually still running well enough 326dda9266SLuck, Tony * to let someone see the entry 336dda9266SLuck, Tony */ 34521f7288SAnton Vorontsov static int pstore_update_ms = -1; 35a3f5f075SAnton Vorontsov module_param_named(update_ms, pstore_update_ms, int, 0600); 36a3f5f075SAnton Vorontsov MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content " 37521f7288SAnton Vorontsov "(default is -1, which means runtime updates are disabled; " 3878c83c82SKees Cook "enabling this option may not be safe; it may lead to further " 39521f7288SAnton Vorontsov "corruption on Oopses)"); 406dda9266SLuck, Tony 41f0f23e54SJoel Fernandes (Google) /* Names should be in the same order as the enum pstore_type_id */ 42f0f23e54SJoel Fernandes (Google) static const char * const pstore_type_names[] = { 43f0f23e54SJoel Fernandes (Google) "dmesg", 44f0f23e54SJoel Fernandes (Google) "mce", 45f0f23e54SJoel Fernandes (Google) "console", 46f0f23e54SJoel Fernandes (Google) "ftrace", 47f0f23e54SJoel Fernandes (Google) "rtas", 48f0f23e54SJoel Fernandes (Google) "powerpc-ofw", 49f0f23e54SJoel Fernandes (Google) "powerpc-common", 50f0f23e54SJoel Fernandes (Google) "pmsg", 51f0f23e54SJoel Fernandes (Google) "powerpc-opal", 52f0f23e54SJoel Fernandes (Google) }; 53f0f23e54SJoel Fernandes (Google) 546dda9266SLuck, Tony static int pstore_new_entry; 556dda9266SLuck, Tony 5624ed960aSKees Cook static void pstore_timefunc(struct timer_list *); 571d27e3e2SKees Cook static DEFINE_TIMER(pstore_timer, pstore_timefunc); 586dda9266SLuck, Tony 596dda9266SLuck, Tony static void pstore_dowork(struct work_struct *); 606dda9266SLuck, Tony static DECLARE_WORK(pstore_work, pstore_dowork); 616dda9266SLuck, Tony 626dda9266SLuck, Tony /* 636248a066SKees Cook * psinfo_lock protects "psinfo" during calls to 646248a066SKees Cook * pstore_register(), pstore_unregister(), and 656248a066SKees Cook * the filesystem mount/unmount routines. 66ca01d6ddSTony Luck */ 67cab12fd0SKees Cook static DEFINE_MUTEX(psinfo_lock); 68060287b8SAnton Vorontsov struct pstore_info *psinfo; 69ca01d6ddSTony Luck 70dee28e72SMatthew Garrett static char *backend; 71d973f7d8SKees Cook module_param(backend, charp, 0444); 72d973f7d8SKees Cook MODULE_PARM_DESC(backend, "specific backend to use"); 73d973f7d8SKees Cook 74fe1d4758SKees Cook static char *compress = 75fe1d4758SKees Cook #ifdef CONFIG_PSTORE_COMPRESS_DEFAULT 76fe1d4758SKees Cook CONFIG_PSTORE_COMPRESS_DEFAULT; 77fe1d4758SKees Cook #else 78fe1d4758SKees Cook NULL; 79fe1d4758SKees Cook #endif 80d973f7d8SKees Cook module_param(compress, charp, 0444); 81d973f7d8SKees Cook MODULE_PARM_DESC(compress, "compression to use"); 82dee28e72SMatthew Garrett 838f5de3fdSGuilherme G. Piccoli /* How much of the kernel log to snapshot */ 848f5de3fdSGuilherme G. Piccoli unsigned long kmsg_bytes = CONFIG_PSTORE_DEFAULT_KMSG_BYTES; 858f5de3fdSGuilherme G. Piccoli module_param(kmsg_bytes, ulong, 0444); 868f5de3fdSGuilherme G. Piccoli MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)"); 878f5de3fdSGuilherme G. Piccoli 88b0aad7a9SAruna Balakrishnaiah /* Compression parameters */ 8940158dbfSGuilherme G. Piccoli static struct crypto_comp *tfm; 908cfc8ddcSGeliang Tang 91b0aad7a9SAruna Balakrishnaiah static char *big_oops_buf; 92b0aad7a9SAruna Balakrishnaiah 93366f7e7aSLuck, Tony void pstore_set_kmsg_bytes(int bytes) 94ca01d6ddSTony Luck { 95366f7e7aSLuck, Tony kmsg_bytes = bytes; 96ca01d6ddSTony Luck } 97ca01d6ddSTony Luck 98ca01d6ddSTony Luck /* Tag each group of saved records with a sequence number */ 99ca01d6ddSTony Luck static int oopscount; 100ca01d6ddSTony Luck 101f0f23e54SJoel Fernandes (Google) const char *pstore_type_to_name(enum pstore_type_id type) 102f0f23e54SJoel Fernandes (Google) { 103f0f23e54SJoel Fernandes (Google) BUILD_BUG_ON(ARRAY_SIZE(pstore_type_names) != PSTORE_TYPE_MAX); 104f0f23e54SJoel Fernandes (Google) 105f0f23e54SJoel Fernandes (Google) if (WARN_ON_ONCE(type >= PSTORE_TYPE_MAX)) 106f0f23e54SJoel Fernandes (Google) return "unknown"; 107f0f23e54SJoel Fernandes (Google) 108f0f23e54SJoel Fernandes (Google) return pstore_type_names[type]; 109f0f23e54SJoel Fernandes (Google) } 110f0f23e54SJoel Fernandes (Google) EXPORT_SYMBOL_GPL(pstore_type_to_name); 111f0f23e54SJoel Fernandes (Google) 112f0f23e54SJoel Fernandes (Google) enum pstore_type_id pstore_name_to_type(const char *name) 113f0f23e54SJoel Fernandes (Google) { 114f0f23e54SJoel Fernandes (Google) int i; 115f0f23e54SJoel Fernandes (Google) 116f0f23e54SJoel Fernandes (Google) for (i = 0; i < PSTORE_TYPE_MAX; i++) { 117f0f23e54SJoel Fernandes (Google) if (!strcmp(pstore_type_names[i], name)) 118f0f23e54SJoel Fernandes (Google) return i; 119f0f23e54SJoel Fernandes (Google) } 120f0f23e54SJoel Fernandes (Google) 121f0f23e54SJoel Fernandes (Google) return PSTORE_TYPE_MAX; 122f0f23e54SJoel Fernandes (Google) } 123f0f23e54SJoel Fernandes (Google) EXPORT_SYMBOL_GPL(pstore_name_to_type); 124f0f23e54SJoel Fernandes (Google) 12578c83c82SKees Cook static void pstore_timer_kick(void) 12678c83c82SKees Cook { 12778c83c82SKees Cook if (pstore_update_ms < 0) 12878c83c82SKees Cook return; 12978c83c82SKees Cook 13078c83c82SKees Cook mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms)); 13178c83c82SKees Cook } 13278c83c82SKees Cook 1338126b1c7SJann Horn static bool pstore_cannot_block_path(enum kmsg_dump_reason reason) 134ea84b580SKees Cook { 1358126b1c7SJann Horn /* 1368126b1c7SJann Horn * In case of NMI path, pstore shouldn't be blocked 1378126b1c7SJann Horn * regardless of reason. 1388126b1c7SJann Horn */ 1399f244e9cSSeiji Aguchi if (in_nmi()) 1409f244e9cSSeiji Aguchi return true; 1419f244e9cSSeiji Aguchi 1429f244e9cSSeiji Aguchi switch (reason) { 1439f244e9cSSeiji Aguchi /* In panic case, other cpus are stopped by smp_send_stop(). */ 1449f244e9cSSeiji Aguchi case KMSG_DUMP_PANIC: 1458126b1c7SJann Horn /* 1468126b1c7SJann Horn * Emergency restart shouldn't be blocked by spinning on 1478126b1c7SJann Horn * pstore_info::buf_lock. 1488126b1c7SJann Horn */ 1499f244e9cSSeiji Aguchi case KMSG_DUMP_EMERG: 1509f244e9cSSeiji Aguchi return true; 1519f244e9cSSeiji Aguchi default: 1529f244e9cSSeiji Aguchi return false; 1539f244e9cSSeiji Aguchi } 1549f244e9cSSeiji Aguchi } 1559f244e9cSSeiji Aguchi 1568cfc8ddcSGeliang Tang static int pstore_compress(const void *in, void *out, 157cb3bee03SGeliang Tang unsigned int inlen, unsigned int outlen) 1588cfc8ddcSGeliang Tang { 159cb3bee03SGeliang Tang int ret; 160cb3bee03SGeliang Tang 16119d8e914SJiri Bohac if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS)) 162fd49e032SMatteo Croce return -EINVAL; 163fd49e032SMatteo Croce 16440158dbfSGuilherme G. Piccoli ret = crypto_comp_compress(tfm, in, inlen, out, &outlen); 165cb3bee03SGeliang Tang if (ret) { 166cb3bee03SGeliang Tang pr_err("crypto_comp_compress failed, ret = %d!\n", ret); 167cb3bee03SGeliang Tang return ret; 1688cfc8ddcSGeliang Tang } 1698cfc8ddcSGeliang Tang 170cb3bee03SGeliang Tang return outlen; 171cb3bee03SGeliang Tang } 172cb3bee03SGeliang Tang 1738cfc8ddcSGeliang Tang static void allocate_buf_for_compression(void) 1748cfc8ddcSGeliang Tang { 17540158dbfSGuilherme G. Piccoli struct crypto_comp *ctx; 17695047b05SKees Cook char *buf; 17795047b05SKees Cook 17895047b05SKees Cook /* Skip if not built-in or compression backend not selected yet. */ 179*1756ddeaSArd Biesheuvel if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !compress) 180cb3bee03SGeliang Tang return; 181cb3bee03SGeliang Tang 18295047b05SKees Cook /* Skip if no pstore backend yet or compression init already done. */ 18395047b05SKees Cook if (!psinfo || tfm) 18495047b05SKees Cook return; 18595047b05SKees Cook 186*1756ddeaSArd Biesheuvel if (!crypto_has_comp(compress, 0, 0)) { 187*1756ddeaSArd Biesheuvel pr_err("Unknown compression: %s\n", compress); 188cb3bee03SGeliang Tang return; 189cb3bee03SGeliang Tang } 190cb3bee03SGeliang Tang 191*1756ddeaSArd Biesheuvel /* 192*1756ddeaSArd Biesheuvel * The compression buffer only needs to be as large as the maximum 193*1756ddeaSArd Biesheuvel * uncompressed record size, since any record that would be expanded by 194*1756ddeaSArd Biesheuvel * compression is just stored uncompressed. 195*1756ddeaSArd Biesheuvel */ 196*1756ddeaSArd Biesheuvel buf = kmalloc(psinfo->bufsize, GFP_KERNEL); 19795047b05SKees Cook if (!buf) { 198*1756ddeaSArd Biesheuvel pr_err("Failed %zu byte compression buffer allocation for: %s\n", 199*1756ddeaSArd Biesheuvel psinfo->bufsize, compress); 200cb3bee03SGeliang Tang return; 2018cfc8ddcSGeliang Tang } 20295047b05SKees Cook 203*1756ddeaSArd Biesheuvel ctx = crypto_alloc_comp(compress, 0, 0); 20440158dbfSGuilherme G. Piccoli if (IS_ERR_OR_NULL(ctx)) { 20595047b05SKees Cook kfree(buf); 206*1756ddeaSArd Biesheuvel pr_err("crypto_alloc_comp('%s') failed: %ld\n", compress, 20740158dbfSGuilherme G. Piccoli PTR_ERR(ctx)); 20895047b05SKees Cook return; 20995047b05SKees Cook } 21095047b05SKees Cook 21195047b05SKees Cook /* A non-NULL big_oops_buf indicates compression is available. */ 21240158dbfSGuilherme G. Piccoli tfm = ctx; 21395047b05SKees Cook big_oops_buf = buf; 21495047b05SKees Cook 215*1756ddeaSArd Biesheuvel pr_info("Using crash dump compression: %s\n", compress); 2168cfc8ddcSGeliang Tang } 2178cfc8ddcSGeliang Tang 2188cfc8ddcSGeliang Tang static void free_buf_for_compression(void) 2198cfc8ddcSGeliang Tang { 220a9fb94a9SPi-Hsun Shih if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && tfm) { 22140158dbfSGuilherme G. Piccoli crypto_free_comp(tfm); 222a9fb94a9SPi-Hsun Shih tfm = NULL; 223a9fb94a9SPi-Hsun Shih } 224cb3bee03SGeliang Tang kfree(big_oops_buf); 225cb3bee03SGeliang Tang big_oops_buf = NULL; 226b0aad7a9SAruna Balakrishnaiah } 227b0aad7a9SAruna Balakrishnaiah 228e581ca81SKees Cook void pstore_record_init(struct pstore_record *record, 229e581ca81SKees Cook struct pstore_info *psinfo) 230e581ca81SKees Cook { 231e581ca81SKees Cook memset(record, 0, sizeof(*record)); 232e581ca81SKees Cook 233e581ca81SKees Cook record->psi = psinfo; 234c7f3c595SKees Cook 235c7f3c595SKees Cook /* Report zeroed timestamp if called before timekeeping has resumed. */ 2367aaa822eSKees Cook record->time = ns_to_timespec64(ktime_get_real_fast_ns()); 237e581ca81SKees Cook } 238e581ca81SKees Cook 239ca01d6ddSTony Luck /* 2400eed84ffSKees Cook * callback from kmsg_dump. Save as much as we can (up to kmsg_bytes) from the 2410eed84ffSKees Cook * end of the buffer. 242ca01d6ddSTony Luck */ 243ca01d6ddSTony Luck static void pstore_dump(struct kmsg_dumper *dumper, 244e2ae715dSKay Sievers enum kmsg_dump_reason reason) 245ca01d6ddSTony Luck { 246f9f3f02dSJohn Ogness struct kmsg_dump_iter iter; 247e2ae715dSKay Sievers unsigned long total = 0; 248381b872cSSeiji Aguchi const char *why; 249b94fdd07SMatthew Garrett unsigned int part = 1; 2508126b1c7SJann Horn unsigned long flags = 0; 25138b91847SGuilherme G. Piccoli int saved_ret = 0; 252e2ae715dSKay Sievers int ret; 253ca01d6ddSTony Luck 254fb13cb8aSKees Cook why = kmsg_dump_reason_str(reason); 2559f6af27fSTony Luck 2568126b1c7SJann Horn if (pstore_cannot_block_path(reason)) { 2578126b1c7SJann Horn if (!spin_trylock_irqsave(&psinfo->buf_lock, flags)) { 2588126b1c7SJann Horn pr_err("dump skipped in %s path because of concurrent dump\n", 259ea84b580SKees Cook in_nmi() ? "NMI" : why); 260959217c8SLi Pengcheng return; 2619f244e9cSSeiji Aguchi } 2628126b1c7SJann Horn } else { 2638126b1c7SJann Horn spin_lock_irqsave(&psinfo->buf_lock, flags); 264ea84b580SKees Cook } 265ea84b580SKees Cook 266f9f3f02dSJohn Ogness kmsg_dump_rewind(&iter); 267f9f3f02dSJohn Ogness 268ca01d6ddSTony Luck oopscount++; 269ca01d6ddSTony Luck while (total < kmsg_bytes) { 270e2ae715dSKay Sievers char *dst; 27176cc9580SKees Cook size_t dst_size; 27276cc9580SKees Cook int header_size; 273b0aad7a9SAruna Balakrishnaiah int zipped_len = -1; 27476cc9580SKees Cook size_t dump_size; 275e581ca81SKees Cook struct pstore_record record; 276e581ca81SKees Cook 277e581ca81SKees Cook pstore_record_init(&record, psinfo); 278e581ca81SKees Cook record.type = PSTORE_TYPE_DMESG; 279e581ca81SKees Cook record.count = oopscount; 280e581ca81SKees Cook record.reason = reason; 281e581ca81SKees Cook record.part = part; 282e581ca81SKees Cook record.buf = psinfo->buf; 283e2ae715dSKay Sievers 284*1756ddeaSArd Biesheuvel dst = big_oops_buf ?: psinfo->buf; 28576cc9580SKees Cook dst_size = psinfo->bufsize; 286235f6d15SNamhyung Kim 28776cc9580SKees Cook /* Write dump header. */ 28876cc9580SKees Cook header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, 28976cc9580SKees Cook oopscount, part); 29076cc9580SKees Cook dst_size -= header_size; 291b0aad7a9SAruna Balakrishnaiah 29276cc9580SKees Cook /* Write dump contents. */ 293f9f3f02dSJohn Ogness if (!kmsg_dump_get_buffer(&iter, true, dst + header_size, 29476cc9580SKees Cook dst_size, &dump_size)) 295b0aad7a9SAruna Balakrishnaiah break; 296b0aad7a9SAruna Balakrishnaiah 297ea84b580SKees Cook if (big_oops_buf) { 298b0aad7a9SAruna Balakrishnaiah zipped_len = pstore_compress(dst, psinfo->buf, 29976cc9580SKees Cook header_size + dump_size, 30076cc9580SKees Cook psinfo->bufsize); 301b0aad7a9SAruna Balakrishnaiah 302b0aad7a9SAruna Balakrishnaiah if (zipped_len > 0) { 30376cc9580SKees Cook record.compressed = true; 30476cc9580SKees Cook record.size = zipped_len; 305b0aad7a9SAruna Balakrishnaiah } else { 306*1756ddeaSArd Biesheuvel record.size = header_size + dump_size; 307*1756ddeaSArd Biesheuvel memcpy(psinfo->buf, dst, record.size); 308b0aad7a9SAruna Balakrishnaiah } 309b0aad7a9SAruna Balakrishnaiah } else { 31076cc9580SKees Cook record.size = header_size + dump_size; 311b0aad7a9SAruna Balakrishnaiah } 312b0aad7a9SAruna Balakrishnaiah 31376cc9580SKees Cook ret = psinfo->write(&record); 31478c83c82SKees Cook if (ret == 0 && reason == KMSG_DUMP_OOPS) { 3156dda9266SLuck, Tony pstore_new_entry = 1; 31678c83c82SKees Cook pstore_timer_kick(); 31738b91847SGuilherme G. Piccoli } else { 31838b91847SGuilherme G. Piccoli /* Preserve only the first non-zero returned value. */ 31938b91847SGuilherme G. Piccoli if (!saved_ret) 32038b91847SGuilherme G. Piccoli saved_ret = ret; 32178c83c82SKees Cook } 322e2ae715dSKay Sievers 32376cc9580SKees Cook total += record.size; 32456280682SMatthew Garrett part++; 325ca01d6ddSTony Luck } 3268126b1c7SJann Horn spin_unlock_irqrestore(&psinfo->buf_lock, flags); 32738b91847SGuilherme G. Piccoli 32838b91847SGuilherme G. Piccoli if (saved_ret) { 32938b91847SGuilherme G. Piccoli pr_err_once("backend (%s) writing error (%d)\n", psinfo->name, 33038b91847SGuilherme G. Piccoli saved_ret); 33138b91847SGuilherme G. Piccoli } 332ca01d6ddSTony Luck } 333ca01d6ddSTony Luck 334ca01d6ddSTony Luck static struct kmsg_dumper pstore_dumper = { 335ca01d6ddSTony Luck .dump = pstore_dump, 336ca01d6ddSTony Luck }; 337ca01d6ddSTony Luck 338306e5c2aSGeliang Tang /* 339306e5c2aSGeliang Tang * Register with kmsg_dump to save last part of console log on panic. 340306e5c2aSGeliang Tang */ 34118730411SGeliang Tang static void pstore_register_kmsg(void) 34218730411SGeliang Tang { 34318730411SGeliang Tang kmsg_dump_register(&pstore_dumper); 34418730411SGeliang Tang } 34518730411SGeliang Tang 346ee1d2674SGeliang Tang static void pstore_unregister_kmsg(void) 347ee1d2674SGeliang Tang { 348ee1d2674SGeliang Tang kmsg_dump_unregister(&pstore_dumper); 349ee1d2674SGeliang Tang } 350ee1d2674SGeliang Tang 351f29e5956SAnton Vorontsov #ifdef CONFIG_PSTORE_CONSOLE 352f29e5956SAnton Vorontsov static void pstore_console_write(struct console *con, const char *s, unsigned c) 353f29e5956SAnton Vorontsov { 354e581ca81SKees Cook struct pstore_record record; 355f29e5956SAnton Vorontsov 3564c6c4d34SYue Hu if (!c) 3574c6c4d34SYue Hu return; 3584c6c4d34SYue Hu 359e581ca81SKees Cook pstore_record_init(&record, psinfo); 360e581ca81SKees Cook record.type = PSTORE_TYPE_CONSOLE; 361e581ca81SKees Cook 362b10b4711SKees Cook record.buf = (char *)s; 363b10b4711SKees Cook record.size = c; 3644c9ec219SKees Cook psinfo->write(&record); 365f29e5956SAnton Vorontsov } 366f29e5956SAnton Vorontsov 367f29e5956SAnton Vorontsov static struct console pstore_console = { 368f29e5956SAnton Vorontsov .write = pstore_console_write, 369f29e5956SAnton Vorontsov .index = -1, 370f29e5956SAnton Vorontsov }; 371f29e5956SAnton Vorontsov 372f29e5956SAnton Vorontsov static void pstore_register_console(void) 373f29e5956SAnton Vorontsov { 374d195c390SKees Cook /* Show which backend is going to get console writes. */ 375d195c390SKees Cook strscpy(pstore_console.name, psinfo->name, 376d195c390SKees Cook sizeof(pstore_console.name)); 377b7753fc7SKees Cook /* 378b7753fc7SKees Cook * Always initialize flags here since prior unregister_console() 379b7753fc7SKees Cook * calls may have changed settings (specifically CON_ENABLED). 380b7753fc7SKees Cook */ 381b7753fc7SKees Cook pstore_console.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME; 382f29e5956SAnton Vorontsov register_console(&pstore_console); 383f29e5956SAnton Vorontsov } 384ee1d2674SGeliang Tang 385ee1d2674SGeliang Tang static void pstore_unregister_console(void) 386ee1d2674SGeliang Tang { 387ee1d2674SGeliang Tang unregister_console(&pstore_console); 388ee1d2674SGeliang Tang } 389f29e5956SAnton Vorontsov #else 390f29e5956SAnton Vorontsov static void pstore_register_console(void) {} 391ee1d2674SGeliang Tang static void pstore_unregister_console(void) {} 392f29e5956SAnton Vorontsov #endif 393f29e5956SAnton Vorontsov 3944c9ec219SKees Cook static int pstore_write_user_compat(struct pstore_record *record, 395fdd03118SKees Cook const char __user *buf) 3965bf6d1b9SMark Salyzyn { 39730800d99SKees Cook int ret = 0; 3985bf6d1b9SMark Salyzyn 39930800d99SKees Cook if (record->buf) 40030800d99SKees Cook return -EINVAL; 4015bf6d1b9SMark Salyzyn 402077090afSGeliang Tang record->buf = memdup_user(buf, record->size); 403dfd6fa39SHirofumi Nakagawa if (IS_ERR(record->buf)) { 404077090afSGeliang Tang ret = PTR_ERR(record->buf); 40530800d99SKees Cook goto out; 4065bf6d1b9SMark Salyzyn } 40730800d99SKees Cook 4084c9ec219SKees Cook ret = record->psi->write(record); 40930800d99SKees Cook 41030800d99SKees Cook kfree(record->buf); 411077090afSGeliang Tang out: 41230800d99SKees Cook record->buf = NULL; 41330800d99SKees Cook 41430800d99SKees Cook return unlikely(ret < 0) ? ret : record->size; 4155bf6d1b9SMark Salyzyn } 4165bf6d1b9SMark Salyzyn 417ca01d6ddSTony Luck /* 418ca01d6ddSTony Luck * platform specific persistent storage driver registers with 419ca01d6ddSTony Luck * us here. If pstore is already mounted, call the platform 420ca01d6ddSTony Luck * read function right away to populate the file system. If not 421ca01d6ddSTony Luck * then the pstore mount code will call us later to fill out 422ca01d6ddSTony Luck * the file system. 423ca01d6ddSTony Luck */ 424ca01d6ddSTony Luck int pstore_register(struct pstore_info *psi) 425ca01d6ddSTony Luck { 4260d7cd09aSKees Cook if (backend && strcmp(backend, psi->name)) { 427d85644dcSGuilherme G. Piccoli pr_warn("backend '%s' already in use: ignoring '%s'\n", 428d85644dcSGuilherme G. Piccoli backend, psi->name); 429d85644dcSGuilherme G. Piccoli return -EBUSY; 4300d7cd09aSKees Cook } 4318e48b1a8SLenny Szubowicz 4324c9ec219SKees Cook /* Sanity check flags. */ 4334c9ec219SKees Cook if (!psi->flags) { 4344c9ec219SKees Cook pr_warn("backend '%s' must support at least one frontend\n", 4354c9ec219SKees Cook psi->name); 4364c9ec219SKees Cook return -EINVAL; 4374c9ec219SKees Cook } 4384c9ec219SKees Cook 4394c9ec219SKees Cook /* Check for required functions. */ 4404c9ec219SKees Cook if (!psi->read || !psi->write) { 4414c9ec219SKees Cook pr_warn("backend '%s' must implement read() and write()\n", 4424c9ec219SKees Cook psi->name); 4434c9ec219SKees Cook return -EINVAL; 4444c9ec219SKees Cook } 4454c9ec219SKees Cook 446cab12fd0SKees Cook mutex_lock(&psinfo_lock); 447ca01d6ddSTony Luck if (psinfo) { 4480d7cd09aSKees Cook pr_warn("backend '%s' already loaded: ignoring '%s'\n", 4490d7cd09aSKees Cook psinfo->name, psi->name); 450cab12fd0SKees Cook mutex_unlock(&psinfo_lock); 451ca01d6ddSTony Luck return -EBUSY; 452ca01d6ddSTony Luck } 453dee28e72SMatthew Garrett 4544c9ec219SKees Cook if (!psi->write_user) 4554c9ec219SKees Cook psi->write_user = pstore_write_user_compat; 456ca01d6ddSTony Luck psinfo = psi; 457f6f82851SKees Cook mutex_init(&psinfo->read_mutex); 4588126b1c7SJann Horn spin_lock_init(&psinfo->buf_lock); 459ca01d6ddSTony Luck 4608880fa32SKees Cook if (psi->flags & PSTORE_FLAGS_DMESG) 461b0aad7a9SAruna Balakrishnaiah allocate_buf_for_compression(); 462b0aad7a9SAruna Balakrishnaiah 4636dda9266SLuck, Tony pstore_get_records(0); 464ca01d6ddSTony Luck 4653524e688SPavel Tatashin if (psi->flags & PSTORE_FLAGS_DMESG) { 4663524e688SPavel Tatashin pstore_dumper.max_reason = psinfo->max_reason; 46718730411SGeliang Tang pstore_register_kmsg(); 4683524e688SPavel Tatashin } 469c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_CONSOLE) 470f29e5956SAnton Vorontsov pstore_register_console(); 471c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_FTRACE) 47265f8c95eSAnton Vorontsov pstore_register_ftrace(); 473c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_PMSG) 4749d5438f4SMark Salyzyn pstore_register_pmsg(); 475ca01d6ddSTony Luck 4766330d553SKees Cook /* Start watching for new records, if desired. */ 47778c83c82SKees Cook pstore_timer_kick(); 4786dda9266SLuck, Tony 47942222c2aSWang Long /* 48042222c2aSWang Long * Update the module parameter backend, so it is visible 48142222c2aSWang Long * through /sys/module/pstore/parameters/backend 48242222c2aSWang Long */ 483563ca40dSKees Cook backend = kstrdup(psi->name, GFP_KERNEL); 48442222c2aSWang Long 485ef748853SFabian Frederick pr_info("Registered %s as persistent store backend\n", psi->name); 4868e48b1a8SLenny Szubowicz 4876248a066SKees Cook mutex_unlock(&psinfo_lock); 488ca01d6ddSTony Luck return 0; 489ca01d6ddSTony Luck } 490ca01d6ddSTony Luck EXPORT_SYMBOL_GPL(pstore_register); 491ca01d6ddSTony Luck 492ee1d2674SGeliang Tang void pstore_unregister(struct pstore_info *psi) 493ee1d2674SGeliang Tang { 4946248a066SKees Cook /* It's okay to unregister nothing. */ 4956248a066SKees Cook if (!psi) 4966248a066SKees Cook return; 4976248a066SKees Cook 4986248a066SKees Cook mutex_lock(&psinfo_lock); 4996248a066SKees Cook 5006248a066SKees Cook /* Only one backend can be registered at a time. */ 5016248a066SKees Cook if (WARN_ON(psi != psinfo)) { 5026248a066SKees Cook mutex_unlock(&psinfo_lock); 5036248a066SKees Cook return; 5046248a066SKees Cook } 5056248a066SKees Cook 50678c83c82SKees Cook /* Unregister all callbacks. */ 507c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_PMSG) 508ee1d2674SGeliang Tang pstore_unregister_pmsg(); 509c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_FTRACE) 510ee1d2674SGeliang Tang pstore_unregister_ftrace(); 511c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_CONSOLE) 512ee1d2674SGeliang Tang pstore_unregister_console(); 513c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_DMESG) 514ee1d2674SGeliang Tang pstore_unregister_kmsg(); 515ee1d2674SGeliang Tang 51678c83c82SKees Cook /* Stop timer and make sure all work has finished. */ 51778c83c82SKees Cook del_timer_sync(&pstore_timer); 51878c83c82SKees Cook flush_work(&pstore_work); 51978c83c82SKees Cook 520609e28bbSKees Cook /* Remove all backend records from filesystem tree. */ 521609e28bbSKees Cook pstore_put_backend_records(psi); 522609e28bbSKees Cook 523ee1d2674SGeliang Tang free_buf_for_compression(); 524ee1d2674SGeliang Tang 525ee1d2674SGeliang Tang psinfo = NULL; 526563ca40dSKees Cook kfree(backend); 527ee1d2674SGeliang Tang backend = NULL; 5286a14f198SGuilherme G. Piccoli 5296a14f198SGuilherme G. Piccoli pr_info("Unregistered %s as persistent store backend\n", psi->name); 5306248a066SKees Cook mutex_unlock(&psinfo_lock); 531ee1d2674SGeliang Tang } 532ee1d2674SGeliang Tang EXPORT_SYMBOL_GPL(pstore_unregister); 533ee1d2674SGeliang Tang 534634f8f51SKees Cook static void decompress_record(struct pstore_record *record) 535634f8f51SKees Cook { 536bdabc8e7SKees Cook int ret; 537634f8f51SKees Cook int unzipped_len; 538bdabc8e7SKees Cook char *unzipped, *workspace; 539634f8f51SKees Cook 54019d8e914SJiri Bohac if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !record->compressed) 5414a16d1cbSAnkit Kumar return; 5424a16d1cbSAnkit Kumar 543634f8f51SKees Cook /* Only PSTORE_TYPE_DMESG support compression. */ 5444a16d1cbSAnkit Kumar if (record->type != PSTORE_TYPE_DMESG) { 545634f8f51SKees Cook pr_warn("ignored compressed record type %d\n", record->type); 546634f8f51SKees Cook return; 547634f8f51SKees Cook } 548634f8f51SKees Cook 549bdabc8e7SKees Cook /* Missing compression buffer means compression was not initialized. */ 550634f8f51SKees Cook if (!big_oops_buf) { 551bdabc8e7SKees Cook pr_warn("no decompression method initialized!\n"); 552634f8f51SKees Cook return; 553634f8f51SKees Cook } 554634f8f51SKees Cook 555bdabc8e7SKees Cook /* Allocate enough space to hold max decompression and ECC. */ 556*1756ddeaSArd Biesheuvel workspace = kmalloc(psinfo->bufsize + record->ecc_notice_size, 5577e8cc8dcSKees Cook GFP_KERNEL); 558bdabc8e7SKees Cook if (!workspace) 559bdabc8e7SKees Cook return; 560bdabc8e7SKees Cook 561bdabc8e7SKees Cook /* After decompression "unzipped_len" is almost certainly smaller. */ 56240158dbfSGuilherme G. Piccoli ret = crypto_comp_decompress(tfm, record->buf, record->size, 56340158dbfSGuilherme G. Piccoli workspace, &unzipped_len); 564bdabc8e7SKees Cook if (ret) { 56540158dbfSGuilherme G. Piccoli pr_err("crypto_comp_decompress failed, ret = %d!\n", ret); 566bdabc8e7SKees Cook kfree(workspace); 5677e8cc8dcSKees Cook return; 5687e8cc8dcSKees Cook } 5697e8cc8dcSKees Cook 5707e8cc8dcSKees Cook /* Append ECC notice to decompressed buffer. */ 571bdabc8e7SKees Cook memcpy(workspace + unzipped_len, record->buf + record->size, 572634f8f51SKees Cook record->ecc_notice_size); 5737e8cc8dcSKees Cook 574bdabc8e7SKees Cook /* Copy decompressed contents into an minimum-sized allocation. */ 575bdabc8e7SKees Cook unzipped = kmemdup(workspace, unzipped_len + record->ecc_notice_size, 576bdabc8e7SKees Cook GFP_KERNEL); 577bdabc8e7SKees Cook kfree(workspace); 578bdabc8e7SKees Cook if (!unzipped) 579bdabc8e7SKees Cook return; 580bdabc8e7SKees Cook 581bdabc8e7SKees Cook /* Swap out compressed contents with decompressed contents. */ 582634f8f51SKees Cook kfree(record->buf); 583bdabc8e7SKees Cook record->buf = unzipped; 584634f8f51SKees Cook record->size = unzipped_len; 585634f8f51SKees Cook record->compressed = false; 586634f8f51SKees Cook } 587634f8f51SKees Cook 588ca01d6ddSTony Luck /* 5893a7d2fd1SKees Cook * Read all the records from one persistent store backend. Create 5906dda9266SLuck, Tony * files in our filesystem. Don't warn about -EEXIST errors 5916dda9266SLuck, Tony * when we are re-scanning the backing store looking to add new 5926dda9266SLuck, Tony * error records. 593ca01d6ddSTony Luck */ 5943a7d2fd1SKees Cook void pstore_get_backend_records(struct pstore_info *psi, 5953a7d2fd1SKees Cook struct dentry *root, int quiet) 596ca01d6ddSTony Luck { 5972a2b0acfSKees Cook int failed = 0; 598656de42eSKees Cook unsigned int stop_loop = 65536; 599ca01d6ddSTony Luck 6003a7d2fd1SKees Cook if (!psi || !root) 601ca01d6ddSTony Luck return; 602ca01d6ddSTony Luck 603f6f82851SKees Cook mutex_lock(&psi->read_mutex); 6042174f6dfSKees Cook if (psi->open && psi->open(psi)) 60506cf91b4SChen Gong goto out; 60606cf91b4SChen Gong 6071dfff7ddSKees Cook /* 6081dfff7ddSKees Cook * Backend callback read() allocates record.buf. decompress_record() 6091dfff7ddSKees Cook * may reallocate record.buf. On success, pstore_mkfile() will keep 6101dfff7ddSKees Cook * the record.buf, so free it only on failure. 6111dfff7ddSKees Cook */ 612656de42eSKees Cook for (; stop_loop; stop_loop--) { 6132a2b0acfSKees Cook struct pstore_record *record; 6142a2b0acfSKees Cook int rc; 6152a2b0acfSKees Cook 6162a2b0acfSKees Cook record = kzalloc(sizeof(*record), GFP_KERNEL); 6172a2b0acfSKees Cook if (!record) { 6182a2b0acfSKees Cook pr_err("out of memory creating record\n"); 6192a2b0acfSKees Cook break; 6202a2b0acfSKees Cook } 621e581ca81SKees Cook pstore_record_init(record, psi); 6222a2b0acfSKees Cook 6232a2b0acfSKees Cook record->size = psi->read(record); 6242a2b0acfSKees Cook 6252a2b0acfSKees Cook /* No more records left in backend? */ 626f6525b96SDouglas Anderson if (record->size <= 0) { 627f6525b96SDouglas Anderson kfree(record); 6282a2b0acfSKees Cook break; 629f6525b96SDouglas Anderson } 6302a2b0acfSKees Cook 6312a2b0acfSKees Cook decompress_record(record); 6323a7d2fd1SKees Cook rc = pstore_mkfile(root, record); 6331dfff7ddSKees Cook if (rc) { 63483f70f07SKees Cook /* pstore_mkfile() did not take record, so free it. */ 6352a2b0acfSKees Cook kfree(record->buf); 6368ca869b2SArd Biesheuvel kfree(record->priv); 63783f70f07SKees Cook kfree(record); 6381dfff7ddSKees Cook if (rc != -EEXIST || !quiet) 6391dfff7ddSKees Cook failed++; 6401dfff7ddSKees Cook } 641ca01d6ddSTony Luck } 6422174f6dfSKees Cook if (psi->close) 64306cf91b4SChen Gong psi->close(psi); 64406cf91b4SChen Gong out: 645f6f82851SKees Cook mutex_unlock(&psi->read_mutex); 646ca01d6ddSTony Luck 647ca01d6ddSTony Luck if (failed) 648656de42eSKees Cook pr_warn("failed to create %d record(s) from '%s'\n", 649ca01d6ddSTony Luck failed, psi->name); 650656de42eSKees Cook if (!stop_loop) 651656de42eSKees Cook pr_err("looping? Too many records seen from '%s'\n", 652656de42eSKees Cook psi->name); 653ca01d6ddSTony Luck } 654ca01d6ddSTony Luck 6556dda9266SLuck, Tony static void pstore_dowork(struct work_struct *work) 6566dda9266SLuck, Tony { 6576dda9266SLuck, Tony pstore_get_records(1); 6586dda9266SLuck, Tony } 6596dda9266SLuck, Tony 66024ed960aSKees Cook static void pstore_timefunc(struct timer_list *unused) 6616dda9266SLuck, Tony { 6626dda9266SLuck, Tony if (pstore_new_entry) { 6636dda9266SLuck, Tony pstore_new_entry = 0; 6646dda9266SLuck, Tony schedule_work(&pstore_work); 6656dda9266SLuck, Tony } 6666dda9266SLuck, Tony 66778c83c82SKees Cook pstore_timer_kick(); 6686dda9266SLuck, Tony } 6696dda9266SLuck, Tony 670cb095afdSKees Cook static int __init pstore_init(void) 671cb095afdSKees Cook { 672cb095afdSKees Cook int ret; 673cb095afdSKees Cook 67441603165SJoel Fernandes (Google) /* 67541603165SJoel Fernandes (Google) * Check if any pstore backends registered earlier but did not 67641603165SJoel Fernandes (Google) * initialize compression because crypto was not ready. If so, 67741603165SJoel Fernandes (Google) * initialize compression now. 67841603165SJoel Fernandes (Google) */ 67941603165SJoel Fernandes (Google) allocate_buf_for_compression(); 68041603165SJoel Fernandes (Google) 681cb095afdSKees Cook ret = pstore_init_fs(); 682cb095afdSKees Cook if (ret) 6838a57d6d4Schenqiwu free_buf_for_compression(); 684cb095afdSKees Cook 6858a57d6d4Schenqiwu return ret; 686cb095afdSKees Cook } 68741603165SJoel Fernandes (Google) late_initcall(pstore_init); 688cb095afdSKees Cook 689cb095afdSKees Cook static void __exit pstore_exit(void) 690cb095afdSKees Cook { 691cb095afdSKees Cook pstore_exit_fs(); 692cb095afdSKees Cook } 693cb095afdSKees Cook module_exit(pstore_exit) 694cb095afdSKees Cook 695cb095afdSKees Cook MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>"); 696cb095afdSKees Cook MODULE_LICENSE("GPL"); 697