/* * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Exynos-SnapShot debugging framework for Exynos SoC * * Author: Hosung Kim * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include "debug-snapshot-local.h" #include #include #include #include #include #include #include #include #include /* This defines are for PSTORE */ #define DSS_LOGGER_LEVEL_HEADER (1) #define DSS_LOGGER_LEVEL_PREFIX (2) #define DSS_LOGGER_LEVEL_TEXT (3) #define DSS_LOGGER_LEVEL_MAX (4) #define DSS_LOGGER_SKIP_COUNT (4) #define DSS_LOGGER_STRING_PAD (1) #define DSS_LOGGER_HEADER_SIZE (68) #define DSS_LOG_ID_MAIN (0) #define DSS_LOG_ID_RADIO (1) #define DSS_LOG_ID_EVENTS (2) #define DSS_LOG_ID_SYSTEM (3) #define DSS_LOG_ID_CRASH (4) #define DSS_LOG_ID_KERNEL (5) typedef struct __attribute__((__packed__)) { uint8_t magic; uint16_t len; uint16_t uid; uint16_t pid; } dss_pmsg_log_header_t; typedef struct __attribute__((__packed__)) { unsigned char id; uint16_t tid; int32_t tv_sec; int32_t tv_nsec; } dss_android_log_header_t; typedef struct dss_logger { uint16_t len; uint16_t id; uint16_t pid; uint16_t tid; uint16_t uid; uint16_t level; int32_t tv_sec; int32_t tv_nsec; char msg; char *buffer; void (*func_hook_logger)(const char*, const char*, size_t); } __attribute__((__packed__)) dss_logger; static dss_logger logger; void register_hook_logger(void (*func)(const char *name, const char *buf, size_t size)) { logger.func_hook_logger = func; logger.buffer = vmalloc(PAGE_SIZE * 3); if (logger.buffer) pr_info("debug-snapshot: logger buffer alloc address: 0x%p\n", logger.buffer); } EXPORT_SYMBOL(register_hook_logger); static int dbg_snapshot_combine_pmsg(char *buffer, size_t count, unsigned int level) { char *logbuf = logger.buffer; if (!logbuf) return -ENOMEM; switch (level) { case DSS_LOGGER_LEVEL_HEADER: { struct tm tmBuf; u64 tv_kernel; unsigned int logbuf_len; unsigned long rem_nsec; if (logger.id == DSS_LOG_ID_EVENTS) break; tv_kernel = local_clock(); rem_nsec = do_div(tv_kernel, 1000000000); time_to_tm(logger.tv_sec, 0, &tmBuf); logbuf_len = snprintf(logbuf, DSS_LOGGER_HEADER_SIZE, "\n[%5lu.%06lu][%d:%16s] %02d-%02d %02d:%02d:%02d.%03d %5d %5d ", (unsigned long)tv_kernel, rem_nsec / 1000, raw_smp_processor_id(), current->comm, tmBuf.tm_mon + 1, tmBuf.tm_mday, tmBuf.tm_hour, tmBuf.tm_min, tmBuf.tm_sec, logger.tv_nsec / 1000000, logger.pid, logger.tid); logger.func_hook_logger("log_platform", logbuf, logbuf_len - 1); } break; case DSS_LOGGER_LEVEL_PREFIX: { static const char *kPrioChars = "!.VDIWEFS"; unsigned char prio = logger.msg; if (logger.id == DSS_LOG_ID_EVENTS) break; logbuf[0] = prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'; logbuf[1] = ' '; logger.func_hook_logger("log_platform", logbuf, DSS_LOGGER_LEVEL_PREFIX); } break; case DSS_LOGGER_LEVEL_TEXT: { char *eatnl = buffer + count - DSS_LOGGER_STRING_PAD; if (logger.id == DSS_LOG_ID_EVENTS) break; if (count == DSS_LOGGER_SKIP_COUNT && *eatnl != '\0') break; logger.func_hook_logger("log_platform", buffer, count - 1); } break; default: break; } return 0; } int dbg_snapshot_hook_pmsg(char *buffer, size_t count) { dss_android_log_header_t header; dss_pmsg_log_header_t pmsg_header; if (!logger.buffer) return -ENOMEM; switch (count) { case sizeof(pmsg_header): memcpy((void *)&pmsg_header, buffer, count); if (pmsg_header.magic != 'l') { dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_TEXT); } else { /* save logger data */ logger.pid = pmsg_header.pid; logger.uid = pmsg_header.uid; logger.len = pmsg_header.len; } break; case sizeof(header): /* save logger data */ memcpy((void *)&header, buffer, count); logger.id = header.id; logger.tid = header.tid; logger.tv_sec = header.tv_sec; logger.tv_nsec = header.tv_nsec; if (logger.id > 7) { /* write string */ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_TEXT); } else { /* write header */ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_HEADER); } break; case sizeof(unsigned char): logger.msg = buffer[0]; /* write char for prefix */ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_PREFIX); break; default: /* write string */ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_TEXT); break; } return 0; } EXPORT_SYMBOL(dbg_snapshot_hook_pmsg); /* * To support pstore/pmsg/pstore_ram, following is implementation for debug-snapshot * dss_ramoops platform_device is used by pstore fs. */ static struct ramoops_platform_data dss_ramoops_data = { .record_size = SZ_512K, .console_size = SZ_512K, .ftrace_size = SZ_512K, .pmsg_size = SZ_512K, .dump_oops = 1, }; static struct platform_device dss_ramoops = { .name = "ramoops", .dev = { .platform_data = &dss_ramoops_data, }, }; static int __init dss_pstore_init(void) { if (dbg_snapshot_get_enable("log_pstore")) { dss_ramoops_data.mem_size = dbg_snapshot_get_item_size("log_pstore"); dss_ramoops_data.mem_address = dbg_snapshot_get_item_paddr("log_pstore"); } return platform_device_register(&dss_ramoops); } static void __exit dss_pstore_exit(void) { platform_device_unregister(&dss_ramoops); } module_init(dss_pstore_init); module_exit(dss_pstore_exit); MODULE_DESCRIPTION("Exynos Snapshot pstore module"); MODULE_LICENSE("GPL");