/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2007 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include /*--- #include ---*/ #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_PROC_FS) #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) #include #endif #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ #include "tffs_local.h" #if defined(CONFIG_AVM_WATCHDOG) #include #endif/*--- #if defined(CONFIG_AVM_WATCHDOG) ---*/ #include #if defined(CONFIG_MACH_AR934x) extern int ath_restore_log_from_ram(char *txtbuf, unsigned int txtlen); extern void ath_inval_log_from_ram(void); #endif/*--- #if defined(CONFIG_MACH_AR934x) ---*/ static struct tffs_fops_handle *panic_handle = NULL; unsigned int tffs_spi_mode = 0; unsigned int tffs_panic_log_suppress = 0; EXPORT_SYMBOL(tffs_panic_log_suppress); /* minimal panic-log: only for atheros without correct nmi because freezer occur */ static char panic_dummy_log[512]; static int tffs_read_dummy_log(char *buf, int buf_len, unsigned int id); static DEFINE_SEMAPHORE(log_sema); /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ void tffs_panic_log_open(void) { if(panic_handle == NULL){ panic_handle = tffs_open_panic(); } } EXPORT_SYMBOL(tffs_panic_log_open); /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ void tffs_panic_log_write(const char *buffer, unsigned int len) { static loff_t off __attribute__((unused)); if(panic_handle != NULL){ tffs_write_kern(panic_handle, buffer, len, &off); } } EXPORT_SYMBOL(tffs_panic_log_write); /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ void tffs_panic_log_close(void) { if(panic_handle != NULL){ tffs_release_kern(panic_handle); panic_handle = NULL; } } EXPORT_SYMBOL(tffs_panic_log_close); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int get_act_time(void) { struct timeval now; do_gettimeofday(&now); return (unsigned int)(now.tv_sec); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *time_to_ascii(unsigned long local_time, char *buf, int len, char *prefix) { static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; struct rtc_time tm; rtc_time_to_tm(local_time, &tm); snprintf(buf, len, "%s %s %02d %02d:%02d:%02d %02d UTC %s", day[tm.tm_wday], month[tm.tm_mon], tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900, prefix ); return buf; } /*--------------------------------------------------------------------------------*\ * maxsize of buf: 14 + 10 + 2 + 2 + 2 = 30 \*--------------------------------------------------------------------------------*/ static char *human_readable_time(char *buf, unsigned long buf_size, unsigned long time) { unsigned long seconds, minutes, hours, days; seconds = time % 60; time /= 60; minutes = time % 60; time /= 60; hours = time % 24; time /= 24; days = time; snprintf(buf, buf_size, "%lu d %lu h %lu min %lu s", days, hours, minutes, seconds); return buf; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void tffs_panic_log_register_spi(void) { tffs_spi_mode = 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int calc_sum_and_correct(unsigned char *buf, int buf_len, int correct) { unsigned int i, sum = 0; for(i = 0; i < buf_len; i++) { if(correct && (buf[i] == 0)){ buf[i] = ' '; /*--- correct buffer (support null-teminated string) ---*/ } sum += buf[i]; } return sum ^ (buf_len << 16); } #define MAX_LOG_IDS 4 /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int map_log_id_to_index(unsigned int id) { switch(id) { case FLASH_FS_ID_PANIC_LOG: return 0x0; case FLASH_FS_ID_PANIC2_LOG: return 0x1; case FLASH_FS_ID_CRASH_LOG: return 0x2; case FLASH_FS_ID_CRASH2_LOG: return 0x3; } return MAX_LOG_IDS; } static unsigned int log_read_multiple[MAX_LOG_IDS]; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static ssize_t tffs_panic_log_read(unsigned char *buf, int buf_len, unsigned int id) { struct tffs_fops_handle *handle; loff_t offp = 0; ssize_t read; handle = tffs_open_kern(id, 0); if(IS_ERR_OR_NULL(handle)) { printk(KERN_ERR"%s tffs_open_kern failed\n", __func__); return -EINVAL; } read = tffs_read_kern(handle, buf, buf_len, &offp); /*--- printk(KERN_INFO"%s tffs_read ret=%d\n", __func__, ret); ---*/ tffs_release_kern(handle); return read; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int paniclog_show(struct seq_file *seq, void *data __attribute__((unused))) { /*--- printk(KERN_INFO"%s %p %p\n", __func__, seq->private, data); ---*/ if(seq->private) { seq_printf(seq, "%s", (char *)seq->private); } return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _time_checksum { unsigned int cs; unsigned int ts; /* timestamp on read */ #define READ_BY_CR 0x1 #define READ_BY_SD 0x2 #define FIRST_READ_BY_SD 0x4 /* first read by SD */ unsigned int readmask; }; #define SKIP_UNTIL_CHAR(p, sign) while(*p && (*p != sign)) p++ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline unsigned COUNT_UNTIL_CHAR(const char *p, unsigned char sign, unsigned char count_sign) { unsigned int count = 0; while(*p && (*p != sign)) { if(*p == count_sign) { count++; } p++; } return count; } /*--------------------------------------------------------------------------------*\ * return: gefuelltes checksum * [id]cs,ts,readmask,[id2]cs,ts,readmask \*--------------------------------------------------------------------------------*/ void parse_crash_env(const char *crashp, struct _time_checksum checksum[]) { memset(checksum, 0, MAX_LOG_IDS * sizeof(struct _time_checksum)); while(*crashp) { unsigned int id, val, val1, val2; if(*crashp == '[') { sscanf(++crashp,"%x]%x,%x,%x", &id, &val, &val1, &val2); if(id < MAX_LOG_IDS) { unsigned int count = COUNT_UNTIL_CHAR(crashp, '[', ','); checksum[id].cs = val; checksum[id].ts = val1; if(count >= 1) { checksum[id].ts = val1; } if(count >= 2) { checksum[id].readmask = val2; } /*--- printk(KERN_INFO"%s: %d(count=%d): %x %x %x\n", __func__, id, count, checksum[id].cs, checksum[id].ts,checksum[id].readmask); ---*/ } SKIP_UNTIL_CHAR(crashp, '['); } else { return; } } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void set_crash_env(char *crashp, int len, struct _time_checksum checksum[]) { unsigned int written, i; for(i = 0; i < MAX_LOG_IDS; i++) { if(len > 0) { written = snprintf(crashp, len, "[%x]%x,%x,%x", i, checksum[i].cs, checksum[i].ts, checksum[i].readmask); len -= written, crashp += written; } } } #define snprintf_add(ptxt, txtlen, args...) { int local_add_len;\ if((local_add_len = snprintf(ptxt, txtlen, args)) > 0) { \ int tail = min((int)txtlen, local_add_len); \ (ptxt) += tail, (txtlen) -= tail; \ } \ } /*--------------------------------------------------------------------------------*\ * report_type: READ_BY_SD Support-Data * READ_BY_CR CrashReport \*--------------------------------------------------------------------------------*/ static int __log_proc_open(struct inode *inode __attribute__((unused)), struct file *file, unsigned int report_type, int id) { char crashenv[128]; char log[386], *plog; int log_len; char timebuf[128]; const char *timestart = ""; int len = (0x1 << CONFIG_LOG_BUF_SHIFT); int buf_len = len - sizeof(timebuf) - sizeof(log); /*--- ReserveTimestamp ---*/ struct _time_checksum saved_checksum[MAX_LOG_IDS]; unsigned int new_read_mask = 0; unsigned int map_idx = map_log_id_to_index(id); char *buf = NULL; struct tffs_core_handle *handle; unsigned int crashreport = (report_type == READ_BY_CR) ? 1 : 0; int result; if(down_interruptible(&log_sema) < 0) { /* exit bei signal */ return 0; } /*--- printk(KERN_ERR"%s: id=%d map_idx=%x %s\n", __func__, id, map_idx, crashreport ? "cr" : "sd"); ---*/ if(map_idx >= MAX_LOG_IDS) { /*--- printk("%s: error: id=%d too big\n", __func__, map_idx); ---*/ up(&log_sema); return -ENODATA; } /*--- printk(KERN_ERR"%s: id=%d map_idx=%x %s f:_flags=%x\n", __func__, id, map_idx, crashreport ? "cr" : "sd", file->f_flags); ---*/ if(file->f_flags & O_WRONLY) { unsigned int mapvalue; mapvalue = report_type; mapvalue |= map_idx << 8; file->private_data = (void *)mapvalue; up(&log_sema); return 0; } memset(saved_checksum, 0, sizeof(saved_checksum)); buf = vmalloc(len); if(buf == NULL) { result = -ENOMEM; goto err_out; } len = sizeof(crashenv); crashenv[0] = 0; handle = TFFS3_Open(FLASH_FS_CRASH, tffs3_mode_read); if(IS_ERR_OR_NULL(handle)){ result = -ENODEV; goto err_out; } log[0] = 0; plog = log; log_len = sizeof(log); result = TFFS3_Read(handle, crashenv, &len); TFFS3_Close(handle); if(result == 0){ parse_crash_env(crashenv, &saved_checksum[0]); snprintf_add(plog, log_len, "crashreport: read crash-variable '%s' \n", crashenv); } else { snprintf_add(plog, log_len, "crashreport: FLASH_FS_CRASH: TFFS3_Read() -> result=%d \n", result); } if(result == -EBUSY) { goto err_out; } buf[0] = 0; /*--- nmi-wa-check: ---*/ len = tffs_read_dummy_log(buf, buf_len, id); if (len <= 0) { len = tffs_panic_log_read(buf, buf_len, id); } if (len > 0) { unsigned int new_sum = calc_sum_and_correct(buf, len, 1); /*--- printk(KERN_INFO"%s: sum=%x len=%d\n", __func__, new_sum, len); ---*/ #if 1 snprintf_add(plog, log_len, "crashreport: old parsed checksum[%u].cs = %08x new_sum=%08x (new_len=%u)\n", map_idx, saved_checksum[map_idx].cs, new_sum, len); #endif if(new_sum != saved_checksum[map_idx].cs) { /*--- new panic-log: set struct ---*/ new_read_mask = crashreport ? READ_BY_CR : (READ_BY_SD | FIRST_READ_BY_SD); saved_checksum[map_idx].readmask = 0; saved_checksum[map_idx].ts = get_act_time(); saved_checksum[map_idx].cs = new_sum; } else { new_read_mask = crashreport ? READ_BY_CR : READ_BY_SD; } } else { len = 0; } #if defined(CONFIG_MACH_AR934x) || defined(CONFIG_MACH_QCA953x) if (id == FLASH_FS_ID_PANIC_LOG) { unsigned int sram_len = ath_restore_log_from_ram(buf, buf_len); if(sram_len) { unsigned int new_sram_sum; map_idx = 1; /*--- ... and prefer sram-log---*/ len = sram_len; new_sram_sum = calc_sum_and_correct(buf, len, 1); /*--- printk(KERN_INFO"%s: prefer sram-trace len=%d", __func__, sram_len); ---*/ if(new_sram_sum != saved_checksum[map_idx].cs) { struct timeval now; /*--- new sram-panic-log: clear counter ---*/ new_read_mask = crashreport ? READ_BY_CR : (READ_BY_SD | FIRST_READ_BY_SD); saved_checksum[map_idx].readmask = 0; saved_checksum[map_idx].ts = get_act_time(); saved_checksum[map_idx].cs = new_sram_sum; } else { new_read_mask = crashreport ? READ_BY_CR : READ_BY_SD; } } } #endif/*--- #if defined(CONFIG_MACH_AR934x) ---*/ if ((len == 0) || (((saved_checksum[map_idx].readmask & new_read_mask) == new_read_mask) && !((log_read_multiple[map_idx] & new_read_mask) == new_read_mask))) { /*--- check if sent ---*/ result = -ENODATA; goto err_out; } saved_checksum[map_idx].readmask |= new_read_mask; if(saved_checksum[map_idx].ts){ timestart = time_to_ascii(saved_checksum[map_idx].ts, timebuf, sizeof(timebuf), (saved_checksum[map_idx].readmask & FIRST_READ_BY_SD) ? "by support data" : "by crash report"); } set_crash_env(crashenv, sizeof(crashenv), saved_checksum); /*--- printk(KERN_INFO"%s: '%s'\n", __func__, crashenv); ---*/ handle = TFFS3_Open(FLASH_FS_CRASH, tffs3_mode_write); if (IS_ERR_OR_NULL(handle)){ result = -ENODEV; goto err_out; } result = TFFS3_Write(handle, crashenv, strlen(crashenv) + 1, 1); TFFS3_Close(handle); if (result < 0) { snprintf_add(plog, log_len, "crashreport: FLASH_FS_CRASH: TFFS3_Write() -> result=%d \n", result); } else { snprintf_add(plog, log_len, "crashreport: crash-variable set to '%s' \n", crashenv); } sprintf(&buf[len], "-----\n%s(first) sent on: %s\n", log, timestart); result = single_open(file, paniclog_show, buf); up(&log_sema); return result; err_out: if (buf != NULL){ vfree(buf); } up(&log_sema); return result; } #define __generic_proc_log_open(name, type, id) static int name(struct inode *inode, struct file *file) { return __log_proc_open(inode, file, type, id); } /*--- Crashreports ---*/ __generic_proc_log_open(paniclog_open_cr, READ_BY_CR, FLASH_FS_ID_PANIC_LOG); __generic_proc_log_open(crashlog_open_cr, READ_BY_CR, FLASH_FS_ID_CRASH_LOG); /*--- Supportdata ---*/ __generic_proc_log_open(paniclog_open_sd, READ_BY_SD, FLASH_FS_ID_PANIC_LOG); __generic_proc_log_open(crashlog_open_sd, READ_BY_SD, FLASH_FS_ID_CRASH_LOG); /*--- Crashreports ---*/ __generic_proc_log_open(panic2log_open_cr, READ_BY_CR, FLASH_FS_ID_PANIC2_LOG); __generic_proc_log_open(crash2log_open_cr, READ_BY_CR, FLASH_FS_ID_CRASH2_LOG); /*--- Supportdata ---*/ __generic_proc_log_open(panic2log_open_sd, READ_BY_SD, FLASH_FS_ID_PANIC2_LOG); __generic_proc_log_open(crash2log_open_sd, READ_BY_SD, FLASH_FS_ID_CRASH2_LOG); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static ssize_t log_proc_write(struct file *file, const char *buffer, size_t count, loff_t *offset __maybe_unused) { unsigned char procfs_buffer[64]; size_t procfs_buffer_size; unsigned int id, report; /*--- von wo kommen wir: ---*/ id = ((unsigned int) file->private_data) >> 8; report = ((unsigned int)file->private_data) & 0xFF; if (id >= MAX_LOG_IDS) { return count; } if (count >= sizeof(procfs_buffer)) { procfs_buffer_size = sizeof(procfs_buffer) - 1; } else { procfs_buffer_size = count; } /* write data to the buffer */ if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size - 1) ) { return -EFAULT; } procfs_buffer[procfs_buffer_size] = 0; if (strstr(procfs_buffer, "readmultiple")) { log_read_multiple[id] |= report; } else if (strstr(procfs_buffer, "readonce")) { log_read_multiple[id] &= ~report; #if defined(avm_rebootcounter_string) } else if (strstr(procfs_buffer, "clear_rebootcounter")) { avm_rebootcounter_reset(); #endif/*--- #if defined(avm_rebootcounter_string) ---*/ } else { printk(KERN_INFO"unknown option: use readmultiple, readonce" #if defined(avm_rebootcounter_string) ", clear_rebootcounter" #endif/*--- #if defined(avm_rebootcounter_string) ---*/ "\n"); } /*--- printk(KERN_INFO"%s: '%s' %p log_read_multiple[%d]=%x (report=%x)\n", __func__, procfs_buffer, file->private_data, id, log_read_multiple[id], report); ---*/ return count; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int log_proc_release(struct inode *inode, struct file *file) { if(!(file->f_flags & O_WRONLY)) { struct seq_file *seq = file->private_data; vfree(seq->private); seq->private = NULL; /*--- printk(KERN_INFO"%s: free after read\n", __func__); ---*/ return single_release(inode, file); /*--- gibt txtbuf frei ----*/ } /*--- printk(KERN_INFO"%s no free\n", __func__); ---*/ return 0; } #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) #define _FLASH_FS_ID_CRASH_LOG FLASH_FS_ID_CRASH_LOG2 #else/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ #define _FLASH_FS_ID_CRASH_LOG FLASH_FS_ID_CRASH_LOG #endif/*--- #else ---*//*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ #define CRASHLOG_BUFSIZE (1 << CONFIG_LOG_BUF_SHIFT) struct _crashlog_write_priv { unsigned int id; unsigned int buf_size; unsigned int index; char *buf; }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int crashlog_open_write(struct inode *inode __attribute__((unused)), struct file *file) { struct _crashlog_write_priv *pclog_w; struct tffs_core_handle *handle = NULL; struct _time_checksum saved_checksum[MAX_LOG_IDS]; char crashenv[128]; int result = 0, len; unsigned int map_idx; if (!(file->f_flags & O_WRONLY)) { return -EPERM; } if (down_interruptible(&log_sema) < 0) { /* exit bei signal */ return 0; } pclog_w = vmalloc(sizeof(struct _crashlog_write_priv) + CRASHLOG_BUFSIZE); if (pclog_w == NULL) { result = -ENOMEM; goto err_out; } pclog_w->buf = (char *)(pclog_w + 1); pclog_w->index = 0; pclog_w->buf_size = CRASHLOG_BUFSIZE; pclog_w->id = _FLASH_FS_ID_CRASH_LOG; map_idx = map_log_id_to_index(pclog_w->id); if (map_idx > MAX_LOG_IDS) { result = -EINVAL; goto err_out; } memset(saved_checksum, 0, sizeof(saved_checksum)); len = sizeof(crashenv); crashenv[0] = 0; handle = TFFS3_Open(FLASH_FS_CRASH, tffs3_mode_read); if (IS_ERR_OR_NULL(handle)){ result = -ENODEV; goto err_out; } result = TFFS3_Read(handle, crashenv, &len); TFFS3_Close(handle); if(result == 0){ parse_crash_env(crashenv, &saved_checksum[0]); } /*--- printk(KERN_INFO"%s: crash crashenv='%s' map_idx=%u readmask=%x\n", __func__, crashenv, map_idx, saved_checksum[map_idx].readmask); ---*/ if ((saved_checksum[map_idx].readmask & READ_BY_CR) == 0) { /*--- printk(KERN_INFO"%s: readmask=%x - append\n", __func__, saved_checksum[map_idx].readmask); ---*/ if ((len = tffs_panic_log_read(pclog_w->buf, pclog_w->buf_size, pclog_w->id)) < 0) { len = 0; } } else { saved_checksum[map_idx].ts = 0; saved_checksum[map_idx].cs = 0; saved_checksum[map_idx].readmask = 0; set_crash_env(crashenv, sizeof(crashenv), saved_checksum); /*--- printk(KERN_INFO"%s: new crashenv '%s'\n", __func__, crashenv); ---*/ handle = TFFS3_Open(FLASH_FS_CRASH, tffs3_mode_write); if (IS_ERR_OR_NULL(handle)){ result = -ENODEV; goto err_out; } TFFS3_Write(handle, crashenv, strlen(crashenv) + 1, 1); TFFS3_Close(handle); len = 0; } pclog_w->index = len; file->private_data = pclog_w; /*--- printk(KERN_INFO"%s: done - index=%u\n", __func__, len); ---*/ up(&log_sema); return 0; err_out: if (pclog_w) { vfree(pclog_w); } up(&log_sema); return result; } /*--------------------------------------------------------------------------------*\ * ret: newlen * skippe an Zeilengrenzen \*--------------------------------------------------------------------------------*/ static unsigned int nice_skip(char *buf, unsigned int *skip, unsigned int len) { unsigned int new_len; while((len > *skip) && (buf[*skip] != '\n')) *skip += 1; if ((len > *skip) && (buf[*skip] == '\n')) *skip += 1; new_len = len - *skip; memmove(buf, &buf[*skip], new_len); return new_len; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static ssize_t crashlog_write(struct file *file, const char *buffer, size_t count, loff_t *offset __maybe_unused) { const char *src; struct _crashlog_write_priv *pclog_w = (struct _crashlog_write_priv *)file->private_data; unsigned int free_place, skip; if (count > pclog_w->buf_size) { skip = count - pclog_w->buf_size; count = pclog_w->buf_size; src = &buffer[skip]; } else { src = buffer; } free_place = (pclog_w->buf_size - pclog_w->index); /*--- printk(KERN_INFO"%s: index=%u -> free_place=%u count=%u\n", __func__, pclog_w->index, free_place, count); ---*/ if (count > free_place) { skip = count - free_place; pclog_w->index = nice_skip(pclog_w->buf, &skip, pclog_w->index); /*--- printk(KERN_INFO"%s: skipped: %u index=%u\n", __func__, skip, pclog_w->index); ---*/ } if (copy_from_user(&pclog_w->buf[pclog_w->index], src, count)) { return -EFAULT; } pclog_w->index += count; return count; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int crashlog_close_write(struct inode *inode, struct file *file) { size_t written; loff_t off __attribute__((unused)); struct _crashlog_write_priv *pclog_w = (struct _crashlog_write_priv *)file->private_data; struct tffs_fops_handle *handle; /*--- printk(KERN_INFO"%s: idx=%u\n", __func__, pclog_w->index); ---*/ handle = tffs_open_kern(pclog_w->id, 1); if (IS_ERR_OR_NULL(handle)){ /*--- printk(KERN_ERR"[%s] tffs_open_kern() failed\n", __func__); ---*/ vfree(pclog_w); return (handle == NULL) ? -ENOMEM : PTR_ERR(handle); } written = tffs_write_kern(handle, pclog_w->buf, pclog_w->index, &off); if (written != pclog_w->index) { /*--- printk(KERN_ERR"[%s] tffs_release_kern() failed %d\n", __func__, written); ---*/ } tffs_release_kern(handle); vfree(pclog_w); return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _logproc { const char *dir; const char *name; const unsigned int mode; const struct file_operations fops; struct proc_dir_entry *dir_entry; } logproc[] = { { dir: NULL, name: "avm/crash.log", mode: 0222, fops: {open: crashlog_open_write, write: crashlog_write, read: NULL, llseek: NULL, release: crashlog_close_write}}, { dir: "avm/log_cr", name: "panic", mode: 0666, fops: {open: paniclog_open_cr, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_cr", name: "crash", mode: 0666, fops: {open: crashlog_open_cr, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_cr", name: "panic2", mode: 0666, fops: {open: panic2log_open_cr, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_cr", name: "crash2", mode: 0666, fops: {open: crash2log_open_cr, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_sd", name: "panic", mode: 0666, fops: {open: paniclog_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_sd", name: "crash", mode: 0666, fops: {open: crashlog_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_sd", name: "panic2", mode: 0666, fops: {open: panic2log_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_sd", name: "crash2", mode: 0666, fops: {open: crash2log_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int __init prepare_log_proc(void) { struct proc_dir_entry *dir_entry = NULL; const char *lastdir = ""; unsigned int i; /*--- printk(KERN_INFO"%s\n", __func__); ---*/ for(i = 0; i < ARRAY_SIZE(logproc); i++) { if (logproc[i].dir && strcmp(lastdir, logproc[i].dir)) { dir_entry = proc_mkdir(logproc[i].dir, NULL); lastdir = logproc[i].dir; } #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) if (logproc[i].dir == NULL) { if (!proc_create(logproc[i].name, logproc[i].mode, NULL, &logproc[i].fops)) { printk(KERN_ERR"%s can't proc_create(%s)\n", __func__, logproc[i].name); } } else if (dir_entry) { logproc[i].dir_entry = dir_entry; if (!proc_create(logproc[i].name, logproc[i].mode, dir_entry, &logproc[i].fops)) { printk(KERN_ERR"%s can't proc_create(%s)\n", __func__, logproc[i].name); } } #endif/*--- #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ } return 0; } late_initcall(prepare_log_proc); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void print_to_console(const char *str, int len) { char txt[512 + 1]; while(len) { int i, tail = min(len, (int)(sizeof(txt) - 1)); /*--- Zeilenweise kopieren - sonst unschoen formatiert ---*/ for(i = 0; i < tail; i++) { if (str[i] == '\n') { tail = i + 1; break; } } memcpy(txt, str, tail); str += tail, len -= tail; txt[tail] = 0; printk(KERN_CONT"%s", txt); #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif } } /** * minimal panic-log: only for atheros without correct nmi because freezer occur */ static int tffs_read_dummy_log(char *buf, int buf_len, unsigned int id) { int len; /*--- support only for panic-log ---*/ if (id != FLASH_FS_ID_PANIC_LOG) { return 0; } len = strlen(panic_dummy_log); if(len == 0) { return 0; } len = min(len, buf_len); memcpy(buf, panic_dummy_log, len); return len; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void tffs_panic_log_printkbuf(struct kmsg_dumper *dumper) { if (tffs_panic_log_suppress == 0) { size_t towrite; struct timespec uptime; char htime[32], huptime[128]; char reboot_txt[128], *reboot_post_txt; char time_stamp_buf[sizeof("UPTIME: \n") + sizeof(htime) + sizeof(huptime) + 10 + 128 + sizeof(reboot_txt) + 32 + sizeof("\n( - panic on )\nPANIC LOG VERSION 2.1\n")]; static char panic_log_buf[1 << CONFIG_LOG_BUF_SHIFT]; unsigned long time_stamp_buf_len; char *fw_version; char *hw_version; char *hw_subversion; tffs_panic_log_suppress = 1; mb(); tffs_panic_log_open(); get_monotonic_boottime(&uptime); reboot_post_txt = ""; reboot_txt[0] = 0; #if defined(avm_rebootcounter_string) avm_rebootcounter_string(reboot_txt, sizeof(reboot_txt)); #endif/*--- #else ---*//*--- #if defined(avm_rebootcounter_string) ---*/ if (reboot_txt[0]) { reboot_post_txt = "(since last regular reboot/power-cut)"; } if ((fw_version = prom_getenv("firmware_info")) == NULL) { fw_version = ""; } if ((hw_version = prom_getenv("HWRevision")) == NULL) { hw_version = "?"; } if ((hw_subversion = prom_getenv("HWSubRevision")) == NULL) { hw_subversion = "0"; } time_stamp_buf_len = snprintf(time_stamp_buf, sizeof(time_stamp_buf), "UPTIME: %lu\n(%s - panic on %s)\n" "%s %s\n" "HW: %s.%s\nFW: %s\n" "PANIC LOG VERSION 2.1\n", (unsigned long)uptime.tv_sec, human_readable_time(huptime, sizeof(huptime), (unsigned long)uptime.tv_sec), time_to_ascii((unsigned long)get_act_time(), htime, sizeof(htime), ""), reboot_txt, reboot_post_txt, hw_version, hw_subversion, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) avm_kernel_version_info ? avm_kernel_version_info->firmwarestring : #endif fw_version ); tffs_panic_log_write(time_stamp_buf, time_stamp_buf_len); #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif if (kmsg_dump_get_buffer(dumper, 0, panic_log_buf, sizeof(panic_log_buf), &towrite) == false) { towrite = 0; } #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif if (towrite > 0) { tffs_panic_log_write(panic_log_buf, towrite); } tffs_panic_log_close(); /*--- Entwickler wollen alles auch noch auf der Konsole sehen ---*/ printk(KERN_ERR"\n\n------------------- Last part of Panic-log Content: -------------------\n\n"); { unsigned int log_symbols = 2 /* Sekunden */ * 115000 / 10 ; unsigned int offset = 0; if (towrite > log_symbols) { offset = towrite - log_symbols; towrite = log_symbols; } else { offset = 0; } print_to_console(&panic_log_buf[offset], towrite); } } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void panic_log(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason){ static bool panicking = false; static DEFINE_SPINLOCK(lock); unsigned long flags; /*--- printk_linux("%s(%d)\n", __func__, reason); ---*/ switch (reason) { case KMSG_DUMP_RESTART: case KMSG_DUMP_HALT: case KMSG_DUMP_POWEROFF: /* These are almost always orderly shutdowns. */ return; case KMSG_DUMP_OOPS: case KMSG_DUMP_PANIC: panicking = true; break; case KMSG_DUMP_EMERG: if (panicking) /* Panic report already captured. */ return; break; default: printk(KERN_ERR"%s: ignoring unrecognized KMSG_DUMP_* reason %d\n", __func__, (int) reason); return; } if (!spin_trylock_irqsave(&lock, flags)) { return; } tffs_panic_log_printkbuf(dumper); spin_unlock_irqrestore(&lock, flags); } /** * minimal panic-log: only for atheros without correct nmi because freezer occur */ void TFFS3_panic_dummy_log(const char *txt){ char reboot_txt[128], *reboot_post_txt; char *fw_version; char *hw_version; char *hw_subversion; unsigned char uuid[16]; reboot_post_txt = ""; reboot_txt[0] = 0; #if defined(avm_rebootcounter_string) avm_rebootcounter_string(reboot_txt, sizeof(reboot_txt)); #endif/*--- #else ---*//*--- #if defined(avm_rebootcounter_string) ---*/ if (reboot_txt[0]) { reboot_post_txt = "(since last regular reboot/power-cut)"; } if ((fw_version = prom_getenv("firmware_info")) == NULL) { fw_version = ""; } if ((hw_version = prom_getenv("HWRevision")) == NULL) { hw_version = "?"; } if ((hw_subversion = prom_getenv("HWSubRevision")) == NULL) { hw_subversion = "0"; } generate_random_uuid(uuid); snprintf(panic_dummy_log, sizeof(panic_dummy_log), "UPTIME: 0\n(0)\n" "%s %s\n" "HW: %s.%s\nFW: %s\n" "PANIC LOG VERSION 2.1\n%s\n%16ph\n", reboot_txt, reboot_post_txt, hw_version, hw_subversion, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) avm_kernel_version_info ? avm_kernel_version_info->firmwarestring : #endif fw_version, txt, uuid ); } #else /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void tffs_panic_log_printkbuf(struct kmsg_dumper *dumper, const char *s1, unsigned long l1, const char *s2, unsigned long l2) { if (tffs_panic_log_suppress == 0) { struct timespec uptime; char htime[32], huptime[128]; char time_stamp_buf[sizeof(" UPTIME: \n") + sizeof(htime) + sizeof(huptime) + 10 + 128 + sizeof("\n( - panic on )\nPANIC LOG VERSION 2.0\n")]; unsigned long time_stamp_buf_len; tffs_panic_log_suppress = 1; mb(); tffs_panic_log_open(); get_monotonic_boottime(&uptime); time_stamp_buf_len = snprintf(time_stamp_buf, sizeof(time_stamp_buf), "UPTIME: %lu\n(%s - panic on %s)\nPANIC LOG VERSION 2.0\n", (unsigned long)uptime.tv_sec, human_readable_time(huptime, sizeof(huptime), (unsigned long)uptime.tv_sec), time_to_ascii((unsigned long)get_act_time(), htime, sizeof(htime), "") ); #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif tffs_panic_log_write(time_stamp_buf, time_stamp_buf_len); #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif if (l1 > 0){ tffs_panic_log_write(s1, l1); } #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif if (l2 > 0) { tffs_panic_log_write(s2, l2); } tffs_panic_log_close(); /*--- Entwickler wollen alles auch noch auf der Konsole sehen ---*/ printk(KERN_ERR"\n\n------------------- Last part of Panic-log Content: -------------------\n\n"); { unsigned int log_symbols = 2 /* Sekunden */ * 115000 / 10 ; if (l2 > log_symbols) { s2 = &s2[l2 - log_symbols]; l2 = log_symbols; print_to_console(s2, l2); } else { if (l2 > 0) { log_symbols -= l2; } if (l1 > 0) { if (l1 > log_symbols) { s1 = &s1[l1 - log_symbols]; l1 = log_symbols; print_to_console(s1, l1); } } if (l2 > 0) { print_to_console(s2, l2); } } } } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void panic_log(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, const char *s1, unsigned long l1, const char *s2, unsigned long l2) { static bool panicking = false; static DEFINE_SPINLOCK(lock); unsigned long flags; /*--- printk_linux("%s(%d)\n", __func__, reason); ---*/ switch (reason) { case KMSG_DUMP_RESTART: case KMSG_DUMP_HALT: case KMSG_DUMP_POWEROFF: /* These are almost always orderly shutdowns. */ return; case KMSG_DUMP_OOPS: case KMSG_DUMP_PANIC: panicking = true; break; case KMSG_DUMP_EMERG: if (panicking) /* Panic report already captured. */ return; break; default: printk(KERN_ERR"%s: ignoring unrecognized KMSG_DUMP_* reason %d\n", __func__, (int) reason); return; } if (!spin_trylock_irqsave(&lock, flags)) { return; } tffs_panic_log_printkbuf(dumper, s1, l1, s2, l2); spin_unlock_irqrestore(&lock, flags); } #endif // LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct kmsg_dumper panic_log_dumper = { .dump = panic_log }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int __init install_panic_log_dumper(void) { /*--- printk("%s\n", __func__); ---*/ return kmsg_dump_register(&panic_log_dumper); } late_initcall(install_panic_log_dumper);