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 */
elog_putchar(struct bcm_elog * elog,unsigned char c)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
elog_unsigned_num(struct bcm_elog * elog,unsigned long unum,unsigned int radix)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
elog_string(struct bcm_elog * elog,const char * str)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 */
bcm_elog_init(void * base,uint32_t size,unsigned int level)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 */
bcm_elog_exit(void)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 */
bcm_elog_copy_log(void * dst,uint32_t max_size)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 */
bcm_elog(const char * fmt,...)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