/*------------------------------------------------------------------------------------------*\ * * 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 #if defined(CONFIG_PROC_FS) #include #include #include #include #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ #include "tffs_local.h" #define ARRAY_EL(a) (sizeof(a) / sizeof(a[0])) #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) ---*/ unsigned int tffs3_spi_mode = 0; static struct _tffs_open *panic_handle = NULL; /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ void tffs3_panic_log_open(void) { if(panic_handle == NULL){ panic_handle = tffs3_open_panic(); } } EXPORT_SYMBOL(tffs3_panic_log_open); /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ void tffs3_panic_log_write(char *buffer, unsigned int len) { static loff_t off __attribute__((unused)); if(panic_handle != NULL){ #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) avm_event_send_log(remote_panic, 1, buffer, len); #else /*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ tffs3_write_kern(panic_handle, buffer, len, &off); #endif/*--- #else ---*//*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ } } EXPORT_SYMBOL(tffs3_panic_log_write); /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ void tffs3_panic_log_close(void) { if(panic_handle != NULL){ #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) printk("%s wait on reboot from host-cpu\n", __func__); for(;;) ; /*--- ... der arm soll booten - ansonsten auf Watchdog warten ---*/ #else/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ tffs3_release_kern(panic_handle); panic_handle = NULL; #endif/*--- #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ } } EXPORT_SYMBOL(tffs3_panic_log_close); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void tffs3_panic_log_register_spi(void) { tffs3_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); } #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) static void *remote_log_event_sink_handle; /*--------------------------------------------------------------------------------*\ * Schreiben eines Remote-Logfiles \*--------------------------------------------------------------------------------*/ void tffs3_write_remote_logfile(unsigned int tffs_id, char *buf, int len) { struct _tffs_open *open_data; loff_t offp = 0; int ret; struct file tffs_file; struct inode tffs_inode; memset(&tffs_file, 0, sizeof(tffs_file)); memset(&tffs_inode, 0, sizeof(tffs_inode)); tffs_file.f_flags = O_WRONLY; tffs_inode.i_rdev = MKDEV(MAJOR(tffs.device), tffs_id); ret = tffs3_open(&tffs_inode, &tffs_file); if(ret) { printk(KERN_ERR"%s tffs3_open failed ret=%d\n", __func__, ret); return; } open_data = (struct _tffs_open *)tffs_file.private_data; open_data->kernel_context = 1; /*--- write-access in kernel-context ---*/ tffs3_write(&tffs_file, buf, len, &offp); /*--- printk(KERN_INFO"%s tffs3_read ret=%d\n", __func__, ret); ---*/ tffs3_release(&tffs_inode, &tffs_file); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void remote_log_sink(void *private __attribute__((unused)), unsigned char *buf, unsigned int len) { unsigned char *logbufuncached; struct _avm_event_log event; if(len < sizeof(struct _avm_event_log)) { printk(KERN_ERR"%s: incompatible event len=%u sizeof=%u\n", __func__, len, sizeof(struct _avm_event_log)); return; } memcpy(&event, buf, min(len, sizeof(event))); if(event.event_header.id != avm_event_id_log) { printk(KERN_ERR"%s: incompatible event (id=%u)\n", __func__, event.event_header.id); return; } switch(event.logtype) { case local_panic: case local_crash: printk(KERN_ERR"%s: ignore logtype(%u)\n", __func__, event.logtype); break; case remote_panic: case remote_crash: { unsigned int cs; unsigned int tffs_id = (event.logtype == remote_crash) ? FLASH_FS_ID_CRASH2_LOG : FLASH_FS_ID_PANIC2_LOG; logbufuncached = (unsigned char *) ioremap_nocache((unsigned int)event.logpointer + ARM_MEM_OFFSET, event.loglen); #if 0 printk(KERN_ERR"%s: logtype(%u) loglen=%u logpointer=%x(%p) cs=%x rf=%x\n", __func__, event.logtype, event.loglen, event.logpointer, logbufuncached, event.checksum, event.rebootflag ); #endif cs = calc_sum_and_correct(logbufuncached, event.loglen, 0); if(cs != event.checksum) { printk(KERN_ERR"%s: obscure checksum %x != %x -> ignore\n", __func__, event.checksum, cs); iounmap(logbufuncached); return; } tffs3_write_remote_logfile(tffs_id, logbufuncached, event.loglen); /*--- printk(KERN_ERR"%s: print '%s'\n", __func__, logbufuncached); ---*/ iounmap(logbufuncached); if(event.rebootflag & 0x1) { restore_printk(); printk(KERN_ERR"\n\nREMOTE-log with rebootflag received -> machine_restart() in 5 s\n\n"); schedule_timeout(5 * HZ); machine_restart(NULL); } break; } default: printk(KERN_ERR"%s: invalid logtype(%u)\n", __func__, event.logtype); break; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void remote_log_register_sink(void *private __attribute__((unused)), unsigned int param1 __attribute__((unused)), unsigned int param2 __attribute__((unused))) { if(remote_log_event_sink_handle) { return; } remote_log_event_sink_handle = avm_event_sink_register("log_remote_sink", (1ULL << avm_event_id_log) | 0, remote_log_sink, NULL ); if(remote_log_event_sink_handle == NULL) { printk(KERN_ERR"%s not registered\n", __func__); } printk(KERN_ERR"%s registered\n", __func__); return; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int remote_log_init(void) { if(tffs2_active()){ pr_info("[%s] TFFS version 2 running, not starting up version 3\n", __func__); return 0; } return avm_event_node_established(remote_log_register_sink, NULL, 0, 0); } late_initcall(remote_log_init); #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ #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 ssize_t tffs3_panic_log_read(unsigned char *buf, int buf_len, unsigned int id) { struct _tffs_open *handle; loff_t offp = 0; ssize_t read; handle = tffs3_open_kern(id, 0); if(handle == NULL) { printk(KERN_ERR"%s tffs3_open_kern failed\n", __func__); return -EINVAL; } read = tffs3_read_kern(handle, buf, buf_len, &offp); /*--- printk(KERN_INFO"%s tffs3_read ret=%d\n", __func__, ret); ---*/ tffs3_release_kern(handle); return read; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *time_to_ascii(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; struct timeval now; unsigned int local_time; do_gettimeofday(&now); /*--- local_time = (u32)(now.tv_sec - (sys_tz.tz_minuteswest * 60)); ---*//*--- tz_minuteswest == 0 ? ---*/ local_time = (u32)(now.tv_sec); rtc_time_to_tm(local_time, &tm); snprintf(buf, len, "%s %s %d %d:%d:%d %d GMT %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; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ 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; } /*--------------------------------------------------------------------------------*\ * return: Pointer auf Zeitstempel, gefuelltes checksum, read_once * * 2 Formate werden unterstuezt: * alt: panic_cs, panic_sram_cs, read_once_cr, time * neu: [id]cs,[id2]cs,.., read_once_cr; time \*--------------------------------------------------------------------------------*/ const char *parse_crash_env3(const char *crashp, unsigned int checksum[], unsigned int *read_once) { memset(checksum, 0, MAX_LOG_IDS * sizeof(unsigned int)); *read_once = 0; if(*crashp == 0) { return crashp; } if(*crashp != '[') { /*--- altes Format: ---*/ sscanf(crashp, "0x%x,0x%x,0x%x;", &checksum[0], &checksum[1], read_once); while(*crashp && (*crashp != ';')) crashp++; if(*crashp == ';') crashp++; return crashp; } while(*crashp) { unsigned int id, val; if(*crashp == '[') { sscanf(++crashp,"%x]%x,", &id,&val); if(id < MAX_LOG_IDS) { checksum[id] = val; } while(*crashp && (*crashp != ',')) crashp++; if(*crashp == ',') crashp++; } else { sscanf(crashp,"%x,", read_once); while(*crashp && (*crashp != ';')) crashp++; if(*crashp == ';') crashp++; return crashp; } } return crashp; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void set_crash_env(char *crashp, int len, unsigned int checksum[], unsigned int read_once, const char *timestart) { unsigned int written, i; for(i = 0; i < MAX_LOG_IDS; i++) { if(len > 0) { written = snprintf(crashp, len, "[%x]%x,", i, checksum[i]); len -= written, crashp += written; } } if(len > 0){ snprintf(crashp, len, "%x;%s\n", read_once, timestart); } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int __log_proc_open(struct inode *inode __attribute__((unused)), struct file *file, unsigned int crashreport, int id) { char crashenv[128]; char timebuf[128]; const char *timestart = ""; int buf_len = (0x1 << 17) - 64; /*--- ReserveTimestamp ---*/ int len = buf_len; unsigned int tffs_once_cr = 0; unsigned int saved_checksum[MAX_LOG_IDS]; unsigned int map_idx = map_log_id_to_index(id); char *buf; struct _tffs_open *handle; int result; unsigned int new_sum; /*--- 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); ---*/ return -ENODATA; } buf = kmalloc(len, GFP_KERNEL); if(buf == NULL) { return -ENOMEM; } len = sizeof(crashenv); crashenv[0] = 0; handle = TFFS3_Open(tffs3_mode_read); if(handle == NULL){ result = -ENODEV; goto err_out; } result = TFFS3_Read(handle, FLASH_FS_CRASH, crashenv, &len); if(result == 0){ timestart = parse_crash_env3(crashenv, &saved_checksum[0], &tffs_once_cr); strlcpy(timebuf, timestart, sizeof(timebuf)); timestart = timebuf; /*--- printk(KERN_ERR"%s: cs[]=%x %x %x %x readonce=%x\n(%s)", __func__, saved_checksum[0], saved_checksum[1], saved_checksum[2], saved_checksum[3], tffs_once_cr, crashenv); ---*/ } else { memset(saved_checksum, 0, sizeof(saved_checksum)); } TFFS3_Close(handle); buf[0] = 0; len = tffs3_panic_log_read(buf, buf_len, id); if(len > 0){ new_sum = calc_sum_and_correct(buf, len, 1); /*--- printk(KERN_INFO"%s: sum=%x len=%d\n", __func__, new_sum, len); ---*/ if(new_sum != saved_checksum[map_idx]) { /*--- new panic-log: clear counter ---*/ tffs_once_cr &= ~(1 << map_idx); timestart = time_to_ascii(timebuf, sizeof(timebuf), crashreport ? "by crash report" : "by support data"); saved_checksum[map_idx] = new_sum; } } else { len = 0; } #if defined(CONFIG_MACH_AR934x) if(id == FLASH_FS_ID_PANIC_LOG) { unsigned int sram_len __attribute__((unused)); sram_len = ath_restore_log_from_ram(buf, buf_len); if(sram_len) { unsigned int new_sram_sum; if(crashreport) tffs_once_cr |= (1 << map_idx); /*--- mark flashfile as readed ... ---*/ 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]) { struct timeval now; /*--- new sram-panic-log: clear counter ---*/ tffs_once_cr &= ~(1 << map_idx); timestart = time_to_ascii(timebuf, sizeof(timebuf), crashreport ? "by crash report" : "by support data"); saved_checksum[map_idx] = new_sram_sum; } } } #endif/*--- #if defined(CONFIG_MACH_AR934x) ---*/ if((len == 0) || (crashreport && (tffs_once_cr & (1 << map_idx)))) { result = -ENODATA; goto err_out; } if(crashreport){ tffs_once_cr |= (1 << map_idx); } sprintf(&buf[len], "-----\n(first) sent on: %s\n", timestart); set_crash_env(crashenv, sizeof(crashenv), saved_checksum, tffs_once_cr, timestart); handle = TFFS3_Open(tffs3_mode_write); if(handle == NULL){ result = -ENODEV; goto err_out; } TFFS3_Write(handle, FLASH_FS_CRASH, crashenv, strlen(crashenv) + 1, 1); TFFS3_Close(handle); return single_open(file, paniclog_show, buf); err_out: if(buf != NULL){ kfree(buf); } 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, 1, FLASH_FS_ID_PANIC_LOG); __generic_proc_log_open(crashlog_open_cr, 1, FLASH_FS_ID_CRASH_LOG); /*--- Supportdata read every time possible ---*/ __generic_proc_log_open(paniclog_open_sd, 0, FLASH_FS_ID_PANIC_LOG); __generic_proc_log_open(crashlog_open_sd, 0, FLASH_FS_ID_CRASH_LOG); #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) /*--- Crashreports ---*/ __generic_proc_log_open(panic2log_open_cr, 1, FLASH_FS_ID_PANIC2_LOG); __generic_proc_log_open(crash2log_open_cr, 1, FLASH_FS_ID_CRASH2_LOG); /*--- Supportdata read every time possible ---*/ __generic_proc_log_open(panic2log_open_sd, 0, FLASH_FS_ID_PANIC2_LOG); __generic_proc_log_open(crash2log_open_sd, 0, FLASH_FS_ID_CRASH2_LOG); #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _logproc { const char *dir; const char *name; struct file_operations fops; struct proc_dir_entry *dir_entry; } logproc[] = { { dir: "avm/log_cr", name: "panic", fops: {open: paniclog_open_cr, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, { dir: "avm/log_cr", name: "crash", fops: {open: crashlog_open_cr, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) { dir: "avm/log_cr", name: "panic2", fops: {open: panic2log_open_cr, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, { dir: "avm/log_cr", name: "crash2", fops: {open: crash2log_open_cr, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ { dir: "avm/log_sd", name: "panic", fops: {open: paniclog_open_sd, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, { dir: "avm/log_sd", name: "crash", fops: {open: crashlog_open_sd, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) { dir: "avm/log_sd", name: "panic2", fops: {open: panic2log_open_sd, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, { dir: "avm/log_sd", name: "crash2", fops: {open: crash2log_open_sd, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ /*--- compatible to the "old" solution: ---*/ { dir: NULL, name: "avm_panic_cr", fops: {open: paniclog_open_cr, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, { dir: NULL, name: "avm_panic_sd", fops: {open: paniclog_open_sd, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int __init prepare_log_proc(void) { struct proc_dir_entry *dir_entry = NULL; const char *lastdir = ""; unsigned int i; if(tffs2_active()){ pr_info("[%s] TFFS version 2 running, not starting up version 3\n", __func__); return 0; } /*--- printk(KERN_ERR"%s\n", __FUNCTION__); ---*/ for(i = 0; i < ARRAY_EL(logproc); i++) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) logproc[i].fops.owner = THIS_MODULE; #endif/*--- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) ---*/ 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, 0444, 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, 0444, 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);