/*--------------------------------------------------------------------------------*\ * * Copyright (C) 2013 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 * * * * * Log printk-buffer and Reboot-Status to internal SRAM (wasp) * all other atheros-chipsets we use as small DRAM-Area to save Reboot-Status * * Author: mbahr@avm.de \*--------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SRAM_LOG_VERSION "SRAM LOG VERSION 2.0" /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *reboot_status[] = { #define RS_NMI_PON 0 "Power-On-Reboot", #define RS_SOFT_REBOOT 1 "Software-Reboot", #define RS_SOFT_WATCHDOG 2 "Software-NMI-Watchdog", #define RS_FIRMWARE_UPDATE 3 "Firmware-Update", #define RS_NMI_WA 4 "Software-NMI-Workaround", #define RS_BUS_ERROR 5 "Bus Error", }; #if defined(CONFIG_MACH_QCA953x) #define ATH_SRAM_PRINTKLEN (ATH_SRAM_SIZE_HBEE - 0x500) #define ATH_REBOOT_LOGBUFFER (ATH_SRAM_BASE_UNCACHED + ATH_SRAM_SIZE_HBEE - ATH_SRAM_PRINTKLEN) #elif defined(ATH_SRAM_SIZE) /*--- use internal sram, but not at the begin, because here maybe data from mni-workarround (aprox first 0x500 bytes) ---*/ #define ATH_SRAM_PRINTKLEN (ATH_SRAM_SIZE - 0x500) #define ATH_REBOOT_LOGBUFFER (ATH_SRAM_BASE_UNCACHED + ATH_SRAM_SIZE - ATH_SRAM_PRINTKLEN) #else/*--- #if defined(CONFIG_MACH_AR934x) ---*/ #undef ATH_SRAM_PRINTKLEN #endif/*--- #else ---*//*--- #if defined(ATH_SRAM_SIZE) ---*/ #if defined(ATH_SRAM_BASE_UNCACHED) #define ATH_DUMPREGISTER_MEMORY ATH_REBOOT_LOGBUFFER #endif/*--- #if defined(ATH_SRAM_BASE_UNCACHED) ---*/ #define MAGIC_PRINK_WORD 0x5352414D #define MAGIC_DUMPREGISTER_WORD 0x44554D50 #define MAGIC_REBOOT_STATUS_WORD 0x53544130 #define MAGIC_REBOOT_STATUS_WORD_SET(a) (((a) & 0xF) | MAGIC_REBOOT_STATUS_WORD) #define MAGIC_REBOOT_STATUS_WORD_CHECK(a) (((a) & ~0xF) == MAGIC_REBOOT_STATUS_WORD) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _ram_print_data { volatile unsigned int rebootstatus; volatile unsigned int magic_word; volatile unsigned int len; unsigned char data[1]; }; /*--------------------------------------------------------------------------------*\ * save log to internal SRAM \*--------------------------------------------------------------------------------*/ void ath_savelog_to_ram(void) { #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) char time_stamp_buf[sizeof("UPTIME: \n") + 10 + sizeof(SRAM_LOG_VERSION"\n")]; unsigned long time_stamp_buf_len; struct timespec uptime; extern unsigned long printk_get_buffer(char **p_log_buf, unsigned long *p_log_end, unsigned long *p_anzahl); struct _ram_print_data *psram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; char *buf, *pdata; unsigned long end, len; unsigned long log_bufsize, limit; do_posix_clock_monotonic_gettime(&uptime); monotonic_to_bootbased(&uptime); time_stamp_buf_len = snprintf(time_stamp_buf, sizeof(time_stamp_buf), "UPTIME: %lu\n%s\n", (unsigned long) uptime.tv_sec, SRAM_LOG_VERSION); restore_printk(); log_bufsize = printk_get_buffer(&buf, &end, &len); psram_print->len = 0; psram_print->magic_word = MAGIC_PRINK_WORD; limit = ATH_SRAM_PRINTKLEN - sizeof(struct _ram_print_data); pdata = psram_print->data; memcpy(pdata, time_stamp_buf, time_stamp_buf_len); pdata += time_stamp_buf_len, limit -= time_stamp_buf_len; /*--- printk(KERN_INFO"%s %d: len=%lu end=%lu log_bufsize =%lu limit=%lu\n",__func__, __LINE__, len, end, log_bufsize, limit); ---*/ if(log_bufsize >= len) { /*--- alles im Buffer ---*/ unsigned int start; if(limit > len) { start = 0; limit = len; } else { start = len - limit; } memcpy(pdata, buf + start, limit); } else { if(limit > end) { unsigned int start_rest = limit - end; unsigned int start_offset = log_bufsize - start_rest; memcpy(pdata, buf + start_offset, start_rest); memcpy(pdata + start_rest, buf, limit - start_rest); } else { unsigned int start = end - limit; memcpy(pdata, buf + start, limit); } } psram_print->len = limit; psram_print->magic_word = MAGIC_PRINK_WORD; #endif /*--- #ifdef CONFIG_TFFS_PANIC_LOG ---*/ } #define rprint(buf, buf_len, args...) if(buf) { \ if(buf_len > 0) { \ unsigned int slen = snprintf(buf, buf_len, args); \ buf += slen, buf_len -= slen; \ } \ } else { \ printk(KERN_ERR args);\ } /*--------------------------------------------------------------------------------*\ der Flash-NMI-Handler sichert hier die Register: letzter Not-Anker, falls NMI-Routine nicht mehr angesprungen werden konnte \*--------------------------------------------------------------------------------*/ int ath_restore_log_from_dumpregister(char *txtbuf, unsigned int txtlen) { char *txtstart = txtbuf; #if defined(ATH_SRAM_BASE_UNCACHED) char txttmp[64]; struct _ram_print_data *psram_register = (struct _ram_print_data *)ATH_DUMPREGISTER_MEMORY; if(txtbuf == NULL) { /*--- Kernel-Dump beim Startup: erstmal checken ob ueberhaupt NMI ---*/ if(!MAGIC_REBOOT_STATUS_WORD_CHECK(psram_register->rebootstatus)) { return 0; } if((psram_register->rebootstatus & 0xF) != RS_SOFT_WATCHDOG) { return 0; } } /*--- checke ob ueberhaupt Dump vom Flash-NMI-Handler---*/ if(psram_register->magic_word == MAGIC_DUMPREGISTER_WORD) { unsigned int *reg = (unsigned int *)psram_register->data; unsigned int exc_code = (reg[32] & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE; unsigned int cp0status = reg[31]; unsigned int stackp = reg[28]; unsigned int regdump_len = psram_register->len & 0xFF; unsigned int stackdump_len = (psram_register->len >> 16) & 0xFF; rprint(txtbuf, txtlen,"\n-----------------------\n%s: Raw-NMI occur - Register:\n", SRAM_LOG_VERSION); rprint(txtbuf, txtlen,"$ 0 : 00000000 %08x %08x %08x\n", reg[0], reg[1], reg[2]); rprint(txtbuf, txtlen,"$ 4 : %08x %08x %08x %08x\n", reg[ 3], reg[4], reg[ 5], reg[6]); rprint(txtbuf, txtlen,"$ 8 : %08x %08x %08x %08x\n", reg[ 7], reg[8], reg[ 9], reg[10]); rprint(txtbuf, txtlen,"$12 : %08x %08x %08x %08x\n", reg[11], reg[12], reg[13], reg[14]); rprint(txtbuf, txtlen,"$16 : %08x %08x %08x %08x\n", reg[15], reg[16], reg[17], reg[18]); rprint(txtbuf, txtlen,"$20 : %08x %08x %08x %08x\n", reg[19], reg[20], reg[21], reg[22]); rprint(txtbuf, txtlen,"$24 : %08x %08x %08x %08x\n", reg[23], reg[24], reg[25], reg[26]); rprint(txtbuf, txtlen,"$28 : %08x %08x %08x %08x\n", reg[27], reg[28], reg[29], reg[30]); txttmp[0] = 0; switch (cp0status & ST0_KSU) { case KSU_USER: strcat(txttmp, "USER "); break; case KSU_SUPERVISOR: strcat(txttmp, "SUPERVISOR "); break; case KSU_KERNEL: strcat(txttmp, "KERNEL "); break; default: strcat(txttmp, "BAD_MODE "); break; } if (cp0status & ST0_ERL) { strcat(txttmp, "ERL "); } if (cp0status & ST0_EXL) { strcat(txttmp, "EXL "); } if (cp0status & ST0_IE) { strcat(txttmp, "IE "); } rprint(txtbuf, txtlen, "Status: %08x %s\n", cp0status, txttmp); rprint(txtbuf, txtlen, "Cause: %08x %s\n", reg[32], exc_code == 1 ? "Mod" : exc_code == 2 ? "TLBL" : exc_code == 3 ? "TLBS" : exc_code == 4 ? "AdEL" : exc_code == 5 ? "AdES" : exc_code == 6 ? "IBE" : exc_code == 7 ? "DBE" : exc_code == 25 ? "Thread" : exc_code == 31 ? "CacheErr" : ""); rprint(txtbuf, txtlen,"epc: %08x %pS\n", reg[33], (void *)reg[33]); rprint(txtbuf, txtlen,"errepc: %08x %pS\n", reg[34], (void *)reg[34]); /*--- rprint(txtbuf, txtlen,"BAdVA: %08x\n", reg[35]); ---*/ if(stackdump_len) { unsigned int i, *pstack = (unsigned int *)(psram_register->data + regdump_len); rprint(txtbuf, txtlen,"Stackdump:\n"); for(i = 0; i < stackdump_len / sizeof(unsigned int); i++) { rprint(txtbuf, txtlen,"[<%08x>] %08x %pS\n", stackp, pstack[i], (void *)pstack[i]); stackp += sizeof(unsigned int); } } rprint(txtbuf, txtlen,"\n-----------------------\n"); } #endif/*--- #if defined(ATH_SRAM_BASE_UNCACHED) ---*/ return txtbuf - txtstart; } /*--------------------------------------------------------------------------------*\ * Restore Log from internal SRAM (if exist) * txtbuf, txtlen: destination (if NULL use printk) * ret: Laenge \*--------------------------------------------------------------------------------*/ int ath_restore_log_from_ram(char *txtbuf, unsigned int txtlen) { char *txtstart = txtbuf; #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) struct _ram_print_data *psram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; char buf[128]; /*--- printk(KERN_INFO"%s: %p len=%d/%d %x\n", __func__, txtbuf, txtlen,psram_print->len, psram_print->magic_word); ---*/ if((psram_print->len < ATH_SRAM_PRINTKLEN) && (psram_print->magic_word == MAGIC_PRINK_WORD)) { char *p = psram_print->data; unsigned int idx = 0; unsigned int len = psram_print->len; /*--- rprint(txtbuf, txtlen, "--------------- LOG found in SRAM (len = %u) ------------------------\n", len); ---*/ while(len) { if(idx < (sizeof(buf) - 2)) { if((*p != '\r') && (*p != '\n')) { if(((*p >= 0x80) || (*p < 0x20)) && (*p != '\t')) { buf[idx] = '?'; } else { buf[idx] = *p; } p++, idx++, len--; continue; } p++, len--; } if(idx) { buf[idx] = 0; rprint(txtbuf, txtlen,"%s\n", buf); idx = 0; } } if(idx) { rprint(txtbuf, txtlen,"%s\n", buf); } /*--- rprint(txtbuf, txtlen,"---------------------------------------------------------------------\n"); ---*/ } else { /*--- Not->anker ---*/ return ath_restore_log_from_dumpregister(txtbuf, txtlen); } #endif/*--- #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) ---*/ return txtbuf - txtstart; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void ath_inval_log_from_ram(void) { #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) struct _ram_print_data *psram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; psram_print->magic_word = 0; #endif/*--- #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) ---*/ #if defined(ATH_SRAM_BASE_UNCACHED) { struct _ram_print_data *psram_register = (struct _ram_print_data *)ATH_DUMPREGISTER_MEMORY; psram_register->magic_word = 0; } #endif/*--- #if defined(ATH_SRAM_BASE_UNCACHED) ---*/ } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void ath_save_rebootstatus_to_ram(unsigned int idx) { #if defined(ATH_SRAM_BASE_UNCACHED) struct _ram_print_data *pram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; if(!MAGIC_REBOOT_STATUS_WORD_CHECK(pram_print->rebootstatus)) { pram_print->rebootstatus = MAGIC_REBOOT_STATUS_WORD_SET(idx); } #endif/*--- #if defined(ATH_SRAM_BASE_UNCACHED) ---*/ } unsigned int ath_reboot_status; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int ath_reboot_status_from_ram(void) { #if defined(ATH_SRAM_BASE_UNCACHED) struct _ram_print_data *pram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; ath_reboot_status = RS_NMI_PON; if(MAGIC_REBOOT_STATUS_WORD_CHECK(pram_print->rebootstatus)) { ath_reboot_status = pram_print->rebootstatus & 0xF; pram_print->rebootstatus = 0x0; } else { /*--- gesetzt von NMI-Handler-Routine im Flash ---*/ struct _ram_print_data *pram_print = (struct _ram_print_data *)ATH_DUMPREGISTER_MEMORY; if(MAGIC_REBOOT_STATUS_WORD_CHECK(pram_print->rebootstatus)) { ath_reboot_status = pram_print->rebootstatus & 0xF; pram_print->rebootstatus = 0x0; } } if((ath_reboot_status >= sizeof(reboot_status) / sizeof(reboot_status[0]))) { ath_reboot_status = 0; } printk(KERN_ERR"(c) AVM 2014, Reboot Status is: %s\n", reboot_status[ath_reboot_status]); #endif/*--- #if defined(ATH_SRAM_BASE_UNCACHED) ---*/ return 0; } arch_initcall(ath_reboot_status_from_ram); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int avm_reset_status(void) { return ath_reboot_status; } EXPORT_SYMBOL(avm_reset_status); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_NMI(void) { ath_save_rebootstatus_to_ram(RS_SOFT_WATCHDOG); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_Update(void) { ath_save_rebootstatus_to_ram(RS_FIRMWARE_UPDATE); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_NMI_WA(void) { ath_save_rebootstatus_to_ram(RS_NMI_WA); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_BusError(void) { ath_save_rebootstatus_to_ram(RS_BUS_ERROR); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_SoftReboot(void) { ath_save_rebootstatus_to_ram(RS_SOFT_REBOOT); }