0001
0002
0003
0004
0005
0006
0007
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
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
0042 #define MTDOOPS_KERNMSG_MAGIC_v2 0x5d005e00
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
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
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
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
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
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
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
0307 mtdoops_write(cxt, 1);
0308 } else {
0309
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
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
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");