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