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