1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Logging support
4 *
5 * Copyright (c) 2017 Google, Inc
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
9#include <common.h>
10#include <display_options.h>
11#include <log.h>
12#include <malloc.h>
13#include <asm/global_data.h>
14#include <dm/uclass.h>
15
16DECLARE_GLOBAL_DATA_PTR;
17
18static const char *const log_cat_name[] = {
19	"none",
20	"arch",
21	"board",
22	"core",
23	"driver-model",
24	"device-tree",
25	"efi",
26	"alloc",
27	"sandbox",
28	"bloblist",
29	"devres",
30	"acpi",
31	"boot",
32	"event",
33	"fs",
34	"expo",
35};
36
37_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE,
38	       "log_cat_name size");
39
40static const char *const log_level_name[] = {
41	"EMERG",
42	"ALERT",
43	"CRIT",
44	"ERR",
45	"WARNING",
46	"NOTICE",
47	"INFO",
48	"DEBUG",
49	"CONTENT",
50	"IO",
51};
52
53_Static_assert(ARRAY_SIZE(log_level_name) == LOGL_COUNT, "log_level_name size");
54
55/* All error responses MUST begin with '<' */
56const char *log_get_cat_name(enum log_category_t cat)
57{
58	const char *name;
59
60	if (cat < 0 || cat >= LOGC_COUNT)
61		return "<invalid>";
62	if (cat >= LOGC_NONE)
63		return log_cat_name[cat - LOGC_NONE];
64
65#if CONFIG_IS_ENABLED(DM)
66	name = uclass_get_name((enum uclass_id)cat);
67#else
68	name = NULL;
69#endif
70
71	return name ? name : "<missing>";
72}
73
74enum log_category_t log_get_cat_by_name(const char *name)
75{
76	enum uclass_id id;
77	int i;
78
79	for (i = LOGC_NONE; i < LOGC_COUNT; i++)
80		if (!strcmp(name, log_cat_name[i - LOGC_NONE]))
81			return i;
82	id = uclass_get_by_name(name);
83	if (id != UCLASS_INVALID)
84		return (enum log_category_t)id;
85
86	return LOGC_NONE;
87}
88
89const char *log_get_level_name(enum log_level_t level)
90{
91	if (level >= LOGL_COUNT)
92		return "INVALID";
93	return log_level_name[level];
94}
95
96enum log_level_t log_get_level_by_name(const char *name)
97{
98	int i;
99
100	for (i = 0; i < LOGL_COUNT; i++) {
101		if (!strcasecmp(log_level_name[i], name))
102			return i;
103	}
104
105	return LOGL_NONE;
106}
107
108struct log_device *log_device_find_by_name(const char *drv_name)
109{
110	struct log_device *ldev;
111
112	list_for_each_entry(ldev, &gd->log_head, sibling_node) {
113		if (!strcmp(drv_name, ldev->drv->name))
114			return ldev;
115	}
116
117	return NULL;
118}
119
120bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
121{
122	int i;
123
124	for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) {
125		if (cat_list[i] == cat)
126			return true;
127	}
128
129	return false;
130}
131
132bool log_has_file(const char *file_list, const char *file)
133{
134	int file_len = strlen(file);
135	const char *s, *p;
136	int substr_len;
137
138	for (s = file_list; *s; s = p + (*p != '\0')) {
139		p = strchrnul(s, ',');
140		substr_len = p - s;
141		if (file_len >= substr_len &&
142		    !strncmp(file + file_len - substr_len, s, substr_len))
143			return true;
144	}
145
146	return false;
147}
148
149/**
150 * log_passes_filters() - check if a log record passes the filters for a device
151 *
152 * @ldev: Log device to check
153 * @rec: Log record to check
154 * Return: true if @rec is not blocked by the filters in @ldev, false if it is
155 */
156static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
157{
158	struct log_filter *filt;
159
160	if (rec->flags & LOGRECF_FORCE_DEBUG)
161		return true;
162
163	/* If there are no filters, filter on the default log level */
164	if (list_empty(&ldev->filter_head)) {
165		if (rec->level > gd->default_log_level)
166			return false;
167		return true;
168	}
169
170	list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
171		if (filt->flags & LOGFF_LEVEL_MIN) {
172			if (rec->level < filt->level)
173				continue;
174		} else if (rec->level > filt->level) {
175			continue;
176		}
177
178		if ((filt->flags & LOGFF_HAS_CAT) &&
179		    !log_has_cat(filt->cat_list, rec->cat))
180			continue;
181
182		if (filt->file_list &&
183		    !log_has_file(filt->file_list, rec->file))
184			continue;
185
186		if (filt->flags & LOGFF_DENY)
187			return false;
188		else
189			return true;
190	}
191
192	return false;
193}
194
195/**
196 * log_dispatch() - Send a log record to all log devices for processing
197 *
198 * The log record is sent to each log device in turn, skipping those which have
199 * filters which block the record.
200 *
201 * All log messages created while processing log record @rec are ignored.
202 *
203 * @rec:	log record to dispatch
204 * Return:	0 msg sent, 1 msg not sent while already dispatching another msg
205 */
206static int log_dispatch(struct log_rec *rec, const char *fmt, va_list args)
207{
208	struct log_device *ldev;
209	char buf[CONFIG_SYS_CBSIZE];
210
211	/*
212	 * When a log driver writes messages (e.g. via the network stack) this
213	 * may result in further generated messages. We cannot process them here
214	 * as this might result in infinite recursion.
215	 */
216	if (gd->processing_msg)
217		return 1;
218
219	/* Emit message */
220	gd->processing_msg = true;
221	list_for_each_entry(ldev, &gd->log_head, sibling_node) {
222		if ((ldev->flags & LOGDF_ENABLE) &&
223		    log_passes_filters(ldev, rec)) {
224			if (!rec->msg) {
225				int len;
226
227				len = vsnprintf(buf, sizeof(buf), fmt, args);
228				rec->msg = buf;
229				gd->log_cont = len && buf[len - 1] != '\n';
230			}
231			ldev->drv->emit(ldev, rec);
232		}
233	}
234	gd->processing_msg = false;
235	return 0;
236}
237
238int _log(enum log_category_t cat, enum log_level_t level, const char *file,
239	 int line, const char *func, const char *fmt, ...)
240{
241	struct log_rec rec;
242	va_list args;
243
244	if (!gd)
245		return -ENOSYS;
246
247	/* Check for message continuation */
248	if (cat == LOGC_CONT)
249		cat = gd->logc_prev;
250	if (level == LOGL_CONT)
251		level = gd->logl_prev;
252
253	rec.cat = cat;
254	rec.level = level & LOGL_LEVEL_MASK;
255	rec.flags = 0;
256	if (level & LOGL_FORCE_DEBUG)
257		rec.flags |= LOGRECF_FORCE_DEBUG;
258	if (gd->log_cont)
259		rec.flags |= LOGRECF_CONT;
260	rec.file = file;
261	rec.line = line;
262	rec.func = func;
263	rec.msg = NULL;
264
265	if (!(gd->flags & GD_FLG_LOG_READY)) {
266		gd->log_drop_count++;
267
268		/* display dropped traces with console puts and DEBUG_UART */
269		if (rec.level <= CONFIG_LOG_DEFAULT_LEVEL ||
270		    rec.flags & LOGRECF_FORCE_DEBUG) {
271			char buf[CONFIG_SYS_CBSIZE];
272
273			va_start(args, fmt);
274			vsnprintf(buf, sizeof(buf), fmt, args);
275			puts(buf);
276			va_end(args);
277		}
278
279		return -ENOSYS;
280	}
281	va_start(args, fmt);
282	if (!log_dispatch(&rec, fmt, args)) {
283		gd->logc_prev = cat;
284		gd->logl_prev = level;
285	}
286	va_end(args);
287
288	return 0;
289}
290
291#define MAX_LINE_LENGTH_BYTES		64
292#define DEFAULT_LINE_LENGTH_BYTES	16
293
294int _log_buffer(enum log_category_t cat, enum log_level_t level,
295		const char *file, int line, const char *func, ulong addr,
296		const void *data, uint width, uint count, uint linelen)
297{
298	if (linelen * width > MAX_LINE_LENGTH_BYTES)
299		linelen = MAX_LINE_LENGTH_BYTES / width;
300	if (linelen < 1)
301		linelen = DEFAULT_LINE_LENGTH_BYTES / width;
302
303	while (count) {
304		uint thislinelen;
305		char buf[HEXDUMP_MAX_BUF_LENGTH(width * linelen)];
306
307		thislinelen = hexdump_line(addr, data, width, count, linelen,
308					   buf, sizeof(buf));
309		assert(thislinelen >= 0);
310		_log(cat, level, file, line, func, "%s\n", buf);
311
312		/* update references */
313		data += thislinelen * width;
314		addr += thislinelen * width;
315		count -= thislinelen;
316	}
317
318	return 0;
319}
320
321int log_add_filter_flags(const char *drv_name, enum log_category_t cat_list[],
322			 enum log_level_t level, const char *file_list,
323			 int flags)
324{
325	struct log_filter *filt;
326	struct log_device *ldev;
327	int ret;
328	int i;
329
330	ldev = log_device_find_by_name(drv_name);
331	if (!ldev)
332		return -ENOENT;
333	filt = calloc(1, sizeof(*filt));
334	if (!filt)
335		return -ENOMEM;
336
337	filt->flags = flags;
338	if (cat_list) {
339		filt->flags |= LOGFF_HAS_CAT;
340		for (i = 0; ; i++) {
341			if (i == ARRAY_SIZE(filt->cat_list)) {
342				ret = -ENOSPC;
343				goto err;
344			}
345			filt->cat_list[i] = cat_list[i];
346			if (cat_list[i] == LOGC_END)
347				break;
348		}
349	}
350	filt->level = level;
351	if (file_list) {
352		filt->file_list = strdup(file_list);
353		if (!filt->file_list) {
354			ret = -ENOMEM;
355			goto err;
356		}
357	}
358	filt->filter_num = ldev->next_filter_num++;
359	/* Add deny filters to the beginning of the list */
360	if (flags & LOGFF_DENY)
361		list_add(&filt->sibling_node, &ldev->filter_head);
362	else
363		list_add_tail(&filt->sibling_node, &ldev->filter_head);
364
365	return filt->filter_num;
366
367err:
368	free(filt);
369	return ret;
370}
371
372int log_remove_filter(const char *drv_name, int filter_num)
373{
374	struct log_filter *filt;
375	struct log_device *ldev;
376
377	ldev = log_device_find_by_name(drv_name);
378	if (!ldev)
379		return -ENOENT;
380
381	list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
382		if (filt->filter_num == filter_num) {
383			list_del(&filt->sibling_node);
384			free(filt);
385
386			return 0;
387		}
388	}
389
390	return -ENOENT;
391}
392
393/**
394 * log_find_device_by_drv() - Find a device by its driver
395 *
396 * @drv: Log driver
397 * Return: Device associated with that driver, or NULL if not found
398 */
399static struct log_device *log_find_device_by_drv(struct log_driver *drv)
400{
401	struct log_device *ldev;
402
403	list_for_each_entry(ldev, &gd->log_head, sibling_node) {
404		if (ldev->drv == drv)
405			return ldev;
406	}
407	/*
408	 * It is quite hard to pass an invalid driver since passing an unknown
409	 * LOG_GET_DRIVER(xxx) would normally produce a compilation error. But
410	 * it is possible to pass NULL, for example, so this
411	 */
412
413	return NULL;
414}
415
416int log_device_set_enable(struct log_driver *drv, bool enable)
417{
418	struct log_device *ldev;
419
420	ldev = log_find_device_by_drv(drv);
421	if (!ldev)
422		return -ENOENT;
423	if (enable)
424		ldev->flags |= LOGDF_ENABLE;
425	else
426		ldev->flags &= ~LOGDF_ENABLE;
427
428	return 0;
429}
430
431void log_fixup_for_gd_move(struct global_data *new_gd)
432{
433	new_gd->log_head.prev->next = &new_gd->log_head;
434}
435
436int log_init(void)
437{
438	struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
439	const int count = ll_entry_count(struct log_driver, log_driver);
440	struct log_driver *end = drv + count;
441
442	/*
443	 * We cannot add runtime data to the driver since it is likely stored
444	 * in rodata. Instead, set up a 'device' corresponding to each driver.
445	 * We only support having a single device for each driver.
446	 */
447	INIT_LIST_HEAD((struct list_head *)&gd->log_head);
448	while (drv < end) {
449		struct log_device *ldev;
450
451		ldev = calloc(1, sizeof(*ldev));
452		if (!ldev) {
453			debug("%s: Cannot allocate memory\n", __func__);
454			return -ENOMEM;
455		}
456		INIT_LIST_HEAD(&ldev->filter_head);
457		ldev->drv = drv;
458		ldev->flags = drv->flags;
459		list_add_tail(&ldev->sibling_node,
460			      (struct list_head *)&gd->log_head);
461		drv++;
462	}
463	gd->flags |= GD_FLG_LOG_READY;
464	if (!gd->default_log_level)
465		gd->default_log_level = CONFIG_LOG_DEFAULT_LEVEL;
466	gd->log_fmt = log_get_default_format();
467	gd->logc_prev = LOGC_NONE;
468	gd->logl_prev = LOGL_INFO;
469
470	return 0;
471}
472