Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * MTD Oops/Panic logger
0004  *
0005  * Copyright © 2007 Nokia Corporation. All rights reserved.
0006  *
0007  * Author: Richard Purdie <rpurdie@openedhand.com>
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/console.h>
0013 #include <linux/vmalloc.h>
0014 #include <linux/workqueue.h>
0015 #include <linux/sched.h>
0016 #include <linux/wait.h>
0017 #include <linux/delay.h>
0018 #include <linux/interrupt.h>
0019 #include <linux/timekeeping.h>
0020 #include <linux/mtd/mtd.h>
0021 #include <linux/kmsg_dump.h>
0022 
0023 /* Maximum MTD partition size */
0024 #define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
0025 
0026 static unsigned long record_size = 4096;
0027 module_param(record_size, ulong, 0400);
0028 MODULE_PARM_DESC(record_size,
0029         "record size for MTD OOPS pages in bytes (default 4096)");
0030 
0031 static char mtddev[80];
0032 module_param_string(mtddev, mtddev, 80, 0400);
0033 MODULE_PARM_DESC(mtddev,
0034         "name or index number of the MTD device to use");
0035 
0036 static int dump_oops = 1;
0037 module_param(dump_oops, int, 0600);
0038 MODULE_PARM_DESC(dump_oops,
0039         "set to 1 to dump oopses, 0 to only dump panics (default 1)");
0040 
0041 #define MTDOOPS_KERNMSG_MAGIC_v1 0x5d005d00  /* Original */
0042 #define MTDOOPS_KERNMSG_MAGIC_v2 0x5d005e00  /* Adds the timestamp */
0043 
0044 struct mtdoops_hdr {
0045     u32 seq;
0046     u32 magic;
0047     ktime_t timestamp;
0048 } __packed;
0049 
0050 static struct mtdoops_context {
0051     struct kmsg_dumper dump;
0052 
0053     int mtd_index;
0054     struct work_struct work_erase;
0055     struct work_struct work_write;
0056     struct mtd_info *mtd;
0057     int oops_pages;
0058     int nextpage;
0059     int nextcount;
0060     unsigned long *oops_page_used;
0061 
0062     unsigned long oops_buf_busy;
0063     void *oops_buf;
0064 } oops_cxt;
0065 
0066 static void mark_page_used(struct mtdoops_context *cxt, int page)
0067 {
0068     set_bit(page, cxt->oops_page_used);
0069 }
0070 
0071 static void mark_page_unused(struct mtdoops_context *cxt, int page)
0072 {
0073     clear_bit(page, cxt->oops_page_used);
0074 }
0075 
0076 static int page_is_used(struct mtdoops_context *cxt, int page)
0077 {
0078     return test_bit(page, cxt->oops_page_used);
0079 }
0080 
0081 static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
0082 {
0083     struct mtd_info *mtd = cxt->mtd;
0084     u32 start_page_offset = mtd_div_by_eb(offset, mtd) * mtd->erasesize;
0085     u32 start_page = start_page_offset / record_size;
0086     u32 erase_pages = mtd->erasesize / record_size;
0087     struct erase_info erase;
0088     int ret;
0089     int page;
0090 
0091     erase.addr = offset;
0092     erase.len = mtd->erasesize;
0093 
0094     ret = mtd_erase(mtd, &erase);
0095     if (ret) {
0096         printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
0097                (unsigned long long)erase.addr,
0098                (unsigned long long)erase.len, mtddev);
0099         return ret;
0100     }
0101 
0102     /* Mark pages as unused */
0103     for (page = start_page; page < start_page + erase_pages; page++)
0104         mark_page_unused(cxt, page);
0105 
0106     return 0;
0107 }
0108 
0109 static void mtdoops_inc_counter(struct mtdoops_context *cxt)
0110 {
0111     cxt->nextpage++;
0112     if (cxt->nextpage >= cxt->oops_pages)
0113         cxt->nextpage = 0;
0114     cxt->nextcount++;
0115     if (cxt->nextcount == 0xffffffff)
0116         cxt->nextcount = 0;
0117 
0118     if (page_is_used(cxt, cxt->nextpage)) {
0119         schedule_work(&cxt->work_erase);
0120         return;
0121     }
0122 
0123     printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n",
0124            cxt->nextpage, cxt->nextcount);
0125 }
0126 
0127 /* Scheduled work - when we can't proceed without erasing a block */
0128 static void mtdoops_workfunc_erase(struct work_struct *work)
0129 {
0130     struct mtdoops_context *cxt =
0131             container_of(work, struct mtdoops_context, work_erase);
0132     struct mtd_info *mtd = cxt->mtd;
0133     int i = 0, j, ret, mod;
0134 
0135     /* We were unregistered */
0136     if (!mtd)
0137         return;
0138 
0139     mod = (cxt->nextpage * record_size) % mtd->erasesize;
0140     if (mod != 0) {
0141         cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / record_size);
0142         if (cxt->nextpage >= cxt->oops_pages)
0143             cxt->nextpage = 0;
0144     }
0145 
0146     while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) {
0147 badblock:
0148         printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
0149                cxt->nextpage * record_size);
0150         i++;
0151         cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size);
0152         if (cxt->nextpage >= cxt->oops_pages)
0153             cxt->nextpage = 0;
0154         if (i == cxt->oops_pages / (mtd->erasesize / record_size)) {
0155             printk(KERN_ERR "mtdoops: all blocks bad!\n");
0156             return;
0157         }
0158     }
0159 
0160     if (ret < 0) {
0161         printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n");
0162         return;
0163     }
0164 
0165     for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
0166         ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
0167 
0168     if (ret >= 0) {
0169         printk(KERN_DEBUG "mtdoops: ready %d, %d\n",
0170                cxt->nextpage, cxt->nextcount);
0171         return;
0172     }
0173 
0174     if (ret == -EIO) {
0175         ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
0176         if (ret < 0 && ret != -EOPNOTSUPP) {
0177             printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
0178             return;
0179         }
0180     }
0181     goto badblock;
0182 }
0183 
0184 static void mtdoops_write(struct mtdoops_context *cxt, int panic)
0185 {
0186     struct mtd_info *mtd = cxt->mtd;
0187     size_t retlen;
0188     struct mtdoops_hdr *hdr;
0189     int ret;
0190 
0191     if (test_and_set_bit(0, &cxt->oops_buf_busy))
0192         return;
0193 
0194     /* Add mtdoops header to the buffer */
0195     hdr = (struct mtdoops_hdr *)cxt->oops_buf;
0196     hdr->seq = cxt->nextcount;
0197     hdr->magic = MTDOOPS_KERNMSG_MAGIC_v2;
0198     hdr->timestamp = ktime_get_real();
0199 
0200     if (panic) {
0201         ret = mtd_panic_write(mtd, cxt->nextpage * record_size,
0202                       record_size, &retlen, cxt->oops_buf);
0203         if (ret == -EOPNOTSUPP) {
0204             printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n");
0205             goto out;
0206         }
0207     } else
0208         ret = mtd_write(mtd, cxt->nextpage * record_size,
0209                 record_size, &retlen, cxt->oops_buf);
0210 
0211     if (retlen != record_size || ret < 0)
0212         printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n",
0213                cxt->nextpage * record_size, retlen, record_size, ret);
0214     mark_page_used(cxt, cxt->nextpage);
0215     memset(cxt->oops_buf, 0xff, record_size);
0216 
0217     mtdoops_inc_counter(cxt);
0218 out:
0219     clear_bit(0, &cxt->oops_buf_busy);
0220 }
0221 
0222 static void mtdoops_workfunc_write(struct work_struct *work)
0223 {
0224     struct mtdoops_context *cxt =
0225             container_of(work, struct mtdoops_context, work_write);
0226 
0227     mtdoops_write(cxt, 0);
0228 }
0229 
0230 static void find_next_position(struct mtdoops_context *cxt)
0231 {
0232     struct mtd_info *mtd = cxt->mtd;
0233     struct mtdoops_hdr hdr;
0234     int ret, page, maxpos = 0;
0235     u32 maxcount = 0xffffffff;
0236     size_t retlen;
0237 
0238     for (page = 0; page < cxt->oops_pages; page++) {
0239         if (mtd_block_isbad(mtd, page * record_size))
0240             continue;
0241         /* Assume the page is used */
0242         mark_page_used(cxt, page);
0243         ret = mtd_read(mtd, page * record_size, sizeof(hdr),
0244                    &retlen, (u_char *)&hdr);
0245         if (retlen != sizeof(hdr) ||
0246                 (ret < 0 && !mtd_is_bitflip(ret))) {
0247             printk(KERN_ERR "mtdoops: read failure at %ld (%zu of %zu read), err %d\n",
0248                    page * record_size, retlen, sizeof(hdr), ret);
0249             continue;
0250         }
0251 
0252         if (hdr.seq == 0xffffffff && hdr.magic == 0xffffffff)
0253             mark_page_unused(cxt, page);
0254         if (hdr.seq == 0xffffffff ||
0255             (hdr.magic != MTDOOPS_KERNMSG_MAGIC_v1 &&
0256              hdr.magic != MTDOOPS_KERNMSG_MAGIC_v2))
0257             continue;
0258         if (maxcount == 0xffffffff) {
0259             maxcount = hdr.seq;
0260             maxpos = page;
0261         } else if (hdr.seq < 0x40000000 && maxcount > 0xc0000000) {
0262             maxcount = hdr.seq;
0263             maxpos = page;
0264         } else if (hdr.seq > maxcount && hdr.seq < 0xc0000000) {
0265             maxcount = hdr.seq;
0266             maxpos = page;
0267         } else if (hdr.seq > maxcount && hdr.seq > 0xc0000000
0268                     && maxcount > 0x80000000) {
0269             maxcount = hdr.seq;
0270             maxpos = page;
0271         }
0272     }
0273     if (maxcount == 0xffffffff) {
0274         cxt->nextpage = cxt->oops_pages - 1;
0275         cxt->nextcount = 0;
0276     }
0277     else {
0278         cxt->nextpage = maxpos;
0279         cxt->nextcount = maxcount;
0280     }
0281 
0282     mtdoops_inc_counter(cxt);
0283 }
0284 
0285 static void mtdoops_do_dump(struct kmsg_dumper *dumper,
0286                 enum kmsg_dump_reason reason)
0287 {
0288     struct mtdoops_context *cxt = container_of(dumper,
0289             struct mtdoops_context, dump);
0290     struct kmsg_dump_iter iter;
0291 
0292     /* Only dump oopses if dump_oops is set */
0293     if (reason == KMSG_DUMP_OOPS && !dump_oops)
0294         return;
0295 
0296     kmsg_dump_rewind(&iter);
0297 
0298     if (test_and_set_bit(0, &cxt->oops_buf_busy))
0299         return;
0300     kmsg_dump_get_buffer(&iter, true,
0301                  cxt->oops_buf + sizeof(struct mtdoops_hdr),
0302                  record_size - sizeof(struct mtdoops_hdr), NULL);
0303     clear_bit(0, &cxt->oops_buf_busy);
0304 
0305     if (reason != KMSG_DUMP_OOPS) {
0306         /* Panics must be written immediately */
0307         mtdoops_write(cxt, 1);
0308     } else {
0309         /* For other cases, schedule work to write it "nicely" */
0310         schedule_work(&cxt->work_write);
0311     }
0312 }
0313 
0314 static void mtdoops_notify_add(struct mtd_info *mtd)
0315 {
0316     struct mtdoops_context *cxt = &oops_cxt;
0317     u64 mtdoops_pages = div_u64(mtd->size, record_size);
0318     int err;
0319 
0320     if (!strcmp(mtd->name, mtddev))
0321         cxt->mtd_index = mtd->index;
0322 
0323     if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
0324         return;
0325 
0326     if (mtd->size < mtd->erasesize * 2) {
0327         printk(KERN_ERR "mtdoops: MTD partition %d not big enough for mtdoops\n",
0328                mtd->index);
0329         return;
0330     }
0331     if (mtd->erasesize < record_size) {
0332         printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n",
0333                mtd->index);
0334         return;
0335     }
0336     if (mtd->size > MTDOOPS_MAX_MTD_SIZE) {
0337         printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n",
0338                mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024);
0339         return;
0340     }
0341 
0342     /* oops_page_used is a bit field */
0343     cxt->oops_page_used =
0344         vmalloc(array_size(sizeof(unsigned long),
0345                    DIV_ROUND_UP(mtdoops_pages,
0346                         BITS_PER_LONG)));
0347     if (!cxt->oops_page_used) {
0348         printk(KERN_ERR "mtdoops: could not allocate page array\n");
0349         return;
0350     }
0351 
0352     cxt->dump.max_reason = KMSG_DUMP_OOPS;
0353     cxt->dump.dump = mtdoops_do_dump;
0354     err = kmsg_dump_register(&cxt->dump);
0355     if (err) {
0356         printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err);
0357         vfree(cxt->oops_page_used);
0358         cxt->oops_page_used = NULL;
0359         return;
0360     }
0361 
0362     cxt->mtd = mtd;
0363     cxt->oops_pages = (int)mtd->size / record_size;
0364     find_next_position(cxt);
0365     printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index);
0366 }
0367 
0368 static void mtdoops_notify_remove(struct mtd_info *mtd)
0369 {
0370     struct mtdoops_context *cxt = &oops_cxt;
0371 
0372     if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
0373         return;
0374 
0375     if (kmsg_dump_unregister(&cxt->dump) < 0)
0376         printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n");
0377 
0378     cxt->mtd = NULL;
0379     flush_work(&cxt->work_erase);
0380     flush_work(&cxt->work_write);
0381 }
0382 
0383 
0384 static struct mtd_notifier mtdoops_notifier = {
0385     .add    = mtdoops_notify_add,
0386     .remove = mtdoops_notify_remove,
0387 };
0388 
0389 static int __init mtdoops_init(void)
0390 {
0391     struct mtdoops_context *cxt = &oops_cxt;
0392     int mtd_index;
0393     char *endp;
0394 
0395     if (strlen(mtddev) == 0) {
0396         printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n");
0397         return -EINVAL;
0398     }
0399     if ((record_size & 4095) != 0) {
0400         printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n");
0401         return -EINVAL;
0402     }
0403     if (record_size < 4096) {
0404         printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n");
0405         return -EINVAL;
0406     }
0407 
0408     /* Setup the MTD device to use */
0409     cxt->mtd_index = -1;
0410     mtd_index = simple_strtoul(mtddev, &endp, 0);
0411     if (*endp == '\0')
0412         cxt->mtd_index = mtd_index;
0413 
0414     cxt->oops_buf = vmalloc(record_size);
0415     if (!cxt->oops_buf)
0416         return -ENOMEM;
0417     memset(cxt->oops_buf, 0xff, record_size);
0418     cxt->oops_buf_busy = 0;
0419 
0420     INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase);
0421     INIT_WORK(&cxt->work_write, mtdoops_workfunc_write);
0422 
0423     register_mtd_user(&mtdoops_notifier);
0424     return 0;
0425 }
0426 
0427 static void __exit mtdoops_exit(void)
0428 {
0429     struct mtdoops_context *cxt = &oops_cxt;
0430 
0431     unregister_mtd_user(&mtdoops_notifier);
0432     vfree(cxt->oops_buf);
0433     vfree(cxt->oops_page_used);
0434 }
0435 
0436 
0437 module_init(mtdoops_init);
0438 module_exit(mtdoops_exit);
0439 
0440 MODULE_LICENSE("GPL");
0441 MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
0442 MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver");