123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- /*
- * Copyright (c) 2018 - 2020, Broadcom
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <stdarg.h>
- #include <stdint.h>
- #include <string.h>
- #include <arch_helpers.h>
- #include <common/debug.h>
- #include <plat/common/platform.h>
- #include <bcm_elog.h>
- /* error logging signature */
- #define BCM_ELOG_SIG_OFFSET 0x0000
- #define BCM_ELOG_SIG_VAL 0x75767971
- /* current logging offset that points to where new logs should be added */
- #define BCM_ELOG_OFF_OFFSET 0x0004
- /* current logging length (excluding header) */
- #define BCM_ELOG_LEN_OFFSET 0x0008
- #define BCM_ELOG_HEADER_LEN 12
- /*
- * @base: base address of memory where log is saved
- * @max_size: max size of memory reserved for logging
- * @is_active: indicates logging is currently active
- * @level: current logging level
- */
- struct bcm_elog {
- uintptr_t base;
- uint32_t max_size;
- unsigned int is_active;
- unsigned int level;
- };
- static struct bcm_elog global_elog;
- extern void memcpy16(void *dst, const void *src, unsigned int len);
- /*
- * Log one character
- */
- static void elog_putchar(struct bcm_elog *elog, unsigned char c)
- {
- uint32_t offset, len;
- offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
- len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
- mmio_write_8(elog->base + offset, c);
- offset++;
- /* log buffer is now full and need to wrap around */
- if (offset >= elog->max_size)
- offset = BCM_ELOG_HEADER_LEN;
- /* only increment length when log buffer is not full */
- if (len < elog->max_size - BCM_ELOG_HEADER_LEN)
- len++;
- mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
- mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
- }
- static void elog_unsigned_num(struct bcm_elog *elog, unsigned long unum,
- unsigned int radix)
- {
- /* Just need enough space to store 64 bit decimal integer */
- unsigned char num_buf[20];
- int i = 0, rem;
- do {
- rem = unum % radix;
- if (rem < 0xa)
- num_buf[i++] = '0' + rem;
- else
- num_buf[i++] = 'a' + (rem - 0xa);
- } while (unum /= radix);
- while (--i >= 0)
- elog_putchar(elog, num_buf[i]);
- }
- static void elog_string(struct bcm_elog *elog, const char *str)
- {
- while (*str)
- elog_putchar(elog, *str++);
- }
- /*
- * Routine to initialize error logging
- */
- int bcm_elog_init(void *base, uint32_t size, unsigned int level)
- {
- struct bcm_elog *elog = &global_elog;
- uint32_t val;
- elog->base = (uintptr_t)base;
- elog->max_size = size;
- elog->is_active = 1;
- elog->level = level / 10;
- /*
- * If a valid signature can be found, it means logs have been copied
- * into designated memory by another software. In this case, we should
- * not re-initialize the entry header in the designated memory
- */
- val = mmio_read_32(elog->base + BCM_ELOG_SIG_OFFSET);
- if (val != BCM_ELOG_SIG_VAL) {
- mmio_write_32(elog->base + BCM_ELOG_SIG_OFFSET,
- BCM_ELOG_SIG_VAL);
- mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET,
- BCM_ELOG_HEADER_LEN);
- mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, 0);
- }
- return 0;
- }
- /*
- * Routine to disable error logging
- */
- void bcm_elog_exit(void)
- {
- struct bcm_elog *elog = &global_elog;
- if (!elog->is_active)
- return;
- elog->is_active = 0;
- flush_dcache_range(elog->base, elog->max_size);
- }
- /*
- * Routine to copy error logs from current memory to 'dst' memory and continue
- * logging from the new 'dst' memory.
- * dst and base addresses must be 16-bytes aligned.
- */
- int bcm_elog_copy_log(void *dst, uint32_t max_size)
- {
- struct bcm_elog *elog = &global_elog;
- uint32_t offset, len;
- if (!elog->is_active || ((uintptr_t)dst == elog->base))
- return -1;
- /* flush cache before copying logs */
- flush_dcache_range(elog->base, max_size);
- /*
- * If current offset exceeds the new max size, then that is considered
- * as a buffer overflow situation. In this case, we reset the offset
- * back to the beginning
- */
- offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
- if (offset >= max_size) {
- offset = BCM_ELOG_HEADER_LEN;
- mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
- }
- /* note payload length does not include header */
- len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
- if (len > max_size - BCM_ELOG_HEADER_LEN) {
- len = max_size - BCM_ELOG_HEADER_LEN;
- mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
- }
- /* Need to copy everything including the header. */
- memcpy16(dst, (const void *)elog->base, len + BCM_ELOG_HEADER_LEN);
- elog->base = (uintptr_t)dst;
- elog->max_size = max_size;
- return 0;
- }
- /*
- * Main routine to save logs into memory
- */
- void bcm_elog(const char *fmt, ...)
- {
- va_list args;
- const char *prefix_str;
- int bit64;
- int64_t num;
- uint64_t unum;
- char *str;
- struct bcm_elog *elog = &global_elog;
- /* We expect the LOG_MARKER_* macro as the first character */
- unsigned int level = fmt[0];
- if (!elog->is_active || level > elog->level)
- return;
- prefix_str = plat_log_get_prefix(level);
- while (*prefix_str != '\0') {
- elog_putchar(elog, *prefix_str);
- prefix_str++;
- }
- va_start(args, fmt);
- fmt++;
- while (*fmt) {
- bit64 = 0;
- if (*fmt == '%') {
- fmt++;
- /* Check the format specifier */
- loop:
- switch (*fmt) {
- case 'i': /* Fall through to next one */
- case 'd':
- if (bit64)
- num = va_arg(args, int64_t);
- else
- num = va_arg(args, int32_t);
- if (num < 0) {
- elog_putchar(elog, '-');
- unum = (unsigned long)-num;
- } else
- unum = (unsigned long)num;
- elog_unsigned_num(elog, unum, 10);
- break;
- case 's':
- str = va_arg(args, char *);
- elog_string(elog, str);
- break;
- case 'x':
- if (bit64)
- unum = va_arg(args, uint64_t);
- else
- unum = va_arg(args, uint32_t);
- elog_unsigned_num(elog, unum, 16);
- break;
- case 'l':
- bit64 = 1;
- fmt++;
- goto loop;
- case 'u':
- if (bit64)
- unum = va_arg(args, uint64_t);
- else
- unum = va_arg(args, uint32_t);
- elog_unsigned_num(elog, unum, 10);
- break;
- default:
- /* Exit on any other format specifier */
- goto exit;
- }
- fmt++;
- continue;
- }
- elog_putchar(elog, *fmt++);
- }
- exit:
- va_end(args);
- }
|