bcm_elog.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright (c) 2018 - 2020, Broadcom
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. #include <stdarg.h>
  7. #include <stdint.h>
  8. #include <string.h>
  9. #include <arch_helpers.h>
  10. #include <common/debug.h>
  11. #include <plat/common/platform.h>
  12. #include <bcm_elog.h>
  13. /* error logging signature */
  14. #define BCM_ELOG_SIG_OFFSET 0x0000
  15. #define BCM_ELOG_SIG_VAL 0x75767971
  16. /* current logging offset that points to where new logs should be added */
  17. #define BCM_ELOG_OFF_OFFSET 0x0004
  18. /* current logging length (excluding header) */
  19. #define BCM_ELOG_LEN_OFFSET 0x0008
  20. #define BCM_ELOG_HEADER_LEN 12
  21. /*
  22. * @base: base address of memory where log is saved
  23. * @max_size: max size of memory reserved for logging
  24. * @is_active: indicates logging is currently active
  25. * @level: current logging level
  26. */
  27. struct bcm_elog {
  28. uintptr_t base;
  29. uint32_t max_size;
  30. unsigned int is_active;
  31. unsigned int level;
  32. };
  33. static struct bcm_elog global_elog;
  34. extern void memcpy16(void *dst, const void *src, unsigned int len);
  35. /*
  36. * Log one character
  37. */
  38. static void elog_putchar(struct bcm_elog *elog, unsigned char c)
  39. {
  40. uint32_t offset, len;
  41. offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
  42. len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
  43. mmio_write_8(elog->base + offset, c);
  44. offset++;
  45. /* log buffer is now full and need to wrap around */
  46. if (offset >= elog->max_size)
  47. offset = BCM_ELOG_HEADER_LEN;
  48. /* only increment length when log buffer is not full */
  49. if (len < elog->max_size - BCM_ELOG_HEADER_LEN)
  50. len++;
  51. mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
  52. mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
  53. }
  54. static void elog_unsigned_num(struct bcm_elog *elog, unsigned long unum,
  55. unsigned int radix)
  56. {
  57. /* Just need enough space to store 64 bit decimal integer */
  58. unsigned char num_buf[20];
  59. int i = 0, rem;
  60. do {
  61. rem = unum % radix;
  62. if (rem < 0xa)
  63. num_buf[i++] = '0' + rem;
  64. else
  65. num_buf[i++] = 'a' + (rem - 0xa);
  66. } while (unum /= radix);
  67. while (--i >= 0)
  68. elog_putchar(elog, num_buf[i]);
  69. }
  70. static void elog_string(struct bcm_elog *elog, const char *str)
  71. {
  72. while (*str)
  73. elog_putchar(elog, *str++);
  74. }
  75. /*
  76. * Routine to initialize error logging
  77. */
  78. int bcm_elog_init(void *base, uint32_t size, unsigned int level)
  79. {
  80. struct bcm_elog *elog = &global_elog;
  81. uint32_t val;
  82. elog->base = (uintptr_t)base;
  83. elog->max_size = size;
  84. elog->is_active = 1;
  85. elog->level = level / 10;
  86. /*
  87. * If a valid signature can be found, it means logs have been copied
  88. * into designated memory by another software. In this case, we should
  89. * not re-initialize the entry header in the designated memory
  90. */
  91. val = mmio_read_32(elog->base + BCM_ELOG_SIG_OFFSET);
  92. if (val != BCM_ELOG_SIG_VAL) {
  93. mmio_write_32(elog->base + BCM_ELOG_SIG_OFFSET,
  94. BCM_ELOG_SIG_VAL);
  95. mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET,
  96. BCM_ELOG_HEADER_LEN);
  97. mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, 0);
  98. }
  99. return 0;
  100. }
  101. /*
  102. * Routine to disable error logging
  103. */
  104. void bcm_elog_exit(void)
  105. {
  106. struct bcm_elog *elog = &global_elog;
  107. if (!elog->is_active)
  108. return;
  109. elog->is_active = 0;
  110. flush_dcache_range(elog->base, elog->max_size);
  111. }
  112. /*
  113. * Routine to copy error logs from current memory to 'dst' memory and continue
  114. * logging from the new 'dst' memory.
  115. * dst and base addresses must be 16-bytes aligned.
  116. */
  117. int bcm_elog_copy_log(void *dst, uint32_t max_size)
  118. {
  119. struct bcm_elog *elog = &global_elog;
  120. uint32_t offset, len;
  121. if (!elog->is_active || ((uintptr_t)dst == elog->base))
  122. return -1;
  123. /* flush cache before copying logs */
  124. flush_dcache_range(elog->base, max_size);
  125. /*
  126. * If current offset exceeds the new max size, then that is considered
  127. * as a buffer overflow situation. In this case, we reset the offset
  128. * back to the beginning
  129. */
  130. offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
  131. if (offset >= max_size) {
  132. offset = BCM_ELOG_HEADER_LEN;
  133. mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
  134. }
  135. /* note payload length does not include header */
  136. len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
  137. if (len > max_size - BCM_ELOG_HEADER_LEN) {
  138. len = max_size - BCM_ELOG_HEADER_LEN;
  139. mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
  140. }
  141. /* Need to copy everything including the header. */
  142. memcpy16(dst, (const void *)elog->base, len + BCM_ELOG_HEADER_LEN);
  143. elog->base = (uintptr_t)dst;
  144. elog->max_size = max_size;
  145. return 0;
  146. }
  147. /*
  148. * Main routine to save logs into memory
  149. */
  150. void bcm_elog(const char *fmt, ...)
  151. {
  152. va_list args;
  153. const char *prefix_str;
  154. int bit64;
  155. int64_t num;
  156. uint64_t unum;
  157. char *str;
  158. struct bcm_elog *elog = &global_elog;
  159. /* We expect the LOG_MARKER_* macro as the first character */
  160. unsigned int level = fmt[0];
  161. if (!elog->is_active || level > elog->level)
  162. return;
  163. prefix_str = plat_log_get_prefix(level);
  164. while (*prefix_str != '\0') {
  165. elog_putchar(elog, *prefix_str);
  166. prefix_str++;
  167. }
  168. va_start(args, fmt);
  169. fmt++;
  170. while (*fmt) {
  171. bit64 = 0;
  172. if (*fmt == '%') {
  173. fmt++;
  174. /* Check the format specifier */
  175. loop:
  176. switch (*fmt) {
  177. case 'i': /* Fall through to next one */
  178. case 'd':
  179. if (bit64)
  180. num = va_arg(args, int64_t);
  181. else
  182. num = va_arg(args, int32_t);
  183. if (num < 0) {
  184. elog_putchar(elog, '-');
  185. unum = (unsigned long)-num;
  186. } else
  187. unum = (unsigned long)num;
  188. elog_unsigned_num(elog, unum, 10);
  189. break;
  190. case 's':
  191. str = va_arg(args, char *);
  192. elog_string(elog, str);
  193. break;
  194. case 'x':
  195. if (bit64)
  196. unum = va_arg(args, uint64_t);
  197. else
  198. unum = va_arg(args, uint32_t);
  199. elog_unsigned_num(elog, unum, 16);
  200. break;
  201. case 'l':
  202. bit64 = 1;
  203. fmt++;
  204. goto loop;
  205. case 'u':
  206. if (bit64)
  207. unum = va_arg(args, uint64_t);
  208. else
  209. unum = va_arg(args, uint32_t);
  210. elog_unsigned_num(elog, unum, 10);
  211. break;
  212. default:
  213. /* Exit on any other format specifier */
  214. goto exit;
  215. }
  216. fmt++;
  217. continue;
  218. }
  219. elog_putchar(elog, *fmt++);
  220. }
  221. exit:
  222. va_end(args);
  223. }