| /****************************************************************************** |
| *(C) Copyright 2008 Marvell International Ltd. |
| * All Rights Reserved |
| ******************************************************************************/ |
| /*-------------------------------------------------------------------------------------------------------------------- |
| * ------------------------------------------------------------------------------------------------------------------- |
| * |
| * Filename: eeh_api.c |
| * |
| * Description: The APIs to process error & exception of system. |
| * |
| * History: |
| * April, 2008 - Rovin Yu Creation of file |
| * |
| * Notes: |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * Include files |
| ******************************************************************************/ |
| |
| #include <sys/ioctl.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <pthread.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <sys/mman.h> |
| #include <sys/prctl.h> |
| #include <cutils/properties.h> |
| |
| #if defined (BIONIC) |
| #include <hardware_legacy/power.h> |
| #include <sys/reboot.h> // reboot, RB_AUTOBOOT |
| #include <cutils/properties.h> |
| #endif |
| |
| #include "pxa_dbg.h" |
| #include "EE_Postmortem.h" |
| #include "EEHandler.h" |
| #include "EEHandler_int.h" |
| #include "EEHandler_config.h" |
| #include "eeh.h" |
| #include "eeh_assert_notify.h" |
| #include "msocket_api.h" |
| #include "cploader.h" |
| #include "loadTable.h" |
| #include "utilities.h" |
| #include "pxa9xx_cp.h" |
| |
| // Run-time "sm.logLevel" depends upon |
| // {eehCfg.logLevel, eehCfg.cpSR_logLevel, sm.cpSR_cntr, eehCfg.cpSR_max} |
| // where "arc"/"arcive" is ZIP/archive into sd/modem_dump/**.gz |
| // Refer "eeh_HowToConfig.txt |
| typedef enum { |
| EEH_LOGLVL_0 = 0, //Don't save any log.bin (but eeh_journal.txt record only) |
| EEH_LOGLVL_1_ARC2SD = 1, //Save com_EEH_Buf.bin only on NVM, arc to SDcard |
| EEH_LOGLVL_2_CPSR = 2, //Save com_EEH_Buf.bin, LogStreamS, dmesg.txt |
| EEH_LOGLVL_3_CPSR_ARC2SD = 3, //Save com_EEH_Buf.bin, LogStreamS, dmesg.txt (arc to SDcard) |
| EEH_LOGLVL_4_RDP = 4, //Save com_EEH_Buf.bin, LogStream and SPY-Dumps files on NVM |
| EEH_LOGLVL_5_RDP_ARC2SD = 5, //Save com_EEH_Buf.bin, LogStream and SPY-Dumps (arc to SDcard) |
| EEH_LOGLVL_6_DBG = 6, //Save all possible files on NVM+SDcard and STALL (no auto-RAMDUMP) |
| EEH_LOGLVL_7_DBG_ARC2SD = 7, //Save all possible files on NVM+SDcard and STALL (no auto-RAMDUMP) |
| EEH_LOGLVL_8_LOG2SD = 8, // All to sd/log directory, rename to sd/log_NNN_DATE (no archive) (takes only ~25sec for all-all logs present on SD) |
| EEH_LOGLVL_9_LOG2SD_ARC2SD = 9, //All to sd/log directory, arc all but DDR_RW to sd/modem_dump/ |
| EEH_LOGLVL_SIZE |
| }eehLogLvl_e; |
| #define EEH_LOGLVL_WITH_ARC2SD(level) (level & 1) |
| |
| /* Define the timers used by EEH */ |
| #define CPSR_RECOVERY_TO_SEC 30/*increase from 4sec*/ |
| typedef enum { |
| timerStopped = 0, |
| CPSilentResetTimerId, //CPSR_RECOVERY_TO_SEC |
| eehGuardTimerId, //see guardTO[] |
| cpAssertReqTimerId, |
| maxTimerId |
| } eehTmrRun_e; |
| |
| typedef struct { |
| EehAssertType inProgress; //eeh_ioctl.h |
| eehTmrRun_e currentTimerId; |
| char hawk_enable; |
| char *hawk_buf; |
| unsigned cpSR_cntr; |
| char cpRestartReq; |
| int logLevel; |
| char *TimeOutPanicDesc; |
| int ramdump_level; |
| char CPASSERT_flag; |
| char wifiSR_cntr; |
| } eehStateMachine_s; |
| static eehStateMachine_s sm; |
| |
| //eehGuardTimerId: depending upon Log-Level and logging latency |
| static const unsigned short guardTO[EEH_LOGLVL_SIZE] = |
| /* level 0 1 2 3 4 5 6 7 8 9 */ |
| /* seconds */ {7, 7, 31, 31, 160, 160, 253, 253 , 31, 160}; //value=0 ~ no timer |
| |
| void * eeh_mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset); |
| int eeh_munmap (void *addr, size_t length); |
| |
| /*************************************************** |
| * Device drivers used by Error-Handler: |
| * /dev/kmsg - ERRMSG and trace messages (EEHandler_config.h) |
| * /dev/seh - seh.ko private EEH driver |
| * /dev/ramfile - part of seh.ko saving files as ramfile |
| * /dev/ramdump_ctl - Panic Emulation driver |
| * /proc/sys/kernel/print-fatal-signals |
| */ |
| int EEH_ERRMSG_fd = -1; |
| int sehdrv_fd = -1; |
| #define EEH_DEVICE "/dev/seh" |
| #define EEH_PANIC_DEV "/dev/ramdump_ctl" |
| #define EEH_RAMFILE_DEV "/dev/ramfile" |
| //#define EEH_RAMFILE_DBG /* force even on "STALL" + traces */ |
| |
| #ifdef BIONIC |
| static char* DIAG_FS_MOUNT_POINT = "/data/acat"; |
| #endif |
| |
| void EehPrint(const char *msg) |
| { |
| ERRMSG("%s\n", msg); |
| DIAG_FILTER(Diag, comMem, Final, DIAG_INFORMATION) |
| diagPrintf("%s", msg); |
| } |
| |
| |
| extern unsigned long PRE_LOADTABLE_PHYADDR; |
| |
| //static void eehDontSleep(void) { acquire_wake_lock(PARTIAL_WAKE_LOCK, "eeh"); } |
| //static void eehCouldSleep(void) { release_wake_lock("eeh"); } |
| static void eehNoSleepLong(void) { ioctl(sehdrv_fd, SEH_IOCTL_DONT_SLEEP, NULL); } |
| static void eehNoSleepShort(void) { ioctl(sehdrv_fd, SEH_IOCTL_SLEEP_ALLOWED, NULL); } |
| |
| |
| void eeh_CPASSERT_flag(int set) |
| { |
| sm.CPASSERT_flag = set; |
| } |
| |
| /*static*/void eeh_clear_fs_cache(int with_sync) |
| { |
| FILE *fd; |
| if (with_sync) |
| sync(); |
| fd = fopen("/proc/sys/vm/drop_caches", "w"); |
| if (fd) { |
| fprintf(fd, "3") ; |
| fclose(fd); |
| } |
| } |
| |
| static void eeh_kill_for_memory(void) |
| { |
| #define EEH_AGRESSIVE_PUSH_RAMFILE |
| #ifdef EEH_AGRESSIVE_PUSH_RAMFILE |
| // Call this only when going into Final RAMDUMP reset... |
| static int kill_done; |
| if (!kill_done) { |
| kill_done = 1; |
| system("killall -9 ota charger"); /*about 500kB */ |
| } |
| #endif |
| } |
| |
| |
| /**** "Conditional" HAWK_ENABLED procedures ******************************* |
| * HAWK is an agent reporting about valuable events (including crashes) |
| * The old name is REM. |
| * In case of crash a report sent after Silent-Recovery or system restart |
| * EEH sends reports over eeh_hawk.c::eeh_to_hawk_indication() |
| * Wrap with macro to minimize #ifdef |
| */ |
| #ifndef HAWK_ENABLED |
| #define HAWK_indication_txt(TXT) |
| #else |
| #define HAWK_indication_txt(TXT) if(sm.hawk_enable) eeh_to_hawk_indication(TXT) |
| #define HAWK_indication_ext(SZ, TYPE, INITIATOR, inBUF) \ |
| if (sm.hawk_enable) { \ |
| char outBuf[SZ]; \ |
| snprintf(outBuf, SZ, TYPE, INITIATOR, inBUF); \ |
| eeh_to_hawk_indication(outBuf); \ |
| } |
| /*HAWK_indication_ext: |
| ( 200, EEH2HAWK_ASSERT_DETECTED, HAWK_INITIATOR_COMM,eeBuffer); |
| (1024, EEH2HAWK_VAL_EVENT_DETECTED,HAWK_INITIATOR_TEL,eeString); |
| ( 200, EEH2HAWK_CRASH_DETECTED, HAWK_INITIATOR_TEL,eeString); |
| */ |
| #endif //HAWK_ENABLED |
| |
| |
| /**** "Conditional" EEH_DUMP_ENHANCE ******************************** |
| * Archiving into SD-card all Com/Modem files |
| */ |
| #ifndef EEH_DUMP_ENHANCE |
| static void EEH_DUMP_ENHANCE_draw_panic(char *eeString) { } |
| static void EEH_DUMP_ENHANCE_record_archive_files(char *eeBuffer){ } |
| static void EEH_DUMP_ENHANCE_reset_archive_files(void) { } |
| #else |
| void EEH_DUMP_ENHANCE_draw_panic(char *eeString); |
| void EEH_DUMP_ENHANCE_record_archive_files(char *eeBuffer); |
| void EEH_DUMP_ENHANCE_reset_archive_files(void); |
| #endif//EEH_DUMP_ENHANCE |
| |
| |
| /*************************************************/ |
| EEH_STATUS EehInsertCommtoReset(void) |
| { |
| EehApiParams ApiParams; |
| INT32 ret; |
| ApiParams.eehApiId = _EehInsertComm2Reset; |
| ApiParams.params = NULL; |
| |
| EehPrint("Eeh Insert Comm to Reset"); |
| |
| ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams); |
| if (ret) { |
| ERRMSG("%s: error=%d\n", __FUNCTION__, errno); |
| return EEH_ERROR; |
| } |
| return ApiParams.status; |
| } |
| |
| /************************************************************ |
| * EehChangeResetState |
| * change the process in crash list no need to reset |
| ************************************************************/ |
| EEH_STATUS EehChangeResetState(void) |
| { |
| EehApiParams ApiParams; |
| INT32 ret; |
| ApiParams.eehApiId = _EehChangeResetState; |
| ApiParams.params = NULL; |
| |
| EehPrint("Eeh Change Reset State"); |
| |
| ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams); |
| if (ret) { |
| ERRMSG("%s: error=%d\n", __FUNCTION__, errno); |
| return EEH_ERROR; |
| } |
| return ApiParams.status; |
| } |
| |
| /*********************************************************/ |
| EEH_STATUS EehReleaseCommFromReset(void) |
| { |
| EehApiParams ApiParams; |
| INT32 ret; |
| ApiParams.eehApiId = _EehReleaseCommFromReset; |
| ApiParams.params = NULL; |
| |
| EehPrint("Eeh Release Comm From Reset"); |
| |
| ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams); |
| if (ret) { |
| ERRMSG("%s: error=%d\n", __FUNCTION__, errno); |
| return EEH_ERROR; |
| } |
| return ApiParams.status; |
| } |
| |
| /*************************************************************** |
| * SaveSilentResetStatistics |
| * Creates log-file with silent reset statistics. |
| * |
| * NOTE: to be obsolet. Everything needed is in eeh_journal.txt |
| ***************************************************************/ |
| void SaveSilentResetStatistics(UINT32 msgId) |
| { |
| UNUSEDPARAM(msgId) |
| FILE *fd; |
| char fullPath[MAX_PATH] = { 0 }; |
| char fileName[] = "SilentResetStatistic.log"; |
| char *nvm_root_dir = eehCfg.logDir; |
| time_t timep; |
| |
| if (nvm_root_dir != NULL) |
| { |
| strlcpy(fullPath, nvm_root_dir, sizeof(fullPath)); |
| strlcat(fullPath, "/", sizeof(fullPath)); |
| } |
| strlcat(fullPath, fileName, sizeof(fullPath)); |
| |
| if ((fd = fopen(fullPath, "aw")) != 0) |
| { |
| time(&timep); |
| fprintf(fd, "CPP ERROR causes silent reset No.%d : %s", sm.cpSR_cntr, ctime(&timep)); |
| fclose(fd); |
| } |
| } |
| |
| #define RD_SIZE 1*1024*1024 |
| /************************************************* |
| * EehCopyCommImageFromFlash |
| * copy Comm image from flash to DDR. |
| **************************************************/ |
| EEH_STATUS EehCopyCommImageFromFlash(void) |
| { |
| struct cp_load_table_head *hdr; |
| const char *errmsg = "*** CPSR failed to load CP images ***"; |
| int ret; |
| |
| EehPrint("Copy Comm Image From Flash to DDR"); |
| |
| /* -1, -1 means have not prepared the current & exist cp property values, |
| * it means need to get these two property values from property_get */ |
| if((hdr = cp_load_table_init()) == NULL) |
| return EEH_ERROR; |
| |
| ret = set_cp_type_property(hdr); |
| if(ret == -1) |
| { |
| cp_load_table_exit(hdr); |
| ERRMSG("%s (err1)\n", errmsg); |
| return EEH_ERROR; |
| } |
| |
| /* MSA must be loaded before RF */ |
| //coverity[tainted_data: SUPPRESS] |
| if(cp_load_arbel_image(hdr, 0) == -1 || |
| #if defined(CONFIG_ASR1901_EEH) || defined(CONFIG_ASR1903_EEH) |
| cp_load_bx2_image(hdr, 0) == -1 || |
| #endif |
| #ifndef CONFIG_ASR1903_EEH |
| cp_load_msa_image(hdr, 0) == -1 || |
| #endif |
| cp_load_rf_image(hdr, 0) == -1 || |
| cp_set_cp_addr(hdr, 0) == -1) |
| { |
| cp_load_table_exit(hdr); |
| ERRMSG("%s (err2)\n", errmsg); |
| return EEH_ERROR; |
| } |
| |
| //coverity[tainted_data: SUPPRESS] |
| if (cp_load_reliable_data(hdr, 0) < 0) |
| { |
| cp_load_table_exit(hdr); |
| return EEH_ERROR; |
| } |
| |
| if (cp_load_mep(hdr, 0) < 0) |
| { |
| cp_load_table_exit(hdr); |
| return EEH_ERROR; |
| } |
| |
| cp_load_table_exit(hdr); |
| |
| return EEH_NO_ERROR; |
| } |
| |
| //---------------------------------------------------------- |
| static int eeh_log_dir_name_generate(char *buf, int len, int re_gen) |
| { |
| static struct tm tm; |
| time_t ttm; |
| int number = 0; |
| |
| if (!buf) //reset request |
| tm.tm_mon = 0; |
| |
| if (re_gen) { |
| if (!tm.tm_mon) |
| return -1; |
| } else { |
| time(&ttm); |
| gmtime_r(&ttm, &tm); |
| } |
| |
| // sd/log_000_mm-dd_hh.mm.ss |
| if (buf) |
| snprintf(buf, len, "%slog_%03d_%02d-%02d_%02d.%02d.%02d", |
| SDCARD_MOUNT, number, 1 + tm.tm_mon, tm.tm_mday, |
| tm.tm_hour, tm.tm_min, tm.tm_sec); |
| return 0; |
| } |
| |
| /*--------------------- final=RAMDUMP utilities ------------------- |
| * EehRamdump - main API including: |
| * open(EEH_RAMFILE_DEV) |
| * EehRamdumpPushLogfiles by the EehPushRamfile |
| * EehRamdumpActivate - panic emulation (called by EehRamdump |
| * or by guard-timer-expiry) |
| *----------------------------------------------------------------- |
| */ |
| #define RAMFILE_HEADER_SIZE 32 /* should match kernel definition for struct ramfile_desc*/ |
| #define RAMFILE_MAX_LEN 0x400000 |
| static int EehPushRamfile(int ramfdev, const char* file) |
| { |
| char *vpa = 0; |
| FILE* ffd = 0; |
| int len, l; |
| struct stat fst; |
| int ret = -1; |
| |
| if ((ffd=fopen(file, "rb"))==NULL) { |
| ERRMSG("RAMFILE: cannot open %s\n", file); |
| goto bail; |
| } |
| if (fstat(fileno(ffd), &fst)) { |
| ERRMSG("RAMFILE: cannot stat %s\n", file); |
| goto bail; |
| } |
| len = fst.st_size; |
| if ((len<=0) || (len>RAMFILE_MAX_LEN)) { |
| ERRMSG("RAMFILE: bad length %d file %s\n", len, file); |
| goto bail; |
| } |
| eeh_clear_fs_cache(0); |
| if ((vpa=(char *)mmap(0, len + RAMFILE_HEADER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, ramfdev, 0))==MAP_FAILED) { |
| ERRMSG("RAMFILE: failed to allocate %d bytes for file %s\n", len, file); |
| goto bail; |
| } |
| if ((l = fread((void*)(vpa+RAMFILE_HEADER_SIZE), 1, len, ffd)) != len) { |
| ERRMSG("RAMFILE: failed to read %d bytes from %s, ret=%d\n", len, file, l); |
| goto bail; |
| } |
| #ifdef EEH_RAMFILE_DBG |
| ERRMSG("RAMFILE: pushed %d bytes from %s to vpa=%.8x+%d\n", |
| len, file, vpa, RAMFILE_HEADER_SIZE); |
| #endif |
| /* OK, data is in memory */ |
| ret = 0; |
| bail: |
| if (ffd) |
| fclose(ffd); |
| if (vpa) |
| munmap(vpa, len); |
| return ret; |
| } |
| |
| #if 1 |
| static int EehRamdumpPushLogfiles(int ramfdev) |
| { |
| static int ramfile_do_once; |
| char *ramfiletmp; |
| int max = 1024; |
| char command_buf[max]; |
| FILE *lfd; |
| int ret, len, i, sz, sd_available, fcntr = 0; |
| char **p; |
| |
| if (ramfile_do_once) |
| return 0; //already done |
| |
| //file is big. Try to create it on SD instead of flash |
| sd_available = eeh_sd_card_available(1); |
| if (sd_available) |
| ramfiletmp = LOG_DIR_SD_RDP "ramfiletmp.tgz"; |
| else |
| ramfiletmp = LOG_DIR "ramfiletmp.tgz"; |
| |
| unlink(ramfiletmp); |
| |
| // If log dir on sd has been used we need to chdir into there |
| if (sd_available && (sm.logLevel == EEH_LOGLVL_8_LOG2SD) && |
| !eeh_log_dir_name_generate(command_buf, max, 1)) { |
| chdir(command_buf); |
| } |
| |
| //Already in LOG_DIR, no chdir() required |
| /* Note: cvfz that works for standard tar does not gzip in busybox implementation, m.b. because of -f: use cvz */ |
| len = sprintf(command_buf, "tar cvz -f %s", ramfiletmp); |
| |
| //Create/add loglist from array but only with existing files |
| //---------------------------------------------------------- |
| |
| p = (char**)ramdump_loglist_ap; |
| sz = ramdump_loglist_ap_sz; |
| for (i = 0; i < sz; i++) { |
| if (access(p[i], F_OK) == 0) { |
| len += snprintf(&command_buf[len], max-len, " %s", p[i]); |
| fcntr++; |
| } |
| } |
| p = (char**)ramdump_loglist_cp; |
| sz = ramdump_loglist_cp_sz; |
| for (i = 0; i < sz; i++) { |
| if (access(p[i], F_OK) == 0) { |
| len += snprintf(&command_buf[len], max-len, " %s", p[i]); |
| fcntr++; |
| } |
| } |
| p = (char**)ramdump_loglist_sdl; |
| sz = ramdump_loglist_sdl_sz; |
| for (i = 0; i < sz; i++) { |
| if (access(p[i], F_OK) == 0) { |
| len += snprintf(&command_buf[len], max-len, " %s", p[i]); |
| fcntr++; |
| } |
| } |
| if (!fcntr) |
| return -1; //nothing to save |
| |
| command_buf[max-1] = 0; |
| |
| // EXECUTE created command/loglist |
| ret = system(command_buf); // continue even is not OK |
| #ifdef EEH_RAMFILE_DBG |
| ERRMSG("RAMFILE-0: %d = %s\n", ret, command_buf); |
| #endif |
| eeh_kill_for_memory(); |
| ret = EehPushRamfile(ramfdev, ramfiletmp); |
| if (!sd_available) |
| unlink(ramfiletmp); //remove from flash, but keep on sd |
| |
| |
| // ---- Repeat for BIG file(s) ------ |
| len = sprintf(command_buf, "tar cvz -f %s", ramfiletmp); |
| fcntr = 0; |
| //do big files last since memory no guaranteed |
| p = (char**)ramdump_loglist_cp_big; |
| sz = ramdump_loglist_cp_big_sz; |
| for (i = 0; i < sz; i++) { |
| if (access(p[i], F_OK) == 0) { |
| len += snprintf(&command_buf[len], max-len, " %s", p[i]); |
| fcntr++; |
| } |
| } |
| if (!fcntr) |
| goto exit; |
| |
| command_buf[max-1] = 0; |
| |
| // EXECUTE created command/loglist |
| /*ret =*/ system(command_buf); // continue even is not OK |
| #ifdef EEH_RAMFILE_DBG |
| ERRMSG("RAMFILE-1: %s\n", command_buf); |
| #endif |
| /*ret =*/ EehPushRamfile(ramfdev, ramfiletmp); |
| if (!sd_available) |
| unlink(ramfiletmp); //remove from flash, but keep on sd |
| |
| exit: |
| ramfile_do_once = !!(!ret); |
| return ret; |
| } |
| #else//version_0 |
| #define RAMFILE_TMP_ARCHIVE "ramfiletmp.tar" /* temp ramdump filename archived */ |
| #define RAMFILE_TMP_ARCHIVE_COMPRESS "ramfiletmp.tar.gz" /* temp ramdump filename archived and compressed*/ |
| #define LISTFILE_TMP "/tmp/sdl-list" |
| #define RAMDUMP_LOGLIST_PATH XSTR(TELEPHONY_CONFIG_PATH)"ramdump_loglist" |
| static int EehRamdumpPushLogfiles() |
| { |
| char logfilename[MAX_PATH]; |
| char command_buf[2 * MAX_PATH+50]; |
| int ret = -1; |
| FILE *lfd = NULL; |
| int archivefd; |
| |
| char *nvm_root_dir = eehCfg.logDir; |
| |
| if (nvm_root_dir == NULL) |
| return ret; |
| |
| if ((lfd=fopen(RAMDUMP_LOGLIST_PATH, "rt"))==NULL) |
| { |
| fprintf(stderr, "RAMDUMP failed to open %s\n", RAMDUMP_LOGLIST_PATH); |
| return ret; |
| } |
| if(chdir(nvm_root_dir) != 0) |
| { |
| fprintf(stderr, "RAMDUMP change directory to %s error\n", nvm_root_dir); |
| goto out; |
| } |
| if((archivefd = archive_open(RAMFILE_TMP_ARCHIVE)) < 0) |
| { |
| fprintf(stderr, "RAMDUMP open archive file %s error\n", RAMFILE_TMP_ARCHIVE); |
| goto out; |
| } |
| while(fgets(logfilename, sizeof(logfilename), lfd)) { |
| /* Must strip end-of-line chars, otherwise filename won't be valid */ |
| char *s_end = strchr(logfilename,'\n'); |
| if (s_end) |
| *s_end = 0; |
| |
| if(archive_regular_file(archivefd, logfilename) != 0) |
| { |
| fprintf(stderr, "RAMDUMP archive log files %s to %s error\n", logfilename, RAMFILE_TMP_ARCHIVE); |
| archive_close(archivefd); |
| unlink(RAMFILE_TMP_ARCHIVE); |
| goto out; |
| } |
| } |
| if(archive_pad_last(archivefd) != 0) |
| { |
| fprintf(stderr, "RAMDUMP archive last padding to %s error\n", RAMFILE_TMP_ARCHIVE); |
| archive_close(archivefd); |
| unlink(RAMFILE_TMP_ARCHIVE); |
| goto out; |
| } |
| archive_close(archivefd); |
| sprintf(command_buf, "gzip %s", RAMFILE_TMP_ARCHIVE); |
| system(command_buf); |
| sprintf(command_buf, "cp %s /tmp/", RAMFILE_TMP_ARCHIVE_COMPRESS); |
| system(command_buf); |
| unlink(RAMFILE_TMP_ARCHIVE_COMPRESS); |
| out: |
| fclose(lfd); |
| return ret; |
| } |
| #endif//version_0 |
| #if 0//PUSH_SDL_TO_RAMDUMP |
| static int EehRamdumpPushSdlfiles(int fd) |
| { |
| char tmpfilename[MAX_PATH]; |
| char command_buf[MAX_PATH+50]; |
| int ret = -1; |
| FILE* lfd = 0; /* list file */ |
| char s[MAX_PATH]; |
| struct stat fst; |
| const char* list_file = LISTFILE_TMP; |
| char *nvm_root_dir = eehCfg.logDir; |
| |
| if (nvm_root_dir == NULL) |
| return ret; |
| |
| sprintf(tmpfilename, "%s/%s", nvm_root_dir, RAMFILE_TMP); |
| /* Get a list of all SDL files in reverse order: starting from the most recent one */ |
| sprintf(command_buf, "busybox ls -1t %s/marvell/*.sdl > %s", nvm_root_dir, list_file); |
| system(command_buf); |
| if ((lfd=fopen(LISTFILE_TMP, "rt"))==NULL) { |
| fprintf(stderr, "RAMDUMP failed to create %s\n", list_file); |
| goto bail; |
| } |
| |
| while(fgets(s, sizeof(s), lfd)) { |
| /* Must strip end-of-line chars, otherwise filename won't be valid */ |
| char *s_end = strchr(s,'\n'); |
| if (s_end) |
| *s_end = 0; |
| /* List contains full path-names. Use tar -C path basename, so no path appears in tgz */ |
| if (stat(s, &fst)) { |
| fprintf(stderr, "RAMDUMP cannot stat %s\n", s); |
| continue; /* cannot stat this file - skip */ |
| } |
| |
| sprintf(command_buf, "busybox tar cvz -f %s -C %s %s", |
| tmpfilename, dirname(s), basename(s)); |
| system(command_buf); /* create tgz containing this SDL file; tgz also saves the filename */ |
| if (EehPushRamfile(fd, tmpfilename)) |
| break; /* first failure (no memory) - stop the process */ |
| } |
| ret = 0; |
| bail: |
| if (lfd) |
| fclose(lfd); /* in tmpfs, leave it there */ |
| unlink(tmpfilename); |
| return ret; |
| } |
| #endif//PUSH_SDL_TO_RAMDUMP |
| |
| |
| static void eeh_panic_reset(void) |
| { |
| if (sehdrv_fd < 0) |
| sehdrv_fd = open(EEH_DEVICE, O_RDWR | O_SYNC); |
| if (sehdrv_fd >= 0) |
| ioctl(sehdrv_fd, SEH_IOCTL_EMULATE_PANIC, NULL); |
| } |
| |
| /*GLOB*/ int write_ramdump_control(char *code_txt) |
| { |
| //ramdump_ctl: options 0/1=e/d, p[anic], B[UG], a[dvance], j[iffies] |
| // CRC: 'c' account, 'C' report, 'U'[rgent]='c'+'C' |
| int fd; |
| int len = strlen(code_txt); |
| |
| // Cut text. Suppose "const char*" is predictably small, |
| // only non-const (writ available) dynamic-description may be huge |
| if (len > 99) |
| code_txt[99] = 0; |
| |
| fd = open(EEH_PANIC_DEV, O_WRONLY | O_SYNC); |
| if (fd < 0) |
| return -EIO; |
| len = write(fd, code_txt, len); |
| close(fd); |
| return |
| (len >= 0) ? 0 : -1; |
| } |
| |
| static int read_ramdump_level(void) |
| { |
| //On graceful shutdown the ramdump_level is changed to |
| //ignore erros. So EEH should re-check it befor handling |
| unsigned char lvl = 0xff; |
| int fd = open(EEH_PANIC_DEV, O_RDONLY | O_SYNC); |
| if (fd >= 0) { |
| //read binary-row byte-data, read() is ASCII and returns -1 |
| read(fd, &lvl, 1); |
| close(fd); |
| if (lvl != 0xff) |
| return (int)lvl; |
| } |
| return -1; |
| } |
| |
| static int/*bool*/ eeh_is_enabled_by_ramdump(void) |
| { |
| int lvl; |
| if (sm.ramdump_level <= 1) |
| return 0; |
| lvl = read_ramdump_level(); |
| if (lvl >= 0) |
| sm.ramdump_level = lvl; |
| return (sm.ramdump_level >= 2); |
| } |
| |
| static char* eeh_kernel_crc_is_valid(void) |
| { |
| /* Disable. It is checked at time of PANIC |
| int rc = write_ramdump_control("c"); |
| if (rc == ERANGE) |
| return " !Bad Kernel CRC!"; |
| // OK, or ignore other failures |
| */ |
| return NULL; |
| } |
| |
| static void EehRamdumpActivate(char *desc_txt) |
| { |
| /* Emulate panic that triggers ramdump */ |
| char buf[100]; |
| |
| if (sm.TimeOutPanicDesc) |
| desc_txt = sm.TimeOutPanicDesc; |
| |
| while (desc_txt || panicExtraDesc[0]) { |
| if (!desc_txt) { |
| if (sm.CPASSERT_flag) |
| break; //this is [TC]. Description has already been added |
| desc_txt = panicExtraDesc; |
| } |
| snprintf(buf, 99, "p_%s", desc_txt); |
| buf[99] = 0; |
| write_ramdump_control(buf); |
| break; |
| } |
| //no-text or fallback |
| |
| if(!eehCfg.stall_FINAL) |
| { |
| eeh_panic_reset(); |
| } |
| } |
| |
| static void EehRamdump(int activate) |
| { |
| //open device here since more than one "PushLog" may be presetnt |
| int dev = open(EEH_RAMFILE_DEV, O_RDWR | O_SYNC); |
| if (dev < 0) { |
| ERRMSG("%s: failed to open ramfile driver, error=%d\n", __FUNCTION__, errno); |
| } else { |
| EehRamdumpPushLogfiles(dev); |
| //EehRamdumpPush***Logfiles() |
| close(dev); |
| } |
| if (sm.hawk_enable) |
| sleep(5); // This sleep should allow us to get message on tablet |
| |
| if (activate) |
| EehRamdumpActivate(NULL); |
| } |
| |
| /* Communicate the error info to SEH (for RAMDUMP and m.b. more) */ |
| static void EehSaveErrorInfo(unsigned err, char *str, unsigned char *context) |
| { |
| int ret = -1; |
| EehErrorInfo info; |
| info.err = err; |
| info.str = str; |
| info.regs = context; |
| ret = ioctl(sehdrv_fd, SEH_IOCTL_SET_ERROR_INFO, &info); |
| if (ret) |
| ERRMSG("%s: error=%d\n", __FUNCTION__, errno); |
| } |
| |
| static void EehSetEEConfigCPResetFlag(int reset_active) |
| { |
| if (ioctl(sehdrv_fd, SEH_IOCTL_SET_EECONFIG_B_CP_RESET, reset_active)) |
| ERRMSG("%s: error=%d\n", __FUNCTION__, errno); |
| } |
| |
| int ee_save_postmortem(const char *path, const char *entry) |
| { |
| int ret = -1; |
| |
| #ifdef EEH_LOG_DUMP_LIB |
| if (sehdrv_fd >= 0) |
| { |
| ERRMSG("%s: the dumping is ongoing, please retry later\n", __FUNCTION__); |
| return -1; |
| } |
| |
| sehdrv_fd = open(EEH_DEVICE, O_RDWR | O_SYNC); |
| if (sehdrv_fd < 0) |
| { |
| ERRMSG("%s: opening %s error %s(%d)\n", |
| __FUNCTION__, EEH_DEVICE, strerror(errno), errno); |
| return -1; |
| } |
| |
| EehApiParams ApiParams; |
| EehGetCPLoadAddrParam param; |
| |
| ApiParams.eehApiId = _EehGetCPLoadAddr; |
| ApiParams.params = ¶m; |
| |
| if (ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams) != 0) |
| { |
| ERRMSG("%s: error=%d\n", __FUNCTION__, errno); |
| close(sehdrv_fd); |
| sehdrv_fd = -1; |
| return -1; |
| } |
| PRE_LOADTABLE_PHYADDR = param.arbel_load_addr; |
| |
| commImageTableInit(); |
| #endif |
| |
| if (getCommImageBaseAddr() != INVALID_ADDRESS) |
| { |
| EE_PostmortemDesc_Entry *p = getEntryByName(entry); |
| |
| if (p != ENTRY_NOT_FOUND) |
| { |
| FILE *fp; |
| |
| DBGMSG("%s: entry found, name %s, addr %08x, len %u\n", |
| __FUNCTION__, p->name, p->bufAddr, p->bufLen); |
| |
| if ((fp = fopen(path, "wb")) != NULL) |
| { |
| unsigned char *buf = (unsigned char *)eeh_mmap(0, p->bufLen, |
| PROT_READ, MAP_PRIVATE, sehdrv_fd, (off_t)ADDR_CONVERT(p->bufAddr)); |
| if (buf) |
| { |
| (void)fwrite(buf, sizeof(char), p->bufLen, fp); |
| eeh_munmap(buf, p->bufLen); |
| ret = 0; |
| } |
| fclose(fp); |
| } |
| } |
| } |
| |
| #ifdef EEH_LOG_DUMP_LIB |
| commImageTableFree(); |
| close(sehdrv_fd); |
| sehdrv_fd = -1; |
| #endif |
| |
| return ret; |
| } |
| |
| //--------------------------------------------------------------------- |
| typedef enum ksig_handl { /* /proc/sys/kernel/print-fatal-signals */ |
| KSIG_HANDL_KILL_NO_PRINTS = 0, |
| KSIG_HANDL_KILL_SHOWREGS = 1, |
| KSIG_HANDL_KILL_SHORT_PRINT = 2, |
| KSIG_HANDL_PANIC = 15, /*0xF Fatal Force panic */ |
| KSIG_HANDL_NO_KILL_FREEZE = 16 |
| } ksig_handl; |
| /* |
| static void eeh_set_ksig_handler_cfg(ksig_handl cfg) |
| { |
| static FILE *fd; // Open once and keep it |
| if (!fd) fd = fopen("/proc/sys/kernel/print-fatal-signals", "w"); |
| if (!fd) fprintf(fd, "%d", cfg); |
| }*/ |
| static int eeh_set_ksig_handler_cfg(ksig_handl cfg) |
| { |
| int rc = 0; |
| FILE *fd = fopen("/proc/sys/kernel/print-fatal-signals", "w"); |
| if (fd) { |
| rc = fprintf(fd, "%d", cfg); |
| fclose(fd); |
| } |
| return rc; |
| } |
| void eeh_set_ksig_fatal_silent_kill(void) |
| { eeh_set_ksig_handler_cfg(KSIG_HANDL_KILL_SHORT_PRINT); } |
| |
| void eeh_set_ksig_fatal_panic(void) |
| { eeh_set_ksig_handler_cfg(KSIG_HANDL_PANIC); } |
| |
| void eeh_set_ksig_fatal_freeze(void) |
| { |
| int rc = eeh_set_ksig_handler_cfg(KSIG_HANDL_NO_KILL_FREEZE); |
| if (rc > 0) |
| ERRMSG("No panic on fatal SIG11 since now %d\n", rc); |
| else |
| ERRMSG(" fatal SIG11 set failed with %d\n", rc); |
| } |
| |
| |
| /********************************************************************************** |
| *********************************************************************************** |
| *************** Main Logic and State-Machine ********************** |
| *********************************************************************************** |
| **********************************************************************************/ |
| static void EehTimerStart(int sec, eehTmrRun_e tmr); |
| static void EehTimerStop(void); |
| static void EehDfltFinalAction(int param); |
| |
| /********************************************************************************** |
| * FINAL ACTIONS (per Device (CP, WiFi, GPS) or Default |
| * EehComFinalAction |
| * EehDfltFinalAction |
| * RETURN: EEH_TASK_CONTINUE or EEH_TASK_DONE |
| **********************************************************************************/ |
| |
| static int EehComFinalAction(void) |
| { |
| if(eehCfg.cpSR_max != 0) |
| { |
| EehTimerStop(); |
| if (eehCfg.apSR_onCP) |
| { |
| EehPrint("[CP-ASSERT] ******* Force AP Reset*******"); |
| } |
| else |
| { |
| EehPrint("[CP-ASSERT] ******* Silent Reset*******"); |
| } |
| } |
| else |
| { |
| if (eehCfg.stall_onCP) |
| { |
| EehTimerStop(); |
| EehPrint("[CP-ASSERT] ******* No Reset - STALL *******"); |
| return EEH_TASK_CONTINUE; |
| } |
| |
| //return EEH_TASK_DONE; |
| } |
| |
| #if 1 |
| if (eehCfg.apSR_onCP || (++sm.cpSR_cntr > eehCfg.cpSR_max)) |
| #else |
| if (eehCfg.apSR_onCP) |
| #endif |
| { |
| EehDfltFinalAction(-1); |
| } |
| |
| eeh_set_ksig_fatal_panic(); |
| |
| // CP-SR enabled. Handle it and |
| time_t tm; |
| time(&tm); |
| if (!sm.cpRestartReq) { |
| eeh_event_prt_and_rec(1, 1, "CPSR recovery no.%d start at %s", sm.cpSR_cntr, ctime(&tm)); |
| /* NOTE: ctime() string has '\n' at the end */ |
| HAWK_indication_txt(EEH2HAWK_SIRESET_NOTIFY); |
| HAWK_indication_txt(sm.hawk_buf);//EEH2HAWK_SIRESET_READY_MODE |
| } |
| EehTimerStart(CPSR_RECOVERY_TO_SEC, CPSilentResetTimerId); |
| EehPrint("Start CP Silent Reset Timer......"); |
| |
| //Insert Comm subsystem into Reset |
| if ( EehInsertCommtoReset() != EEH_SUCCESS ) |
| return EEH_TASK_DONE; |
| |
| // msocket_linkdown(); already done |
| |
| #if defined (EE_COPY_COMM_IMAGE_FROM_FLASH) |
| //Copy CP&DSP image from Flash to DDR |
| if (EehCopyCommImageFromFlash() != EEH_NO_ERROR) |
| return EEH_TASK_DONE; |
| #endif |
| |
| /* before release cp, check the crash list, change the process which need to reset cp to no need to reset */ |
| EehChangeResetState(); |
| |
| notify_cp_network_mode_on_dual_simcard(); |
| //Release Comm subsystem from Reset |
| EehReleaseCommFromReset(); |
| |
| if (msocket_linkup() == -1) { |
| EehPrint("CPSR link up failed!!!\n"); |
| return EEH_TASK_DONE; |
| } |
| |
| return EEH_TASK_CONTINUE; |
| } |
| |
| |
| static void EehDfltFinalAction(int param __attribute__ ((unused))) |
| { |
| int try = 5; |
| int ramdumpReadyOnce = 0; |
| /*noreturn*/ |
| sync(); |
| |
| //eehNoSleepLong is long enough, don't call driver if not needed |
| if (eehCfg.ramdump_ena) |
| { |
| while (eehCfg.sd_presence_poll) { |
| if (eeh_sd_card_available(0)) |
| break; |
| if (!ramdumpReadyOnce) { |
| ramdumpReadyOnce++; |
| EehRamdump(0); |
| } |
| EehPrint("*** FINAL: waiting for SD-card presence ***\n"); |
| sleep(10); |
| } |
| EehPrint("*** RAMDUMP Emulate PANIC ***\n"); |
| #if 0 |
| if (!ramdumpReadyOnce) |
| EehRamdump(1); // calls for RESET |
| else |
| #endif |
| EehRamdumpActivate(NULL); |
| } |
| |
| while (eehCfg.stall_FINAL) { |
| EehPrint("FINAL ******* No Reset - STALL *******"); |
| eehNoSleepShort(); |
| sleep(120); |
| } |
| |
| //No RAMDUMP required but regular reboot only |
| //Also, fallback if panic-emulation failed |
| eehNoSleepLong(); |
| while (try--) |
| { |
| //Clear Panic-descriptor placed by SEH.ko |
| // _TBD_ |
| #if defined (BIONIC) |
| reboot(RB_AUTOBOOT); |
| #else |
| system("reboot"); |
| #endif |
| sleep(5); //should never be here |
| } |
| |
| // If still fail to restart, Use "reboot -f" to force |
| // reboot system. |
| sleep(5); |
| system("reboot -f"); |
| } |
| |
| |
| /********************************************************************************** |
| * Main EVENT handlers |
| * EehTimerEventHandling |
| * EehCtrlEventHandling |
| * EehTelStateEventHandling |
| * EehAppEventHandling -> EehWiFiEventHandling, EehGpsEventHandling |
| * EehComMsgEventHandling |
| * EehComEventHandling |
| * See also "second-level" eehConfig_TxtMsgParser |
| * RETURN: EEH_TASK_CONTINUE or EEH_TASK_DONE |
| **********************************************************************************/ |
| |
| /* TBD in eeh_ioctl.h:EehMsgInd and used by SEH.ko */ |
| #define EEH_TIMER_EXPIRED_MSG 0x10 |
| #define EEH_CONTROL_MSG 0x11 |
| #define EEH_ATC_OK_MSG 0x12 |
| /* TEL_OK_FILE presence is a fallback for no EEH_ATC_OK_MSG */ |
| |
| static int EehComEventHandling(EehMsgStruct *msg); |
| |
| static int EehTimerEventHandling(EehMsgStruct *msg __attribute__ ((unused))) |
| { |
| //TBD: all timers should be passed over SEH messages, |
| // Currently full handling done directly inside of EehTimerHandler() |
| return EEH_TASK_CONTINUE; |
| } |
| |
| static void EehTimerHandler(int signum __attribute__ ((unused)), |
| struct siginfo *pInfo __attribute__ ((unused)), |
| void *p __attribute__ ((unused))) |
| { |
| //eehTmrRun_e tmr = .. p; |
| if (sm.currentTimerId == timerStopped) |
| return; |
| if (sm.currentTimerId == CPSilentResetTimerId) { |
| sm.currentTimerId = timerStopped; |
| if (access(TEL_OK_FILE, F_OK) == 0) { |
| time_t tm; |
| time(&tm); |
| sm.inProgress = EEH_NONE_ASSERT; |
| sm.CPASSERT_flag = 0; |
| sm.cpSR_cntr = 0; |
| panicExtraDesc[0] = 0; |
| if (sm.cpRestartReq) { |
| sm.cpRestartReq = 0; |
| eeh_event_prt_and_rec(1, 1, "CP Switch/Restart passed at %s", |
| ctime(&tm)); |
| } else { |
| eeh_event_prt_and_rec(1, 1, "CPSR recovery no.%d passed at %s", |
| sm.cpSR_cntr, ctime(&tm)); |
| } |
| eehNoSleepShort(); |
| } else { |
| eehNoSleepLong(); |
| if (sm.cpRestartReq) { |
| int fd; |
| sm.cpRestartReq++; |
| ERRMSG("CP Switch/Restart TimeOut. Try to CPASSERT\n"); |
| sm.TimeOutPanicDesc = "SwitchRestart_TO"; |
| eehCfg.cpSR_max = 0; |
| eehCfg.logLevel = 4; //Ramdump |
| eehCfg.stall_onCP = 0; |
| EehTimerStart(2, cpAssertReqTimerId); |
| //echo a > /dev/acipc |
| fd = open("/dev/acipc", O_RDWR | O_SYNC); |
| if (fd >= 0) { |
| write(fd, "a", 1); |
| close(fd); |
| return; //don't fail into final action now |
| } |
| } else { |
| ERRMSG("CPSR recovery TimeOut. Go to panic\n"); |
| sm.TimeOutPanicDesc = "CPSR_recoveryTO"; |
| } |
| EehDfltFinalAction(-1); |
| } |
| } |
| else |
| if (sm.currentTimerId == eehGuardTimerId) { |
| ERRMSG("Long Guard Timer %ds (level %d) expired. Go to panic\n", |
| guardTO[sm.logLevel], sm.logLevel); |
| sm.currentTimerId = timerStopped; |
| EehDfltFinalAction(-1); |
| } |
| else |
| if (sm.currentTimerId == cpAssertReqTimerId) { |
| ERRMSG("CP Switch and CPASSERT failed (%d). Go to panic\n", sm.cpRestartReq); |
| sm.currentTimerId = timerStopped; |
| EehDfltFinalAction(-1); |
| } else { |
| ERRMSG("Wrong timer state %d\n", sm.currentTimerId); |
| } |
| } |
| |
| static void EehTimerStart(int sec, eehTmrRun_e tmr) |
| { |
| int rc; |
| struct itimerval interval_timer; |
| sm.currentTimerId = timerStopped; //mark as stop first |
| interval_timer.it_interval.tv_usec = 0; |
| interval_timer.it_interval.tv_sec = 0; |
| interval_timer.it_value.tv_usec = 0; |
| interval_timer.it_value.tv_sec = sec; |
| rc = setitimer(ITIMER_REAL, &interval_timer, NULL); |
| if (rc) |
| ERRMSG("Cannot start timer\n"); |
| else |
| sm.currentTimerId = tmr; |
| } |
| |
| static void EehTimerStop(void) |
| { |
| struct itimerval interval_timer; |
| if (sm.currentTimerId == timerStopped) |
| return; |
| sm.currentTimerId = timerStopped; |
| memset(&interval_timer, 0, sizeof(interval_timer)); |
| setitimer(ITIMER_REAL, &interval_timer, NULL); |
| } |
| |
| static void EehTimerInit(int init) |
| { |
| if (init) { |
| /* set up the handler */ |
| struct sigaction act; |
| act.sa_sigaction = EehTimerHandler; |
| sigemptyset(&act.sa_mask); |
| act.sa_flags = 0; |
| sm.currentTimerId = timerStopped; |
| if (sigaction(SIGALRM, &act, NULL)) |
| ERRMSG("Cannot create timer\n"); |
| } else { |
| EehTimerStop(); |
| } |
| } |
| //-}} TIMER ----------- |
| |
| static int EehCtrlEventHandling(EehMsgStruct *msg __attribute__ ((unused))) |
| { |
| return EEH_TASK_CONTINUE; |
| } |
| |
| static int EehTelStateEventHandling(EehMsgStruct *msg __attribute__ ((unused))) |
| { |
| eeh_event_prt_and_rec(1, 1, "TELEPHONY is OK\n"); |
| HAWK_indication_txt(EEH2HAWK_MTSD_OK); |
| return EEH_TASK_CONTINUE; |
| } |
| |
| static int EehWiFiEventHandling(EehMsgStruct *msg __attribute__ ((unused))) |
| { |
| return EEH_TASK_DONE; |
| } |
| |
| static int EehGpsEventHandling(EehMsgStruct *msg __attribute__ ((unused))) |
| { |
| return EEH_TASK_DONE; |
| } |
| |
| static int EehAppEventHandling(EehMsgStruct *msg) |
| { |
| int ret = 1; //= go to AP-final-action |
| char eeString[512+60]; |
| sprintf(eeString, "[AP] %s", msg->msgDesc); |
| eeh_event_prt_and_rec(1, 1, "%s\n", eeString); |
| |
| if (sm.inProgress == EEH_NONE_ASSERT) { |
| sm.inProgress = EEH_AP_ASSERT; |
| } else { |
| ERRMSG("New ASSERT ignored since previous ongoing\n"); |
| return EEH_TASK_CONTINUE; |
| } |
| EEH_DUMP_ENHANCE_draw_panic(eeString); |
| |
| msocket_linkdown(); |
| |
| HAWK_indication_txt(EEH2HAWK_ASSERT_NOTIFY); |
| |
| //if(level...) |
| eeh_ap_thread_info(1); |
| |
| if (strcasestr(msg->msgDesc, "WiFi")) |
| ret = EehWiFiEventHandling(msg); |
| if (strcasestr(msg->msgDesc, "GPS")) |
| ret = EehGpsEventHandling(msg); |
| |
| return ret; |
| } |
| |
| |
| /***************************************************************** |
| * RETURN: |
| * EEH_TASK_CONTINUE=0 go back to read(msg) main loop |
| * EEH_TASK_DONE exit from loop and fail into EehDfltFinalAction |
| ****************************************************************** |
| */ |
| static int EehComMsgEventHandling(const char *prefix, const char *msgDesc) |
| { |
| int ret; |
| char eeString[512+300]={""}; |
| |
| sm.cpRestartReq = 1; |
| sm.inProgress = EEH_CP_SILENT_RESET; |
| |
| /* For switch modem, reset cpSR_cntr to avoid Emulate PANIC */ |
| sm.cpSR_cntr = 0; |
| |
| unlink(TEL_OK_FILE); |
| if (msgDesc) { |
| snprintf(eeString, 100, "%s %s", prefix, msgDesc); |
| eeh_event_prt_and_rec(1, 1, "%s\n", eeString); |
| } |
| //In CP-assert case the CP is already stopped |
| //The reset executed asynchroneously for data Uplink/Downlink |
| //(including the diag). Stop(!) CP before linkdown |
| //EehInsertCommtoReset(); |
| if (msocket_linkdown()) { |
| ERRMSG(" cannot msocket_linkdown but try go ahead\n"); |
| } |
| //HAWK_indication_txt(EEH2HAWK_RESTART_REQ); |
| ret = EehComFinalAction(); |
| return ret; |
| } |
| |
| static int SaveBmBuffer(void) |
| { |
| #define BM_BUFF_FILE "/sys/kernel/debug/bm/buffer" |
| #define BUF_SIZE (256 * 1024) |
| |
| int fd_src = -1, fd_dst = -1; |
| int ret = -1, nr, nw, total = 0; |
| char *buf = NULL; |
| char dstfile[128]; |
| |
| if (access(BM_BUFF_FILE, F_OK)) |
| return 0; |
| |
| ERRMSG("%s: start...\r\n", __func__); |
| fd_src = open(BM_BUFF_FILE, O_RDONLY); |
| if (fd_src < 0) { |
| ERRMSG("open %s failed.\r\n", BM_BUFF_FILE); |
| goto err; |
| } |
| |
| memset(dstfile, 0, sizeof(dstfile)); |
| sprintf(dstfile, "%s/bm_buffer.bin", eehCfg.logDir); |
| if (access(dstfile, F_OK) == 0) |
| remove(dstfile); |
| |
| fd_dst = open(dstfile, O_CREAT | O_RDWR | O_SYNC, 777); |
| if (fd_dst < 0) { |
| ERRMSG("open %s failed.\r\n", dstfile); |
| goto err; |
| } |
| |
| buf = malloc(BUF_SIZE); |
| if (!buf) { |
| ERRMSG("malloc %d failed.\r\n", BUF_SIZE); |
| goto err; |
| } |
| |
| while (1) { |
| nr = read(fd_src, buf, BUF_SIZE); |
| if (nr < 0) { |
| ERRMSG("Failed to read from %s, nr: %d\r\n", BM_BUFF_FILE, nr); |
| goto err; |
| } |
| |
| if (nr == 0) |
| break; |
| |
| total += nr; |
| nw = write(fd_dst, buf, nr); |
| if (nw != nr) { |
| ERRMSG("Failed to write dst file %s, nw: %d, nr: %d\r\n", dstfile, nw, nr); |
| goto err; |
| } |
| } |
| |
| ret = 0; |
| err: |
| if (fd_src >= 0) close(fd_src); |
| if (fd_dst >= 0) close(fd_dst); |
| if (buf) free(buf); |
| |
| ERRMSG("%s: %s, size: 0x%x\r\n", __func__, ret < 0 ? "FAILED" : "OK", total); |
| return ret; |
| } |
| |
| static int EehComEventHandling(EehMsgStruct *msg __attribute__ ((unused))) |
| { |
| int ret, curr_dir_sd_log = -1; |
| int sd_known_available = -1; //unknown |
| char cmdBuffer[MAX_PATH + 20]; |
| char eeBuffer[512]={""}; |
| char eeString[512+300]={""}; |
| char *crc_txt, *log_dir, *cp_token; |
| static char cpSR_logLevel_bank = 0; |
| |
| ERRMSG("EehComEventHandling: cpSR_max %d, apSR_onCP %d, logLevel %d %d\n", |
| eehCfg.cpSR_max, eehCfg.apSR_onCP, eehCfg.logLevel, eehCfg.cpSR_logLevel); |
| |
| if ((!eeh_sd_card_available(0)) && eehCfg.cpSR_max && (!eehCfg.apSR_onCP)) { |
| ERRMSG("EEH/S-RESET: No sdcard, disable cp log saving: %d\n", eehCfg.cpSR_logLevel); |
| if (eehCfg.cpSR_logLevel) |
| cpSR_logLevel_bank = eehCfg.cpSR_logLevel; |
| eehCfg.cpSR_logLevel = 0; |
| } else { |
| if (cpSR_logLevel_bank && (eehCfg.cpSR_logLevel == 0)) |
| eehCfg.cpSR_logLevel = cpSR_logLevel_bank; |
| } |
| |
| if (sm.inProgress != EEH_NONE_ASSERT) { |
| ERRMSG("EehComEventHandling: inProgress %d, cpRestartReq %d\n", sm.inProgress, sm.cpRestartReq); |
| if (access(TEL_OK_FILE, F_OK)) { |
| if (sm.inProgress == EEH_CP_SILENT_RESET) { |
| ERRMSG("New ASSERT happened in ongoing silent reset\n"); |
| EehTimerStop(); |
| } |
| else |
| { |
| ERRMSG("New ASSERT ignored since previous ongoing\n"); |
| //TBD: add timeout or/and go to panic |
| return EEH_TASK_CONTINUE; |
| } |
| } |
| } |
| |
| if(eehCfg.cpSR_max != 0) |
| { |
| sm.cpRestartReq = 1; |
| sm.inProgress = EEH_CP_SILENT_RESET; |
| } |
| else |
| { |
| sm.inProgress = EEH_CP_ASSERT; |
| } |
| |
| EEH_DUMP_ENHANCE_reset_archive_files(); |
| |
| //Update "working" log-level |
| sm.logLevel = eehCfg.logLevel; |
| if (sm.cpRestartReq > 1) { |
| if (sm.currentTimerId == cpAssertReqTimerId) { |
| EehTimerStop(); |
| ERRMSG("CP Switch failed. CPASSERT activated\n"); |
| } |
| } |
| #if 0 |
| else if (sm.cpRestartReq) { |
| sm.logLevel = 0; |
| } |
| #endif |
| |
| if (eehCfg.cpSR_max && ((sm.cpSR_cntr+1) <= eehCfg.cpSR_max)) { |
| //CPSR enabled and active |
| sm.logLevel = eehCfg.cpSR_logLevel; |
| } else { |
| //Not CPSR but FINAL |
| if (!eehCfg.stall_onCP && (sm.logLevel != EEH_LOGLVL_7_DBG_ARC2SD)) { |
| sd_known_available = eeh_sd_card_available(0); |
| if (!sd_known_available) { |
| if (eehCfg.sd_presence_poll) { |
| ERRMSG("No SD-card. FINAL would wait for insert or command"); |
| } else { |
| //Fallback into legacy mode |
| ERRMSG("No SD-card. Fallback to SaveALL to %s and STALL\n", TEMP_LOG_DIR); |
| eehCfg.stall_onCP = 1; |
| sm.logLevel = EEH_LOGLVL_7_DBG_ARC2SD; |
| } |
| } |
| } |
| } |
| |
| if ((sm.logLevel | 1) == EEH_LOGLVL_9_LOG2SD_ARC2SD) { |
| if (sd_known_available < 0) |
| sd_known_available = eeh_sd_card_available(0); |
| if (!sd_known_available) |
| sm.logLevel = EEH_LOGLVL_7_DBG_ARC2SD; |
| } |
| |
| if (guardTO[sm.logLevel] && eehCfg.guard_timer) {//if(Zero) no timer required |
| if (eehCfg.guard_timer > 1) //this is for "adjust debug" only |
| EehTimerStart(eehCfg.guard_timer, eehGuardTimerId); |
| else |
| EehTimerStart(guardTO[sm.logLevel], eehGuardTimerId); |
| } |
| eeh_set_ksig_fatal_freeze(); //ignore APPS failures |
| unlink(TEL_OK_FILE); |
| crc_txt = eeh_kernel_crc_is_valid(); |
| |
| commImageTableInit(); |
| if (getCommImageBaseAddr() == INVALID_ADDRESS) { |
| // Likely APPS-problem. Go to AppFinalAction |
| eeh_event_prt_and_rec(1, 1, "[CP] assert, description is unavailable\n"); |
| HAWK_indication_txt(EEH2HAWK_ASSERT_NOTIFY); |
| return EEH_TASK_DONE; |
| } |
| /* Print out the CP Error Buffer Description Info */ |
| cp_token = "[CP]"; |
| if (!crc_txt && panicExtraDesc[0]) { |
| crc_txt = panicExtraDesc; |
| if (sm.CPASSERT_flag) |
| cp_token = "[TC]"; |
| } |
| |
| GetComEEDescbuf(eeBuffer, sizeof(eeBuffer), crc_txt); |
| EehSaveErrorInfo(ERR_EEH_CP, eeBuffer, 0); |
| sprintf(eeString, "%s %s", cp_token, eeBuffer); |
| eeh_event_prt_and_rec(1, 1, "%s\n", eeString); |
| HAWK_indication_txt(EEH2HAWK_ASSERT_NOTIFY); |
| eeh_cp_assert_info_rec(eeString); |
| |
| if (!eehCfg.cpSR_max) { |
| EEH_DUMP_ENHANCE_draw_panic(eeString); |
| } |
| |
| // mark linkdown to prevent potential AP failures or CP-SR |
| if (msocket_linkdown()) { |
| EehPrint(" cannot msocket_linkdown but try go ahead\n"); |
| } |
| |
| if (sd_known_available < 0) { |
| sd_known_available = eeh_sd_card_available(0); |
| } |
| |
| ERRMSG("EehComEventHandling: logLevel %d\n", sm.logLevel); |
| |
| // Save files according to Log-Level |
| switch (sm.logLevel) { |
| |
| case EEH_LOGLVL_9_LOG2SD_ARC2SD: |
| case EEH_LOGLVL_8_LOG2SD: |
| case EEH_LOGLVL_7_DBG_ARC2SD: |
| case EEH_LOGLVL_6_DBG: |
| system("rm com_*.bin* diag*.sdl *.txt"); |
| if ((sm.logLevel | 1) == EEH_LOGLVL_9_LOG2SD_ARC2SD) { //8 or 9 |
| SaveComPostmortem(0, 0); |
| ERRMSG("change directory(%s), lev %d\n", LOG_DIR_SD_LOG, sm.logLevel); |
| curr_dir_sd_log = chdir(LOG_DIR_SD_LOG); |
| if (!curr_dir_sd_log) { |
| eehCfg.logDir = (char*)LOG_DIR_SD_LOG; |
| system("rm com_*.bin* diag*.sdl"); |
| } |
| } |
| |
| if (sd_known_available) { |
| if (access(LOG_DIR_SD_LOG, F_OK) != 0) { |
| if(mkdir(LOG_DIR_SD_LOG, 0777) < 0) |
| { |
| ERRMSG("create directory %s error\n", LOG_DIR_SD_LOG); |
| } |
| } |
| |
| curr_dir_sd_log = chdir(LOG_DIR_SD_LOG); |
| if (!curr_dir_sd_log) { |
| eehCfg.logDir = (char*)LOG_DIR_SD_LOG; |
| } else { |
| ERRMSG("change directory(%s) fail, \n", LOG_DIR_SD_LOG); |
| } |
| } |
| |
| if (sd_known_available && !curr_dir_sd_log) |
| SaveBmBuffer(); |
| |
| SaveComPostmortem(0, 2); |
| Linux_EELOG_shareReadFWrite();//log-streaming |
| SaveComPostmortem(3, 100); |
| |
| /* com_DDR_RW.bin. */ |
| if (sd_known_available) { |
| EE_SaveComDDR_RW(1); |
| } |
| |
| commImageTableFree(); //free memory asap |
| eeh_ap_thread_info(1); |
| |
| #ifdef DIAG_FS_MOUNT_POINT |
| //In nevo platform, diag over fs will save CP log in /tmp/acat, when CP assert happen, copy it to /data/acat directory. |
| //Since in 920 platform, diag over fs already save CP log in /data/acat, so below insure have no effect in 920 platform. |
| if((access(DIAG_FS_MOUNT_POINT, F_OK) == 0) && (strncmp(DIAG_FS_MOUNT_POINT, "/data/", strlen("/data/")))) |
| { |
| snprintf(cmdBuffer, sizeof(cmdBuffer), "cp -rf %s /data", DIAG_FS_MOUNT_POINT); |
| system(cmdBuffer); |
| } |
| #endif |
| // Create "end-of-logging" file-marker = com_WDT_END.bin |
| system("date > com_WDT_END.bin"); |
| system("dmesg > dmesg.txt"); |
| system("cat /proc/cmdline > cmdline.txt"); |
| system("touch ubootlog.txt"); |
| system("cat /proc/boot_log > ubootlog.txt"); |
| record_archive_files(NULL, "dmesg.txt"); |
| record_archive_files(NULL, "ubootlog.txt"); |
| break; |
| |
| |
| case EEH_LOGLVL_5_RDP_ARC2SD: |
| case EEH_LOGLVL_4_RDP: |
| Linux_EELOG_shareReadFWrite();//log-streaming |
| commImageTableFree(); |
| eeh_ap_thread_info(1); |
| #ifdef DIAG_FS_MOUNT_POINT |
| //In nevo platform, diag over fs will save CP log in /tmp/acat, when CP assert happen, copy it to /data/acat directory. |
| //Since in 920 platform, diag over fs already save CP log in /data/acat, so below insure have no effect in 920 platform. |
| if((access(DIAG_FS_MOUNT_POINT, F_OK) == 0) && (strncmp(DIAG_FS_MOUNT_POINT, "/data/", strlen("/data/")))) |
| { |
| snprintf(cmdBuffer, sizeof(cmdBuffer), "cp -rf %s /data", DIAG_FS_MOUNT_POINT); |
| system(cmdBuffer); |
| } |
| #endif |
| break; |
| |
| case EEH_LOGLVL_3_CPSR_ARC2SD: |
| case EEH_LOGLVL_2_CPSR: |
| Linux_EELOG_shareReadFWrite();//log-streaming |
| commImageTableFree(); |
| system("dmesg > dmesg.txt"); |
| system("touch ubootlog.txt"); |
| system("cat /proc/boot_log > ubootlog.txt"); |
| break; |
| |
| default: |
| set_ap_dump_flag(); |
| ERRMSG("unknown logLevel=%d handled as level=%d\n", sm.logLevel, 1); |
| case EEH_LOGLVL_1_ARC2SD: |
| SaveComPostmortem(0, 0); |
| set_ap_dump_flag(); |
| case EEH_LOGLVL_0: |
| commImageTableFree(); |
| set_ap_dump_flag(); |
| break; |
| |
| }//END switch(logLevel) |
| |
| if (!curr_dir_sd_log) |
| system("cp " EEH_JOURNAL_FILE " ."); //copy from flash/log into sd/log |
| |
| if (eehCfg.hawk_ena) { |
| //Here should be HAWK saving procedure |
| } else { |
| if (EEH_LOGLVL_WITH_ARC2SD(sm.logLevel)) { |
| if ((sm.logLevel == EEH_LOGLVL_7_DBG_ARC2SD) || |
| (sm.logLevel == EEH_LOGLVL_9_LOG2SD_ARC2SD)) { |
| |
| //WA: arc for com_DDR_RW.bin takes several minutes |
| // or never finished. Exclude it by rename |
| int wa = -1; |
| |
| EEH_DUMP_ENHANCE_record_archive_files(eeString); |
| if (!sd_known_available) |
| { |
| wa = rename("com_RAMLOG.bin", "com_RAMLOG.bin" "_tmp"); |
| if (!wa) |
| rename("com_RAMLOG.bin" "_tmp", "com_RAMLOG.bin"); |
| } |
| else |
| { |
| wa = rename("com_DDR_RW.bin", "com_DDR_RW.bin" "_tmp"); |
| if (!wa) |
| rename("com_DDR_RW.bin" "_tmp", "com_DDR_RW.bin"); |
| } |
| |
| } else { |
| EEH_DUMP_ENHANCE_record_archive_files(eeString); //arc files |
| } |
| } |
| } |
| log_dir = eehCfg.logDir; |
| if (!curr_dir_sd_log) { |
| chdir(LOG_DIR); //restore back to default |
| eehCfg.logDir = (char*)LOG_DIR; |
| |
| if (sm.logLevel == EEH_LOGLVL_8_LOG2SD) { |
| //All logs (including huge) saved in sd/log directory. |
| //Rename sd/log to sd/log_000_mm-dd_hh.mm.ss |
| eeh_log_dir_name_generate(cmdBuffer, sizeof(cmdBuffer), 0); |
| ERRMSG("rename directory(%s)\n", cmdBuffer); |
| rename(LOG_DIR_SD_LOG, cmdBuffer); |
| mkdir(LOG_DIR_SD_LOG, 0777); |
| log_dir = cmdBuffer; |
| } |
| } |
| //commImageTableFree(); |
| eeh_event_prt_and_rec(1, 1, "[EEH] Save CP dump bins done to %s\n", log_dir); |
| |
| ret = EehComFinalAction(); |
| return ret; |
| } |
| |
| |
| /**************************************************************************** |
| * EehTask : Error handler TASK (main logic) |
| * EehInit |
| * EehDeinit |
| ****************************************************************************/ |
| static void *EehTask(void* arg) |
| { |
| UNUSEDPARAM(arg) |
| EehMsgStruct msg; |
| int ret = 0; |
| |
| //Detach is like process-fork. Don't use |
| // pthread_detach(pthread_self()); |
| prctl(PR_SET_NAME, "EehTask"); /* equal pthread_setname_np(pthread_self(),"EehTask");*/ |
| DBGMSG("%s is running\n", __FUNCTION__); |
| eeh_event_prt_and_rec(0, 1, "Error Handler Task started\n"); |
| HAWK_indication_txt(EEH2HAWK_BOOT); |
| |
| while (!ret) |
| { |
| ret = read(sehdrv_fd, &msg, sizeof(EehMsgStruct)); |
| /* eehNoSleepLong(); -- already done by the /dev/seh */ |
| if (ret != sizeof(EehMsgStruct)) { |
| ERRMSG("read fail (ret=%d/errno=%d, msgId=%d)\n", ret, errno, msg.msgId); |
| //EEH failed. go to FinalAction |
| ret = -1; |
| break; |
| //ret = 0; continue; |
| } |
| msg.msgDesc[EEH_MSG_DESC_LEN - 1] = '\0'; |
| switch (msg.msgId) |
| { |
| case EEH_TIMER_EXPIRED_MSG: |
| ret = EehTimerEventHandling(&msg); |
| break; |
| |
| case EEH_CONTROL_MSG: |
| ret = EehCtrlEventHandling(&msg); |
| break; |
| |
| case EEH_ATC_OK_MSG: |
| ret = EehTelStateEventHandling(&msg); |
| break; |
| |
| case EEH_AP_ASSERT_MSG: |
| case EEH_AP_CRASH_MSG: //to be obsolet |
| ret = EehAppEventHandling(&msg); |
| break; |
| |
| case EEH_CP_SILENT_RESET_MSG: |
| // This is txt-message from other processes |
| // Use "second-level" eehConfig_TxtMsgParser |
| ret = eeh_is_enabled_by_ramdump(); |
| if (!ret) |
| break; //ret=0, ignore the message |
| |
| msg.msgDesc[EEH_MSG_DESC_LEN - 1] = '\0'; |
| switch (eehConfig_TxtMsgParser(msg.msgDesc, msg.force)) { |
| /* |
| * if (ret==0) then EEH_TASK_CONTINUE in the while() |
| * else {while(!ret)} aborted with goto EehDfltFinalAction() |
| */ |
| case EEH_MSG_SWITCHMODEM: |
| ret = EehComMsgEventHandling("CP", msg.msgDesc); |
| break; |
| case EEH_MSG_WIFI_ASSERT: |
| if (++sm.wifiSR_cntr > eehCfg.wifiSR_max) { |
| ret = EEH_TASK_DONE; |
| break; |
| } |
| HAWK_indication_txt(msg.msgDesc); |
| #define MODEM_RESET_ON_WIFI_RESET 0 |
| if (MODEM_RESET_ON_WIFI_RESET) { |
| ret = weh_do_silent_reset(0); |
| ret = EehComMsgEventHandling("WIFI", NULL); |
| ret = weh_do_silent_reset(1); |
| } else { |
| ret = weh_do_silent_reset(2); |
| } |
| break; |
| default: |
| ret = EEH_TASK_CONTINUE; |
| break; |
| } |
| break; |
| |
| case EEH_WDT_INT_MSG: |
| ret = eeh_is_enabled_by_ramdump(); |
| if (!ret) |
| break; //ret=0, ignore the message |
| ret = EehComEventHandling(&msg); |
| break; |
| |
| default: |
| ERRMSG("Invalid message =%d\n", msg.msgId); |
| ret = -1; //goto EehDfltFinalAction() |
| break; |
| } |
| |
| if (!ret/*=EEH_TASK_CONTINUE=0*/ && (sm.currentTimerId == timerStopped)) |
| eehNoSleepShort(); |
| } |
| //ERRMSG("wait-loop exit\n");//dbg |
| EehDfltFinalAction(ret); |
| return NULL; |
| } |
| |
| #define DSP_LOAD_FNAME "/dev/dsp_load" |
| |
| /**************************************************************************** |
| * DspLoadTask : DSP LOAD TASK (main logic) |
| ****************************************************************************/ |
| static void *DspLoadTask(void* arg) |
| { |
| UNUSEDPARAM(arg) |
| int ret = 0; |
| char buffer[4]; |
| int dsp_load_fd = -1; |
| struct cp_load_table_head *hdr; |
| |
| prctl(PR_SET_NAME, "DspLoadTask"); |
| DBGMSG("%s is running\n", __FUNCTION__); |
| |
| dsp_load_fd = open(DSP_LOAD_FNAME, O_RDWR | O_SYNC); |
| if (dsp_load_fd < 0) { |
| pxa_dbg("open dsp load file failed\n"); |
| return NULL; |
| } |
| |
| while (1) |
| { |
| ret = read(dsp_load_fd, buffer, 4); |
| DBGMSG("%s get load req: %d\n", __FUNCTION__, ret); |
| /* -1, -1 means have not prepared the current & exist cp property values, |
| * it means need to get these two property values from property_get */ |
| if ((hdr = cp_load_table_init()) == NULL) { |
| pxa_dbg("dsp_load: cp_load_table_init failed\n"); |
| sleep(1); |
| continue; |
| } |
| |
| DBGMSG("%s load table ok\n", __FUNCTION__); |
| ret = set_cp_type_property(hdr); |
| if (ret == -1) |
| { |
| cp_load_table_exit(hdr); |
| pxa_dbg("dsp_load: set_cp_type_property failed\n"); |
| sleep(1); |
| continue; |
| } |
| DBGMSG("%s get prop ok\n", __FUNCTION__); |
| /* MSA must be loaded before RF */ |
| if ( |
| #if defined(CONFIG_ASR1901_EEH) || defined(CONFIG_ASR1903_EEH) |
| cp_load_bx2_image(hdr, 0) == -1 || |
| #endif |
| #ifndef CONFIG_ASR1903_EEH |
| cp_load_msa_image(hdr, 0) == -1 || |
| #endif |
| cp_load_rf_image(hdr, 0) == -1) |
| { |
| cp_load_table_exit(hdr); |
| pxa_dbg("dsp_load: dsp/rf image loading failed\n"); |
| sleep(1); |
| continue; |
| } |
| DBGMSG("%s image loading ok\n", __FUNCTION__); |
| write(dsp_load_fd, buffer, 4); |
| cp_load_table_exit(hdr); |
| } |
| |
| return NULL; |
| } |
| |
| /********************************************************/ |
| EEH_STATUS EehInit(int createPthread) |
| { |
| int ret, ramdump_level; |
| EehApiParams ApiParams; |
| EehGetCPLoadAddrParam param; |
| const char *errmsg = NULL; |
| pthread_t eeh_task; |
| pthread_t dsp_load_task; |
| |
| sm.inProgress = EEH_NONE_ASSERT; |
| ramdump_level = read_ramdump_level(); |
| sm.ramdump_level = (ramdump_level >= 0) ? ramdump_level : 2; |
| |
| eehConfigInit(); |
| |
| #ifdef HAWK_ENABLED |
| { |
| sm.hawk_enable = eehCfg.hawk_ena; |
| if (sm.hawk_enable && eehCfg.cpSR_max) { |
| sm.hawk_buf = malloc(200); |
| //construct msg once only |
| snprintf(sm.hawk_buf, 200, |
| EEH2HAWK_ASSERT_DETECTED, EEH2HAWK_SIRESET_READY_MODE); |
| } |
| } |
| #endif |
| |
| EEH_DUMP_ENHANCE_reset_archive_files(); |
| |
| // open driver if it still not opened (by main()) |
| // Do it BEFORE pthread_create(EehTask) which also uses it |
| if (sehdrv_fd < 0) |
| sehdrv_fd = open(EEH_DEVICE, O_RDWR | O_SYNC); |
| if (sehdrv_fd < 0) { |
| errmsg = "open seh-driver"; |
| goto retErr; |
| } |
| |
| EehTimerInit(1); |
| |
| if (createPthread) { |
| ret = pthread_create(&eeh_task, NULL, (void *)EehTask, NULL); |
| if (ret) { |
| errmsg = "create EehTask"; |
| goto retErr; |
| } |
| } |
| |
| ret = pthread_create(&dsp_load_task, NULL, (void *)DspLoadTask, NULL); |
| if (ret) { |
| errmsg = "create DspLoadTask"; |
| goto retErr; |
| } |
| |
| /* else |
| * Every thread requires 16*2 contiguous memory |
| * so it is better to run on the main() thread |
| * TBD with main() below inits modification and call |
| * EehTask(NULL); from here |
| */ |
| ApiParams.eehApiId = _EehGetCPLoadAddr; |
| ApiParams.params = ¶m; |
| ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams); |
| if (ret) { |
| errmsg = "ioctl"; |
| goto retErr; |
| } |
| PRE_LOADTABLE_PHYADDR = param.arbel_load_addr; |
| return EEH_SUCCESS; |
| retErr: |
| ERRMSG("%s ERROR: %s\n", __FUNCTION__, errmsg); |
| return EEH_ERROR_INIT; |
| } |
| |
| void eeh_send_to_hawk(char *msg) |
| { |
| HAWK_indication_txt(msg); |
| } |
| /********************************************************/ |
| EEH_STATUS EehDeinit (void) |
| { |
| int ret; |
| EehApiParams ApiParams; |
| EehTimerInit(0); |
| ApiParams.eehApiId = _EehDeInit; |
| ApiParams.params = NULL; |
| ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams); |
| if (ret) |
| return EEH_ERROR; |
| return ApiParams.status; |
| } |
| |
| struct sleep_log_unit { |
| UINT32 ulTickHigh; |
| UINT32 ulTickLow; |
| UINT16 usRsv; |
| UINT16 usSleepState; |
| }; |
| |
| struct sleep_log_buf { |
| UINT32 ulTotal; |
| UINT32 ulLast; |
| UINT32 ulRsv; |
| struct sleep_log_unit stBuf[0]; |
| }; |
| |
| void ee_convert_sleep_record(unsigned char *buf, unsigned int bufLen, double threshold, FILE *fpTxt) |
| { |
| unsigned int lrn; //last record number |
| unsigned int trn; //total record number |
| unsigned int crn; //current record number |
| UINT32 max_record_num = bufLen / sizeof(struct sleep_log_unit) - 1; |
| |
| struct sleep_log_buf *logger = (struct sleep_log_buf *)buf; |
| struct sleep_log_unit *pCur, *pNext; |
| |
| UINT64 curTick, nextTick, curDurationTick; |
| double curTime, nextTime, curDurationTime; |
| double startTime, endTime; |
| UINT64 sleepTicks = 0, awakeTicks = 0; |
| double sleepTimes = 0, awakeTimes = 0; |
| |
| trn = logger->ulTotal; |
| lrn = logger->ulLast; |
| crn = (lrn + 1) % max_record_num; // lrn is the last valid record, so lrn+1 is the oldest valid record |
| |
| pCur = &logger->stBuf[crn]; |
| curTick = (UINT64)pCur->ulTickHigh << 32 | pCur->ulTickLow; |
| crn = (crn + 1) % max_record_num; |
| |
| //locate first record |
| while (1) { |
| pNext = &logger->stBuf[crn]; |
| nextTick = (UINT64)pNext->ulTickHigh << 32 | pNext->ulTickLow; |
| if (nextTick < curTick) { |
| nextTime = (double)nextTick/TICKSPER13M; //nextTick is the first record |
| startTime = nextTime; |
| break; |
| } |
| curTick = nextTick; //record curTick as nextTick |
| crn = (crn + 1) % max_record_num; //move to next crn |
| }; |
| |
| fprintf(fpTxt, "================= Client:MPSS =================\n"); |
| fprintf(fpTxt, " Time sclk \t Time sec \tState\tDuration\n"); |
| trn = max_record_num - 1; |
| while (trn-- > 0) { |
| pCur = pNext; |
| curTick = nextTick; |
| curTime = nextTime; |
| crn = (crn + 1) % max_record_num; |
| pNext = &logger->stBuf[crn]; |
| nextTick = (UINT64)pNext->ulTickHigh << 32 | pNext->ulTickLow; |
| curDurationTick = (INT64)nextTick - (INT64)curTick; |
| curDurationTime = (double)curDurationTick/TICKSPER13M; |
| nextTime = curTime + curDurationTime; |
| if (pCur->usSleepState == SLEEP_STATE_WAKE_UP) { |
| awakeTicks += curDurationTick; |
| awakeTimes += curDurationTime; |
| } else { |
| sleepTicks += curDurationTick; |
| sleepTimes += curDurationTime; |
| } |
| if (curTime > 0 && nextTime - curTime > threshold && pCur->usSleepState == SLEEP_STATE_WAKE_UP) |
| fprintf(fpTxt, "%020llu\t%.6lf\t%5d\t%.6lf <- NOTICE! AWAKE EXCEED THRESHOLD: %lf\n", curTick, curTime, pCur->usSleepState, curDurationTime, threshold); |
| else |
| fprintf(fpTxt, "%020llu\t%.6lf\t%5d\t%.6lf\n", curTick, curTime, pCur->usSleepState, curDurationTime); |
| }; |
| |
| endTime = nextTime; |
| fprintf(fpTxt, "\n"); |
| fprintf(fpTxt, "Log available: %.6lf ~ %.6lf (sec)\n", startTime, endTime); |
| fprintf(fpTxt, "Time Sleep: %20llu (%.6lf sec)\n", sleepTicks, sleepTimes); |
| fprintf(fpTxt, "Time Awake: %20llu (%.6lf sec)\n", awakeTicks, awakeTimes); |
| fprintf(fpTxt, "Sleep: %.6lf%% Awake: %.6lf%%\n", 100.0 * sleepTicks / (sleepTicks + awakeTicks), 100.0 * awakeTicks / (sleepTicks + awakeTicks)); |
| } |
| |
| int ee_convert_sleep_record_file(const char *fileBin, const char *fileTxt, double threshold) |
| { |
| FILE *fpBin, *fpTxt; |
| long sz, len; |
| unsigned char *buf = NULL; |
| |
| if ((fpBin = fopen(fileBin, "rb")) == NULL) { |
| ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileBin); |
| goto rferr; |
| } |
| if ((fpTxt = fopen(fileTxt, "w")) == NULL) { |
| ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileTxt); |
| goto wferr; |
| } |
| |
| /* get binary file length */ |
| fseek(fpBin, 0L, SEEK_END); |
| sz = ftell(fpBin); |
| if (sz < 0) { |
| ERRMSG("%s: get file: %s size error\n", __FUNCTION__, fileBin); |
| goto szerr; |
| } |
| |
| buf = malloc(sz); |
| if (!buf) { |
| ERRMSG("%s: malloc error\n", __FUNCTION__); |
| goto memerr; |
| } |
| /* read binary file to buf */ |
| fseek(fpBin, 0L, SEEK_SET); |
| if ((len = fread(buf, 1, sz, fpBin)) != sz) { |
| ERRMSG("%s: failed to read %ld bytes from %s, ret=%ld\n", __FUNCTION__, sz, fileBin, len); |
| goto readerr; |
| } |
| |
| /* convert buf and write to text file */ |
| ee_convert_sleep_record(buf, len, threshold, fpTxt); |
| |
| /* release resources */ |
| free(buf); |
| fclose(fpTxt); |
| fclose(fpBin); |
| |
| return 0; |
| |
| readerr: |
| free(buf); |
| memerr: |
| szerr: |
| fclose(fpTxt); |
| wferr: |
| fclose(fpBin); |
| rferr: |
| return -1; |
| } |
| |
| struct sleep_observer_unit { |
| UINT32 ulTick; |
| UINT32 ulCount; |
| UINT8 ucResource[8]; |
| UINT8 ucClient[8]; |
| }; |
| |
| struct sleep_observer_buf { |
| UINT32 ulTotal; |
| UINT32 ulLast; |
| UINT8 ucRsv[16]; |
| struct sleep_observer_unit stBuf[0]; |
| }; |
| |
| void ee_convert_observer_record(unsigned char *buf, unsigned int bufLen, FILE *fpTxt) |
| { |
| unsigned int lrn; //last record number |
| unsigned int trn; //total record number |
| unsigned int crn; //current record number |
| UINT32 max_record_num = bufLen / sizeof(struct sleep_observer_unit) - 1; |
| |
| struct sleep_observer_buf *observer = (struct sleep_observer_buf *)buf; |
| struct sleep_observer_unit *pCur; |
| UINT32 curTick, prevTick; |
| |
| trn = observer->ulTotal; |
| lrn = observer->ulLast; |
| crn = (lrn + 1) % max_record_num; // lrn is the last valid record, so lrn+1 is the oldest valid record |
| |
| pCur = &observer->stBuf[crn]; |
| curTick = pCur->ulTick; |
| crn = (crn + 1) % max_record_num; |
| |
| //locate first record |
| while (1) { |
| prevTick = curTick; |
| pCur = &observer->stBuf[crn]; |
| curTick = pCur->ulTick; |
| if (curTick < prevTick) //cur record is the first record |
| break; |
| crn = (crn + 1) % max_record_num; |
| }; |
| |
| fprintf(fpTxt, "================= Client:OBSERVER =================\n"); |
| fprintf(fpTxt, " Time sclk \t Time sec \tData\n"); |
| trn = max_record_num - 1; |
| while (trn-- > 0) { |
| pCur = &observer->stBuf[crn]; |
| crn = (crn + 1) % max_record_num; |
| fprintf(fpTxt, "%012u\t%.6lf\t%8s\t%8s\n", pCur->ulTick, (double)pCur->ulTick/TICKSPER32K, pCur->ucResource, pCur->ucClient); |
| }; |
| } |
| int ee_convert_observer_record_file(const char *fileBin, const char *fileTxt) |
| { |
| FILE *fpBin, *fpTxt; |
| long sz, len; |
| unsigned char *buf = NULL; |
| |
| if ((fpBin = fopen(fileBin, "rb")) == NULL) { |
| ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileBin); |
| goto rferr; |
| } |
| |
| if ((fpTxt = fopen(fileTxt, "w")) == NULL) { |
| ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileTxt); |
| goto wferr; |
| } |
| |
| /* get binary file length */ |
| fseek(fpBin, 0L, SEEK_END); |
| sz = ftell(fpBin); |
| if (sz < 0) { |
| ERRMSG("%s: get file: %s size error\n", __FUNCTION__, fileBin); |
| goto szerr; |
| } |
| buf = malloc(sz); |
| if (!buf) { |
| ERRMSG("%s: malloc error\n", __FUNCTION__); |
| goto memerr; |
| } |
| |
| /* read binary file to buf */ |
| fseek(fpBin, 0L, SEEK_SET); |
| if ((len = fread(buf, 1, sz, fpBin)) != sz) { |
| ERRMSG("%s: failed to read %ld bytes from %s, ret=%ld\n", __FUNCTION__, sz, fileBin, len); |
| goto readerr; |
| } |
| |
| /* convert buf and write to text file */ |
| ee_convert_observer_record(buf, len, fpTxt); |
| |
| /* release resources */ |
| free(buf); |
| fclose(fpTxt); |
| fclose(fpBin); |
| |
| return 0; |
| |
| readerr: |
| free(buf); |
| memerr: |
| szerr: |
| fclose(fpTxt); |
| wferr: |
| fclose(fpBin); |
| rferr: |
| return -1; |
| } |
| |