// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #define LOGBUF_RDFLG 0x80000000 #define RTOSINFO_PHY_ADDR (0x80000000 + 0x1000) struct xrtosinfo_t { u32 version; u32 status; u32 cpumask; u32 flags; u32 logbuf_len; u32 logbuf_phy; u32 rtos_run_flag; u32 android_status; u32 reserved0; u32 reserved1; }; struct logbuf_t { uint32_t write; uint32_t read; char buf[0]; }; static struct xrtosinfo_t *rtosinfo; static struct logbuf_t *logbuf; static int detect_quit_cmd(void) { int quit = 0; int key = 0; if (tstc()) { /* we got a key press */ key = getc(); /* consume input */ switch (key) { case 'q': case 'Q': quit = 1; break; } } return quit; } static inline void logbuf_begin_get_log(void) { u32 rd1, rd2; rd1 = READ_ONCE(logbuf->read); rd2 = (rd1 | LOGBUF_RDFLG); WRITE_ONCE(logbuf->read, rd2); } static inline int logbuf_get_readptr(const char **pbuf) { const u32 logbuf_len = rtosinfo->logbuf_len - sizeof(struct logbuf_t); u32 rd1, rd2, wt; int len; rd1 = READ_ONCE(logbuf->read); wt = READ_ONCE(logbuf->write); rd2 = (rd1 & ~LOGBUF_RDFLG); if (rd2 <= wt) len = wt - rd2; else len = logbuf_len - rd2; if (pbuf) *pbuf = (const char *)logbuf->buf + rd2; return len; } static inline void logbuf_post_get_log(int len, int last) { const u32 logbuf_len = rtosinfo->logbuf_len - sizeof(struct logbuf_t); u32 rd1, rd2; rd1 = READ_ONCE(logbuf->read); rd2 = (rd1 & ~LOGBUF_RDFLG); rd2 += len; if (rd2 >= logbuf_len) rd2 -= logbuf_len; if (last == 0) rd2 |= (rd1 & LOGBUF_RDFLG); WRITE_ONCE(logbuf->read, rd2); } static bool map_freertos_logbuf(void) { void *buf; buf = map_physmem(RTOSINFO_PHY_ADDR, sizeof(struct xrtosinfo_t), MAP_WRBACK); if (!buf) { puts("Failed to map RTOSINFO memory\n"); return false; } rtosinfo = (struct xrtosinfo_t *)buf; logbuf = (struct logbuf_t *)map_physmem(rtosinfo->logbuf_phy, rtosinfo->logbuf_len, MAP_WRBACK); if (!logbuf) { puts("Failed to map log buf\n"); unmap_physmem(buf, sizeof(struct xrtosinfo_t)); return false; } return true; } static void unmap_freertos_logbuf(void) { unmap_physmem(logbuf, rtosinfo->logbuf_len); unmap_physmem(rtosinfo, sizeof(struct xrtosinfo_t)); logbuf = NULL; rtosinfo = NULL; } static void output_freertos_logbuf(const char *p, int len) { #define TMP_BUF_LEN 128 static char tmpbuf[TMP_BUF_LEN + 1]; if (len > 0) { int remain = len; do { int copylen = remain; if (copylen > TMP_BUF_LEN) copylen = TMP_BUF_LEN; memcpy(tmpbuf, p, copylen); tmpbuf[copylen] = 0; puts(tmpbuf); p += copylen; remain -= copylen; } while (remain > 0); } } int do_freertos_log(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int quit = 0; const char *p; int cnt = 0; if (argc < 2) return CMD_RET_USAGE; if (strncmp(argv[1], "read", 4) == 0) { if (!map_freertos_logbuf()) return CMD_RET_FAILURE; logbuf_begin_get_log(); cnt = logbuf_get_readptr(&p); if (cnt > 0) output_freertos_logbuf(p, cnt); logbuf_post_get_log(cnt, true); unmap_freertos_logbuf(); } else if (strncmp(argv[1], "autoread", 8) == 0) { if (!map_freertos_logbuf()) return CMD_RET_FAILURE; do { logbuf_begin_get_log(); cnt = logbuf_get_readptr(&p); if (cnt > 0) output_freertos_logbuf(p, cnt); logbuf_post_get_log(cnt, true); quit = detect_quit_cmd(); } while (!quit); unmap_freertos_logbuf(); } else { printf("arg error\n"); return CMD_RET_USAGE; } return 0; } static char freertos_log_help_text[] = "read freertos log\n" "[read log once ]\n" "[autoread auto read, 'q' exit]\n"; U_BOOT_CMD(freertos_log, 2, 0, do_freertos_log, "freerots_log commands", freertos_log_help_text );