/*************************************************************************** * * Copyright 2015-2020 BES. * All rights reserved. All unpublished rights reserved. * * No part of this work may be used or reproduced in any form or by any * means, or stored in a database or retrieval system, without prior written * permission of BES. * * Use of this work is governed by a license granted by BES. * This work contains confidential and proprietary information of * BES. which is protected by copyright, trade secret, * trademark and other intellectual property rights. * ****************************************************************************/ #include #include #include "hal_trace.h" #include "log_section.h" #include #include "hal_timer.h" #include "hal_norflash.h" #include "norflash_api.h" #include "cmsis.h" #include "hal_cache.h" #include "hal_sleep.h" #include "pmu.h" // #define DUMP_NO_ROLLBACK #define LOG_DUMP_PREFIX "__LOG_DUMP:" #if 0 #define LOG_DUMP_TRACE(num,fmt, ...) TRACE(num,fmt, ##__VA_ARGS__) #else #define LOG_DUMP_TRACE(num,fmt, ...) #endif #define LOG_DUMP_TRACE_FORCE(num,fmt, ...) TRACE(num,fmt, ##__VA_ARGS__) #define DUMP_LOG_ALIGN(x,a) (uint32_t)(((x + a - 1)/a) * a) extern uint32_t __log_dump_start; extern uint32_t __log_dump_end; extern void watchdog_ping(void); static const uint32_t log_dump_flash_start_addr = (uint32_t)&__log_dump_start; static const uint32_t log_dump_flash_end_addr = (uint32_t)&__log_dump_end; static uint32_t log_dump_flash_len; static DATA_BUFFER data_buffer_list[LOG_DUMP_SECTOR_BUFFER_COUNT]; static uint32_t log_dump_w_seqnum = 0; static uint32_t log_dump_f_seqnum = 0; static uint32_t log_dump_flash_offs = 0; static uint32_t log_dump_cur_dump_seqnum; static bool log_dump_is_init = false; static bool log_dump_is_immediately; static bool log_dump_is_bootup = true; static enum LOG_DUMP_FLUSH_STATE log_dump_flash_state = LOG_DUMP_FLASH_STATE_IDLE; static enum NORFLASH_API_RET_T _flash_api_read(uint32_t addr, uint8_t* buffer, uint32_t len) { return norflash_sync_read(NORFLASH_API_MODULE_ID_LOG_DUMP, addr, buffer, len); } static void _flash_api_flush(void) { hal_trace_pause(); do { norflash_api_flush(); } while(!norflash_api_buffer_is_free(NORFLASH_API_MODULE_ID_LOG_DUMP)); hal_trace_continue(); } static enum NORFLASH_API_RET_T _flash_api_erase(uint32_t addr, bool is_async) { uint32_t lock; enum NORFLASH_API_RET_T ret = NORFLASH_API_OK; if(log_dump_is_immediately) { is_async = false; } do { lock = int_lock_global(); hal_trace_pause(); ret = norflash_api_erase(NORFLASH_API_MODULE_ID_LOG_DUMP, addr, LOG_DUMP_SECTOR_SIZE, is_async); hal_trace_continue(); int_unlock_global(lock); if (NORFLASH_API_OK == ret) { // LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"%s: norflash_api_erase ok!",__func__); break; } else if(NORFLASH_API_BUFFER_FULL == ret) { norflash_api_flush(); if(is_async) { break; } } else { ASSERT(0, "_flash_api_erase: norflash_api_erase failed. ret = %d,addr = 0x%x.", ret,addr); } } while (1); return ret; } static enum NORFLASH_API_RET_T _flash_api_write(uint32_t addr, uint8_t* ptr, uint32_t len, bool is_async) { uint32_t lock; enum NORFLASH_API_RET_T ret = NORFLASH_API_OK; if(log_dump_is_immediately) { is_async = false; } do { lock = int_lock_global(); hal_trace_pause(); ret = norflash_api_write(NORFLASH_API_MODULE_ID_LOG_DUMP, addr, ptr, len, is_async); hal_trace_continue(); int_unlock_global(lock); if (NORFLASH_API_OK == ret) { // LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"%s: norflash_api_write ok!",__func__); break; } else if (NORFLASH_API_BUFFER_FULL == ret) { norflash_api_flush(); if(is_async) { LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"%s: norflash api buffer full",__func__); break; } } else { ASSERT(0,"_flash_api_write: norflash_api_write failed. ret = %d",ret); } }while (1); return ret; } static int32_t _get_seqnum(uint32_t * dump_seqnum, uint32_t *sector_seqnum) { #ifndef DUMP_NO_ROLLBACK uint32_t i; uint32_t count; static enum NORFLASH_API_RET_T result; LOG_DUMP_HEADER log_dump_header; uint32_t max_dump_seqnum = 0; uint32_t max_sector_seqnum = 0; bool is_existed = false; count = (log_dump_flash_end_addr-log_dump_flash_start_addr)/LOG_DUMP_SECTOR_SIZE; for(i = 0; i < count; i++) { result = _flash_api_read( log_dump_flash_start_addr + i*LOG_DUMP_SECTOR_SIZE, (uint8_t*)&log_dump_header, sizeof(LOG_DUMP_HEADER)); if(result == NORFLASH_API_OK) { if(log_dump_header.magic == DUMP_LOG_MAGIC && log_dump_header.version == DUMP_LOG_VERSION ) { is_existed = true; if(log_dump_header.seqnum > max_dump_seqnum) { max_dump_seqnum = log_dump_header.seqnum; max_sector_seqnum = i; } } } else { ASSERT(0,"_get_cur_sector_seqnum: _flash_api_read failed!result = %d.",result); } } if(is_existed) { *dump_seqnum = max_dump_seqnum + 1; *sector_seqnum = max_sector_seqnum + 1 >= count ? 0: max_sector_seqnum + 1; } else { *dump_seqnum = 0; *sector_seqnum = 0; } #else { LOG_DUMP_HEADER log_dump_header; _flash_api_read( log_dump_flash_start_addr, (uint8_t*)&log_dump_header, sizeof(LOG_DUMP_HEADER)); *dump_seqnum = 0; *sector_seqnum = 0; } #endif return 0; } static int _log_dump_flush(uint32_t buff_state,bool is_async) { enum NORFLASH_API_RET_T result; static uint32_t total_len = 0; DATA_BUFFER *data_buff; if(log_dump_flash_state == LOG_DUMP_FLASH_STATE_IDLE && (log_dump_flash_offs % LOG_DUMP_SECTOR_SIZE) == 0) { LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s:%d,state = idle,addr = 0x%x.", __func__,__LINE__,log_dump_flash_start_addr + log_dump_flash_offs); result = _flash_api_erase(log_dump_flash_start_addr + log_dump_flash_offs,is_async); if(result == NORFLASH_API_OK) { LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s:%d,erase ok,addr = 0x%x,", __func__,__LINE__,log_dump_flash_start_addr + log_dump_flash_offs); log_dump_flash_state = LOG_DUMP_FLASH_STATE_ERASED; } else { LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s: _flash_api_erase failed!addr = 0x%x,ret = %d.", __func__,log_dump_flash_start_addr + log_dump_flash_offs, result); } } else if(log_dump_flash_state == LOG_DUMP_FLASH_STATE_ERASED || log_dump_flash_state == LOG_DUMP_FLASH_STATE_WRITTING) { data_buff = &data_buffer_list[log_dump_f_seqnum]; if((data_buff->state & buff_state) != 0 && data_buff->offset > 0) { LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s:%d,state = ERASED,f_seqnum = %d.", __func__,__LINE__,log_dump_f_seqnum); log_dump_flash_state = LOG_DUMP_FLASH_STATE_WRITTING; result = _flash_api_write( log_dump_flash_start_addr + log_dump_flash_offs, data_buff->buffer, data_buff->offset, is_async); if(result == NORFLASH_API_OK) { LOG_DUMP_TRACE(5,LOG_DUMP_PREFIX"%s:%d,write ok,addr = 0x%x,f_seqnum = 0x%x,total_len = 0x%x.", __func__,__LINE__, log_dump_flash_start_addr + log_dump_flash_offs, log_dump_f_seqnum, total_len); data_buff->state = DATA_BUFFER_STATE_FREE; log_dump_f_seqnum = log_dump_f_seqnum + 1 == LOG_DUMP_SECTOR_BUFFER_COUNT ? 0 : log_dump_f_seqnum + 1; total_len += data_buff->offset; log_dump_flash_offs = log_dump_flash_offs + data_buff->offset >= log_dump_flash_len ? 0 : log_dump_flash_offs + data_buff->offset; log_dump_flash_state = LOG_DUMP_FLASH_STATE_IDLE; } else { LOG_DUMP_TRACE(2,LOG_DUMP_PREFIX"%s: _flash_api_write failed!ret = %d.",__func__,result); return 3; } } } else { LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s:%d state = %d.", __func__,__LINE__,log_dump_flash_state); } return 0; } void _log_dump_flush_remain(void) { uint32_t i; uint32_t unfree_count = 0; do{ unfree_count = 0; for(i = 0; i < LOG_DUMP_SECTOR_BUFFER_COUNT; i++) { if(data_buffer_list[i].state != DATA_BUFFER_STATE_FREE) { unfree_count ++; } } if(unfree_count == 0) { break; } _log_dump_flush(DATA_BUFFER_STATE_WRITTING | DATA_BUFFER_STATE_WRITTEN,false); }while(1); _flash_api_flush(); } int log_dump_flush_all(void) { norflash_api_flush_enable_all(); log_dump_is_immediately = true; _log_dump_flush_remain(); return 0; } int log_dump_flush(void) { if(!log_dump_is_init) { return 0; } return _log_dump_flush(DATA_BUFFER_STATE_WRITTEN, true); } void log_dump_notify_handler(enum HAL_TRACE_STATE_T state) { //uint32_t lock; if(!log_dump_is_init) { return; } LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s: state = %d,start_addr = 0x%x,end_addr = 0x%x.", __func__,state,log_dump_flash_start_addr,log_dump_flash_end_addr); LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s: dump_seqnum = 0x%x,flash_offset = 0x%x.", __func__,log_dump_cur_dump_seqnum,log_dump_flash_offs); if(state == HAL_TRACE_STATE_CRASH_ASSERT_START || state == HAL_TRACE_STATE_CRASH_FAULT_START) { norflash_api_flush_enable_all(); log_dump_is_immediately = true; } else { LOG_DUMP_TRACE(2,LOG_DUMP_PREFIX" crash end."); //lock = int_lock_global(); _log_dump_flush_remain(); //int_unlock_global(lock); } } void log_dump_output_handler(const unsigned char *buf, unsigned int buf_len) { uint32_t write_len; uint32_t written_len; uint32_t remain_len; LOG_DUMP_HEADER log_header; DATA_BUFFER *data_buff; if(!log_dump_is_init) { return; } if(strstr((char*)buf,LOG_DUMP_PREFIX) != NULL) { return; } data_buff = &data_buffer_list[log_dump_w_seqnum]; remain_len = buf_len; written_len = 0; do{ if(data_buff->state == DATA_BUFFER_STATE_FREE || data_buff->state == DATA_BUFFER_STATE_WRITTEN) { // LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s:%d data_buff->state is free.w_seqnum = %d.", // __func__,__LINE__,log_dump_w_seqnum); data_buff->state = DATA_BUFFER_STATE_WRITTING; data_buff->offset = 0; memset(data_buff->buffer,0,LOG_DUMP_SECTOR_SIZE); } if(data_buff->offset == 0) { #ifndef DUMP_NO_ROLLBACK // LOG_DUMP_TRACE(4,LOG_DUMP_PREFIX"%s:%d offset = 0.w_seqnum = %d,dump_seqnum = %d.", // __func__,__LINE__,log_dump_w_seqnum,log_dump_cur_dump_seqnum); memset((uint8_t*)&log_header,0,sizeof(log_header)); log_header.magic = DUMP_LOG_MAGIC; log_header.version = DUMP_LOG_VERSION; log_header.seqnum = log_dump_cur_dump_seqnum; if(log_dump_is_bootup) { log_header.is_bootup = 1; log_dump_is_bootup = false; } else { log_header.is_bootup = 0; } log_header.reseved[0] = '\0'; log_header.reseved[1] = '\0'; log_header.reseved[2] = '\n'; memcpy(data_buff->buffer + data_buff->offset, (uint8_t*)&log_header, sizeof(log_header)); data_buff->offset += sizeof(log_header); #else if(log_dump_cur_dump_seqnum*LOG_DUMP_SECTOR_SIZE >= log_dump_flash_len) { log_header = log_header; log_dump_is_bootup = log_dump_is_bootup; break; } #endif log_dump_cur_dump_seqnum ++; } if(data_buff->offset + remain_len > LOG_DUMP_SECTOR_SIZE) { write_len = (LOG_DUMP_SECTOR_SIZE - data_buff->offset); } else { write_len = remain_len; } if(write_len > 0) { memcpy(data_buff->buffer + data_buff->offset, buf + written_len, write_len); data_buff->offset += write_len; written_len += write_len; } if(data_buff->offset == LOG_DUMP_SECTOR_SIZE) { data_buff->state = DATA_BUFFER_STATE_WRITTEN; } remain_len -= write_len; if(data_buff->offset == LOG_DUMP_SECTOR_SIZE) { log_dump_w_seqnum = log_dump_w_seqnum + 1 == LOG_DUMP_SECTOR_BUFFER_COUNT ? 0 : log_dump_w_seqnum + 1; if(log_dump_w_seqnum == log_dump_f_seqnum) { log_dump_f_seqnum = log_dump_f_seqnum + 1 == LOG_DUMP_SECTOR_BUFFER_COUNT ? 0 : log_dump_f_seqnum + 1; } // LOG_DUMP_TRACE(4,LOG_DUMP_PREFIX"%s:%d w_seqnum = %d,dump_seqnum = %d.", // __func__,__LINE__,log_dump_w_seqnum,log_dump_cur_dump_seqnum); data_buff = &data_buffer_list[log_dump_w_seqnum]; ASSERT(data_buff->state == DATA_BUFFER_STATE_FREE || data_buff->state == DATA_BUFFER_STATE_WRITTEN, "log_dump_output_handler: data_buff state error! state = %d.", data_buff->state); } }while(remain_len > 0); } void log_dump_callback(void* param) { NORFLASH_API_OPERA_RESULT *opera_result; opera_result = (NORFLASH_API_OPERA_RESULT*)param; LOG_DUMP_TRACE_FORCE(6,LOG_DUMP_PREFIX"%s:type = %d, addr = 0x%x,len = 0x%x,remain = %d,result = %d.", __func__, opera_result->type, opera_result->addr, opera_result->len, opera_result->remain_num, opera_result->result); { static uint32_t is_fst = 1; if(is_fst) { LOG_DUMP_TRACE(3,LOG_DUMP_PREFIX"%s:log_dump_start = 0x%x, log_dump_end = 0x%x.", __func__, log_dump_flash_start_addr, log_dump_flash_end_addr); is_fst = 0; } } } void log_dump_init(void) { uint32_t block_size = 0; uint32_t sector_size = 0; uint32_t page_size = 0; uint32_t buffer_len = 0; uint32_t dump_seqnum = 0; uint32_t sector_seqnum = 0; enum NORFLASH_API_RET_T result; uint32_t i; log_dump_flash_len = log_dump_flash_end_addr - log_dump_flash_start_addr; hal_norflash_get_size(HAL_NORFLASH_ID_0,NULL, &block_size, §or_size, &page_size); buffer_len = LOG_DUMP_NORFALSH_BUFFER_LEN; /* LOG_DUMP_TRACE(4,LOG_DUMP_PREFIX"%s: log_dump_start = 0x%x, log_dump_len = 0x%x, buff_len = 0x%x.", __func__, log_dump_flash_start_addr, log_dump_flash_len, buffer_len); */ result = norflash_api_register( NORFLASH_API_MODULE_ID_LOG_DUMP, HAL_NORFLASH_ID_0, log_dump_flash_start_addr, log_dump_flash_end_addr - log_dump_flash_start_addr, block_size, sector_size, page_size, buffer_len, log_dump_callback ); if(result == NORFLASH_API_OK) { /* LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"%s: norflash_api_register ok.", __func__); */ } else { /* LOG_DUMP_TRACE(2,LOG_DUMP_PREFIX"%s: norflash_api_register failed,result = %d.", __func__, result); */ return; } hal_trace_app_register(log_dump_notify_handler, log_dump_output_handler); hal_sleep_set_sleep_hook(HAL_SLEEP_HOOK_DUMP_LOG, log_dump_flush); _get_seqnum(&dump_seqnum,§or_seqnum); log_dump_cur_dump_seqnum = dump_seqnum; log_dump_flash_offs = sector_seqnum*LOG_DUMP_SECTOR_SIZE; memset((uint8_t*)&data_buffer_list,0,sizeof(data_buffer_list)); for(i = 0;i < LOG_DUMP_SECTOR_BUFFER_COUNT; i++) { data_buffer_list[i].state = DATA_BUFFER_STATE_FREE; data_buffer_list[i].offset = 0; } log_dump_w_seqnum = 0; log_dump_f_seqnum = 0; log_dump_flash_state = LOG_DUMP_FLASH_STATE_IDLE; log_dump_is_immediately = false; log_dump_is_init = true; } void log_dump_clear(void) { uint32_t i; uint32_t addr; uint32_t lock = int_lock_global(); for(i = 0; i < log_dump_flash_len/LOG_DUMP_SECTOR_SIZE; i++) { addr = log_dump_flash_start_addr + i*LOG_DUMP_SECTOR_SIZE; _flash_api_erase(addr,false); } int_unlock_global(lock); } #if 0 uint8_t test_buff_r[LOG_DUMP_SECTOR_SIZE]; uint32_t test_log_dump_from_flash(uint32_t addr,uint32_t size) { //uint32_t start_addr; uint32_t i; //uint8_t value = 0; int32_t ret = 0; enum NORFLASH_API_RET_T result = HAL_NORFLASH_OK; LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"%s enter!!!", __func__); for(i = 0; i < size/LOG_DUMP_SECTOR_SIZE; i++) { result = _flash_api_read((uint32_t)(addr)+ (i*LOG_DUMP_SECTOR_SIZE), test_buff_r,LOG_DUMP_SECTOR_SIZE); if(result != NORFLASH_API_OK) { ret = -1; //LOG_DUMP_TRACE(2,LOG_DUMP_PREFIX"%s ret=%d", __func__, ret); goto _func_end; } LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"%s", test_buff_r); } LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"%s end!!!", __func__); _func_end: LOG_DUMP_TRACE(1,LOG_DUMP_PREFIX"_debug: flash checking end. ret = %d.",ret); return ret; } #endif