| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/cdev.h> |
| #include <linux/semaphore.h> |
| #include <linux/timer.h> |
| #include <linux/ioport.h> |
| #include <linux/poll.h> |
| #include <linux/delay.h> |
| #include <linux/wait.h> |
| #include <linux/err.h> |
| #include <linux/interrupt.h> |
| #include <linux/sched.h> |
| #include <linux/types.h> |
| #include <linux/device.h> |
| #include <linux/miscdevice.h> |
| #include <linux/list.h> |
| #include <linux/slab.h> |
| #include <linux/sched.h> |
| #include <asm/io.h> |
| #include <linux/vmalloc.h> |
| #include <linux/soc/zte/rpmsg.h> |
| #include "pub_debug_info.h" |
| #include "ringbuf.h" |
| #include "ram_config.h" |
| #include "ZspTrace.h" |
| #include <linux/proc_fs.h> |
| #include <asm/barrier.h> |
| #include <asm/cache.h> |
| #include <asm/cacheflush.h> |
| #include <linux/seq_file.h> |
| #include <linux/ktime.h> |
| #include <linux/time.h> |
| |
| #if defined(_USE_ZXIC_DEBUG_INFO) |
| /******************************************************************************* |
| * 宏定义 * |
| *******************************************************************************/ |
| #define DEBUG_INFO_AP_MEM_LEN (0x2000) |
| #define DEBUG_INFO_READABLE_LEN (0x1400) |
| #define DEBUG_INFO_READ_TIME_MSECS (10000) |
| |
| #define DEBUG_INFO_OK (0) |
| #define DEBUG_INFO_ERROR (-1) |
| #define DEBUG_READ_NOBLOCK (11) |
| |
| #define DEBUG_INFO_MAX_ID_LEN (16+3) |
| #define DEBUG_INFO_MAX_DATA_LEN (128) |
| #define DEBUG_INFO_MAX_TIME_LEN (0)//(20+3) |
| #define DEBUG_INFO_MAX_DATE_LEN (19+3) |
| #define DEBUG_INFO_MAX_TOTAL_LEN (DEBUG_INFO_MAX_ID_LEN + DEBUG_INFO_MAX_DATA_LEN + DEBUG_INFO_MAX_TIME_LEN) |
| |
| #define DEBUG_INFO_CORE_NUM (2) |
| |
| /******************************************************************************* |
| * 结构体定义 * |
| *******************************************************************************/ |
| typedef unsigned int UINT32; |
| typedef unsigned short UINT16; |
| typedef unsigned char UINT8; |
| typedef unsigned long UINT64; |
| typedef struct{ |
| unsigned int head; /* 0x010a0a0a */ |
| unsigned int datalen[DEBUG_INFO_CORE_NUM]; /* 数据内容长度 */ |
| } T_SAVE_FILE_DATA; |
| |
| /******************************************************************************* |
| * 全局变量 * |
| *******************************************************************************/ |
| static struct semaphore debug_sem; |
| static int g_init_flag = 0; |
| UINT32 *g_ps_debug_write_cnt; |
| UINT32 *g_ps_debug_full_cnt; |
| UINT32 g_ps_debug_read_cnt; |
| UINT32 g_ps_debug_read_full_cnt; |
| unsigned long g_debug_read_cnt; |
| unsigned long g_ap_debug_write_cnt; |
| unsigned long g_ap_debug_read_cnt; |
| UINT8 *g_ap_debug_buffer_start; |
| UINT8 *g_ap_debug_buffer_read; |
| UINT8 *g_ap_debug_buffer_write; |
| UINT8 *g_ap_debug_buffer_end; |
| UINT8 *g_ps_debug_buffer_start; |
| UINT8 *g_ps_debug_buffer_end; |
| UINT8 *g_ps_debug_buffer_read; |
| static DEFINE_SPINLOCK(debug_info_lock); |
| static char g_ap_debug_buffer[DEBUG_INFO_AP_MEM_LEN]; |
| unsigned long g_debug_read_state = 0; |
| |
| /******************************************************************************* |
| * 内部函数定义 * |
| *******************************************************************************/ |
| static int sc_debug_info_read_to_user(char *buf, size_t count); |
| static int sc_debug_info_record_from_user(const char *info, size_t count); |
| static void sc_debug_info_from_ps(void *buf, unsigned int len); |
| static void kernel_timer_timeout(struct timer_list *t); |
| static ssize_t debug_info_read(struct file *fp, char __user *buf, size_t count, loff_t *pos); |
| static ssize_t debug_info_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos); |
| static int debug_info_open(struct inode *ip, struct file *fp); |
| static long debug_info_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); |
| static int debug_info_release(struct inode *ip, struct file *fp); |
| extern u64 cop_time_get_value64(void); |
| extern int seq_write(struct seq_file *seq, const void *data, size_t len); |
| static DEFINE_TIMER(timer, kernel_timer_timeout); |
| static const struct file_operations debug_info_fops = { |
| .owner = THIS_MODULE, |
| .read = debug_info_read, |
| .write = debug_info_write, |
| .open = debug_info_open, |
| .unlocked_ioctl = debug_info_ioctl, |
| .release = debug_info_release, |
| }; |
| |
| static struct miscdevice debug_info_device = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = "debug_info", |
| .fops = &debug_info_fops, |
| }; |
| static void kernel_timer_timeout(struct timer_list *t) |
| { |
| if (debug_sem.count == 0) |
| { |
| up(&debug_sem); |
| } |
| /* Kernel Timer restart */ |
| mod_timer(&timer, jiffies + msecs_to_jiffies(DEBUG_INFO_READ_TIME_MSECS)); |
| } |
| |
| /* Started by AICoder, pid:y6ffbg9a06ieda114a190b4cb0ff4417bd78c0f4 */ |
| static ssize_t debug_info_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) |
| { |
| int ret; |
| int rd_len; |
| |
| if (g_debug_read_state != DEBUG_READ_NOBLOCK) |
| { |
| ret = down_interruptible(&debug_sem); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| } |
| |
| rd_len = sc_debug_info_read_to_user(buf, count); |
| |
| return rd_len; |
| } |
| /* Ended by AICoder, pid:y6ffbg9a06ieda114a190b4cb0ff4417bd78c0f4 */ |
| |
| static ssize_t debug_info_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos) |
| { |
| int wr_len = sc_debug_info_record_from_user(buf, count); |
| |
| return wr_len; |
| } |
| |
| static int debug_info_open(struct inode *ip, struct file *fp) |
| { |
| return 0; |
| } |
| |
| /* Started by AICoder, pid:u635ed463ce5ab2145a109c3c0f54d149e6386b3 */ |
| static long debug_info_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { |
| if (cmd == DEBUG_READ_NOBLOCK) { |
| g_debug_read_state = DEBUG_READ_NOBLOCK; |
| } else { |
| // 如果命令不匹配,返回错误码 |
| return -EINVAL; |
| } |
| |
| // 记录调试信息 |
| sc_debug_info_record("debug_info", "read noblock!\n"); |
| |
| return 0; |
| } |
| /* Ended by AICoder, pid:u635ed463ce5ab2145a109c3c0f54d149e6386b3 */ |
| |
| static int debug_info_release(struct inode *ip, struct file *fp) |
| { |
| return 0; |
| } |
| |
| static int ap_log_proc_show(struct seq_file *m, void *v) |
| { |
| unsigned long writecnt = g_ap_debug_write_cnt; |
| |
| if (writecnt > DEBUG_INFO_AP_MEM_LEN) |
| { |
| writecnt = DEBUG_INFO_AP_MEM_LEN; |
| } |
| |
| seq_write(m, g_ap_debug_buffer_start, writecnt); |
| |
| return 0; |
| } |
| |
| static int ps_log_proc_show(struct seq_file *m, void *v) |
| { |
| unsigned long writecnt = 0; |
| unsigned long fulllcnt = 0; |
| |
| writecnt = *(UINT32 *)g_ps_debug_write_cnt; |
| fulllcnt = *(UINT32 *)g_ps_debug_full_cnt; |
| |
| if (fulllcnt > 0) |
| { |
| writecnt = PS_DEBUG_INFO_SIZE; |
| } |
| |
| //__inval_dcache_area((void *)g_ps_debug_buffer_start, writecnt); |
| seq_write(m, g_ps_debug_buffer_start, writecnt); |
| |
| return 0; |
| } |
| |
| UINT32 skip_end_null(char *buf, size_t count) |
| { |
| UINT32 len = 0; |
| char *tmpbuf = buf; |
| |
| while (count > 0) |
| { |
| if (*tmpbuf == '\0') |
| { |
| ++len; |
| tmpbuf -= 1; |
| count -= 1; |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| return len; |
| } |
| |
| int sc_debug_info_read_ap(char *buf, size_t count) |
| { |
| UINT32 readLen = 0; |
| UINT32 updatelen = 0; |
| UINT32 readremainLen = 0; |
| UINT8 *readbuffaddr = 0; |
| UINT32 nullLen = 0; |
| char *tmpbuf = buf; |
| |
| updatelen = g_ap_debug_write_cnt - g_ap_debug_read_cnt; |
| if (updatelen > DEBUG_INFO_AP_MEM_LEN) |
| updatelen = DEBUG_INFO_AP_MEM_LEN; |
| |
| if(g_ap_debug_buffer_end - g_ap_debug_buffer_read >= updatelen) |
| { |
| readbuffaddr = g_ap_debug_buffer_read; |
| readLen = updatelen; |
| if (copy_to_user(tmpbuf , readbuffaddr, readLen)) |
| { |
| return -EFAULT; |
| } |
| g_ap_debug_buffer_read += readLen; |
| } |
| else if(g_ap_debug_buffer_end - g_ap_debug_buffer_read < updatelen) |
| { |
| readbuffaddr = g_ap_debug_buffer_read; |
| readLen = g_ap_debug_buffer_end - readbuffaddr; |
| readremainLen = updatelen - readLen; |
| nullLen = skip_end_null(g_ap_debug_buffer_end - 1, readLen); |
| readLen = readLen - nullLen; |
| if (copy_to_user(tmpbuf, readbuffaddr, readLen)) |
| { |
| return -EFAULT; |
| } |
| if (copy_to_user(tmpbuf + readLen, g_ap_debug_buffer_start, readremainLen)) |
| { |
| return -EFAULT; |
| } |
| g_ap_debug_buffer_read = g_ap_debug_buffer_start + readremainLen; |
| } |
| else |
| { |
| panic("gaohf sc_debug_info_read_to_user error"); |
| } |
| g_ap_debug_read_cnt = g_ap_debug_write_cnt; |
| |
| return (readLen + readremainLen); |
| } |
| |
| int sc_debug_info_read_ps(char *buf, size_t count) |
| { |
| UINT32 readLen = 0; |
| UINT32 readremainLen = 0; |
| UINT8 *readbuffaddr = 0; |
| UINT32 fulllcnt = 0; |
| UINT32 fulllcntdiff = 0; |
| unsigned long writecnt = 0; |
| UINT32 nullLen = 0; |
| char *tmpbuf = buf; |
| |
| writecnt = *(volatile UINT32 *)g_ps_debug_write_cnt; |
| fulllcnt = *(volatile UINT32 *)g_ps_debug_full_cnt; |
| |
| if (fulllcnt >= g_ps_debug_read_full_cnt) |
| { |
| fulllcntdiff = fulllcnt - g_ps_debug_read_full_cnt; |
| } |
| else |
| { |
| fulllcntdiff = 0xFFFFFFFF - g_ps_debug_read_full_cnt + fulllcnt; |
| } |
| if(fulllcntdiff >= 2) |
| { |
| readbuffaddr = g_ps_debug_buffer_start + writecnt; |
| readLen = g_ps_debug_buffer_end - readbuffaddr; |
| readremainLen = writecnt; |
| nullLen = skip_end_null(g_ps_debug_buffer_end - 1, readLen); |
| readLen = readLen - nullLen; |
| if (copy_to_user(tmpbuf, readbuffaddr, readLen)) |
| { |
| return -EFAULT; |
| } |
| if (copy_to_user(tmpbuf + readLen, g_ps_debug_buffer_start, readremainLen)) |
| { |
| return -EFAULT; |
| } |
| g_ps_debug_buffer_read = g_ps_debug_buffer_start + readremainLen; |
| } |
| else if(fulllcntdiff == 0) |
| { |
| readbuffaddr = g_ps_debug_buffer_read; |
| readLen = writecnt - g_ps_debug_read_cnt;; |
| if (copy_to_user(tmpbuf , readbuffaddr, readLen)) |
| { |
| return -EFAULT; |
| } |
| g_ps_debug_buffer_read += readLen; |
| } |
| else if(fulllcntdiff == 1) |
| { |
| readLen = writecnt >= g_ps_debug_read_cnt ? writecnt : g_ps_debug_read_cnt; |
| readLen = PS_DEBUG_INFO_SIZE - readLen; |
| readbuffaddr = g_ps_debug_buffer_read; |
| nullLen = skip_end_null(g_ps_debug_buffer_end - 1, readLen); |
| readLen = readLen - nullLen; |
| if (copy_to_user(tmpbuf, readbuffaddr, readLen)) |
| { |
| return -EFAULT; |
| } |
| readremainLen = writecnt; |
| if (copy_to_user(tmpbuf + readLen , g_ps_debug_buffer_start, readremainLen)) |
| { |
| return -EFAULT; |
| } |
| g_ps_debug_buffer_read = g_ps_debug_buffer_start + readremainLen; |
| } |
| else |
| { |
| panic("gaohf sc_debug_info_read_to_user error"); |
| } |
| g_ps_debug_read_cnt = writecnt; |
| g_ps_debug_read_full_cnt = fulllcnt; |
| |
| return (readLen + readremainLen); |
| } |
| |
| int sc_debug_info_read_to_user(char *buf, size_t count) |
| { |
| char *tmpbuf = 0; |
| int ret = 0; |
| int ap_len = 0; |
| int ps_len = 0; |
| T_SAVE_FILE_DATA fileDataHead; |
| |
| if (g_init_flag == 0) |
| { |
| printk("debug_info not init.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| if (count == 0 || buf == NULL ) |
| { |
| printk("sc_debug_info_read_to_user count == 0 || buf == NULL \n"); |
| return DEBUG_INFO_ERROR; |
| } |
| tmpbuf = buf + sizeof(T_SAVE_FILE_DATA); |
| ap_len = sc_debug_info_read_ap(tmpbuf, DEBUG_INFO_AP_MEM_LEN); |
| tmpbuf += ap_len; |
| ps_len = sc_debug_info_read_ps(tmpbuf, PS_DEBUG_INFO_SIZE); |
| |
| fileDataHead.head = 0x010a0a0a; |
| fileDataHead.datalen[0] = ap_len; |
| fileDataHead.datalen[1] = ps_len; |
| |
| if (copy_to_user(buf, &fileDataHead, sizeof(T_SAVE_FILE_DATA))) |
| { |
| printk("sc_debug_info_read_to_user copy_to_user error \n"); |
| return -EFAULT; |
| } |
| return sizeof(T_SAVE_FILE_DATA) + ap_len + ps_len; |
| } |
| |
| static int sc_debug_info_record_from_user(const char *info, size_t count) |
| { |
| UINT32 cnt = 0; |
| int spacelen; |
| int msg_len = 0; |
| struct tm tm; |
| time64_t time; |
| unsigned long used_space; |
| char buffer[DEBUG_INFO_MAX_TOTAL_LEN]; |
| unsigned long flags; |
| UINT8 *tmp_write_addr = buffer; |
| |
| if (g_init_flag == 0 || info == NULL) |
| { |
| printk("debug_info not init or sc_debug_info_record_from_user info is NULL\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| if(count > DEBUG_INFO_MAX_DATA_LEN + DEBUG_INFO_MAX_ID_LEN) |
| { |
| printk("debug_info data too long\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| time = ktime_get_real_seconds(); |
| time64_to_tm(time, 0, &tm); |
| cnt = snprintf((char *)tmp_write_addr, DEBUG_INFO_MAX_DATE_LEN, "[%ld-%02d-%02d %d:%02d:%02d]", \ |
| tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); |
| tmp_write_addr += cnt; |
| msg_len = cnt; |
| |
| spin_lock_irqsave(&debug_info_lock, flags); |
| spacelen = g_ap_debug_buffer_end - g_ap_debug_buffer_write; |
| if (spacelen < msg_len + count) |
| { |
| g_ap_debug_write_cnt += spacelen; |
| memset(g_ap_debug_buffer_write, '\0', spacelen); |
| g_ap_debug_buffer_write = g_ap_debug_buffer_start; |
| } |
| memcpy(g_ap_debug_buffer_write, buffer, msg_len); |
| if (copy_from_user(g_ap_debug_buffer_write + msg_len, info, count)) |
| return -EFAULT; |
| msg_len += count; |
| g_ap_debug_buffer_write += msg_len; |
| g_ap_debug_write_cnt += msg_len; |
| used_space = g_ap_debug_write_cnt - g_ap_debug_read_cnt; |
| spin_unlock_irqrestore(&debug_info_lock, flags); |
| if (used_space > DEBUG_INFO_READABLE_LEN) |
| { |
| if (debug_sem.count == 0) |
| { |
| up(&debug_sem); |
| } |
| } |
| return msg_len; |
| } |
| |
| int sc_debug_info_vrecord(char *id, const char *format, va_list args) |
| { |
| int spacelen; |
| int msg_len = 0; |
| int cnt = 0; |
| unsigned long used_space; |
| struct tm tm; |
| time64_t time; |
| char buffer[DEBUG_INFO_MAX_TOTAL_LEN]; |
| unsigned long flags; |
| UINT8 *tmp_write_addr = buffer; |
| |
| time = ktime_get_real_seconds(); |
| time64_to_tm(time, 0, &tm); |
| cnt = snprintf((char *)tmp_write_addr, DEBUG_INFO_MAX_DATE_LEN, "[%ld-%02d-%02d %d:%02d:%02d]", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); |
| tmp_write_addr += cnt; |
| msg_len = cnt; |
| |
| cnt = snprintf((char *)tmp_write_addr, DEBUG_INFO_MAX_ID_LEN, "[%s]",id); |
| tmp_write_addr += cnt; |
| msg_len += cnt; |
| msg_len += vsnprintf((char *)tmp_write_addr, DEBUG_INFO_MAX_DATA_LEN, format, args); |
| |
| spin_lock_irqsave(&debug_info_lock, flags); |
| spacelen = g_ap_debug_buffer_end - g_ap_debug_buffer_write; |
| if (spacelen < DEBUG_INFO_MAX_TOTAL_LEN) |
| { |
| g_ap_debug_write_cnt += spacelen; |
| memset(g_ap_debug_buffer_write, '\0', spacelen); |
| g_ap_debug_buffer_write = g_ap_debug_buffer_start; |
| } |
| |
| memcpy(g_ap_debug_buffer_write, buffer, msg_len); |
| g_ap_debug_buffer_write += msg_len; |
| g_ap_debug_write_cnt += msg_len; |
| used_space = g_ap_debug_write_cnt - g_ap_debug_read_cnt; |
| spin_unlock_irqrestore(&debug_info_lock, flags); |
| if (used_space > DEBUG_INFO_READABLE_LEN) |
| { |
| if (debug_sem.count == 0) |
| { |
| up(&debug_sem); |
| } |
| } |
| return msg_len; |
| } |
| EXPORT_SYMBOL(sc_debug_info_vrecord); |
| |
| int sc_debug_info_record(char *id, const char *format, ...) |
| { |
| va_list args; |
| int r; |
| |
| if (g_init_flag == 0) |
| { |
| printk("debug_info not init.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| va_start(args, format); |
| r = sc_debug_info_vrecord(id, format, args); |
| va_end(args); |
| |
| return r; |
| } |
| EXPORT_SYMBOL(sc_debug_info_record); |
| |
| void early_debug_info_init(void) |
| { |
| g_ap_debug_buffer_start = g_ap_debug_buffer; |
| g_ap_debug_buffer_write = g_ap_debug_buffer_start; |
| g_ap_debug_buffer_read = g_ap_debug_buffer_start; |
| g_ap_debug_buffer_end = g_ap_debug_buffer + DEBUG_INFO_AP_MEM_LEN; |
| g_ap_debug_read_cnt = 0; |
| g_ap_debug_write_cnt = 0; |
| |
| g_init_flag = 1; |
| printk("cap early_debug_info_init success \n"); |
| } |
| |
| static int __init debug_info_init(void) |
| { |
| int ret; |
| |
| ret = misc_register(&debug_info_device); |
| if (ret) |
| { |
| printk("debug_info_init init.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| g_ps_debug_write_cnt = (UINT32 *)ioremap(PS_DEBUG_INFO_LEN_ADDR_PA, PS_DEBUG_INFO_LEN_SIZE); |
| // printf("PS_DEBUG_INFO_LEN_ADDR_PA \n",) |
| g_ps_debug_full_cnt = (UINT32 *)((char *)g_ps_debug_write_cnt + 4); |
| g_ps_debug_buffer_start = (unsigned long)ioremap(PS_DEBUG_INFO_ADDR_PA, PS_DEBUG_INFO_SIZE); |
| g_ps_debug_buffer_read = g_ps_debug_buffer_start; |
| g_ps_debug_buffer_end = g_ps_debug_buffer_start + PS_DEBUG_INFO_SIZE; |
| g_ps_debug_read_cnt = 0; |
| g_ps_debug_read_full_cnt = 0; |
| sema_init(&debug_sem, 0); |
| proc_create_single("debug_info_cap", 0, NULL, ap_log_proc_show); |
| proc_create_single("debug_info_ap", 0, NULL, ps_log_proc_show); |
| mod_timer(&timer, jiffies + 1); |
| printk("cap debug_info_init success \n"); |
| // g_init_flag = 1; |
| return 0; |
| } |
| |
| static void __exit debug_info_exit(void) |
| { |
| misc_deregister(&debug_info_device); |
| del_timer(&timer); |
| } |
| |
| module_init(debug_info_init); |
| module_exit(debug_info_exit); |
| MODULE_DESCRIPTION("debug_info driver"); |
| MODULE_LICENSE("GPL"); |
| |
| #else |
| int sc_debug_info_record(char *id, const char *format, ...) |
| { |
| return 0; |
| } |
| #endif /* _USE_ZXIC_DEBUG_INFO */ |
| |
| |