183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2e9c8d49dSSimon Glass /*
3e9c8d49dSSimon Glass * Logging support
4e9c8d49dSSimon Glass *
5e9c8d49dSSimon Glass * Copyright (c) 2017 Google, Inc
6e9c8d49dSSimon Glass * Written by Simon Glass <sjg@chromium.org>
7e9c8d49dSSimon Glass */
8e9c8d49dSSimon Glass
9e9c8d49dSSimon Glass #include <common.h>
10e9c8d49dSSimon Glass #include <log.h>
11e9c8d49dSSimon Glass #include <malloc.h>
12f941c8d7SSimon Glass #include <dm/uclass.h>
13e9c8d49dSSimon Glass
14e9c8d49dSSimon Glass DECLARE_GLOBAL_DATA_PTR;
15e9c8d49dSSimon Glass
16f941c8d7SSimon Glass static const char *log_cat_name[LOGC_COUNT - LOGC_NONE] = {
17f941c8d7SSimon Glass "none",
18f941c8d7SSimon Glass "arch",
19f941c8d7SSimon Glass "board",
20f941c8d7SSimon Glass "core",
21f941c8d7SSimon Glass "driver-model",
22f941c8d7SSimon Glass "device-tree",
231973b381SHeinrich Schuchardt "efi",
24f941c8d7SSimon Glass };
25f941c8d7SSimon Glass
26f941c8d7SSimon Glass static const char *log_level_name[LOGL_COUNT] = {
27f941c8d7SSimon Glass "EMERG",
28f941c8d7SSimon Glass "ALERT",
29f941c8d7SSimon Glass "CRIT",
30f941c8d7SSimon Glass "ERR",
31f941c8d7SSimon Glass "WARNING",
32f941c8d7SSimon Glass "NOTICE",
33f941c8d7SSimon Glass "INFO",
34f941c8d7SSimon Glass "DEBUG",
35f941c8d7SSimon Glass "CONTENT",
36f941c8d7SSimon Glass "IO",
37f941c8d7SSimon Glass };
38f941c8d7SSimon Glass
log_get_cat_name(enum log_category_t cat)39f941c8d7SSimon Glass const char *log_get_cat_name(enum log_category_t cat)
40f941c8d7SSimon Glass {
41c2e4e7e6SSimon Glass const char *name;
42c2e4e7e6SSimon Glass
43c2e4e7e6SSimon Glass if (cat < 0 || cat >= LOGC_COUNT)
44c2e4e7e6SSimon Glass return "<invalid>";
45f941c8d7SSimon Glass if (cat >= LOGC_NONE)
46f941c8d7SSimon Glass return log_cat_name[cat - LOGC_NONE];
47f941c8d7SSimon Glass
48c2e4e7e6SSimon Glass name = uclass_get_name((enum uclass_id)cat);
49c2e4e7e6SSimon Glass
50c2e4e7e6SSimon Glass return name ? name : "<missing>";
51f941c8d7SSimon Glass }
52f941c8d7SSimon Glass
log_get_cat_by_name(const char * name)53f941c8d7SSimon Glass enum log_category_t log_get_cat_by_name(const char *name)
54f941c8d7SSimon Glass {
55f941c8d7SSimon Glass enum uclass_id id;
56f941c8d7SSimon Glass int i;
57f941c8d7SSimon Glass
58f941c8d7SSimon Glass for (i = LOGC_NONE; i < LOGC_COUNT; i++)
59f941c8d7SSimon Glass if (!strcmp(name, log_cat_name[i - LOGC_NONE]))
60f941c8d7SSimon Glass return i;
61f941c8d7SSimon Glass id = uclass_get_by_name(name);
62f941c8d7SSimon Glass if (id != UCLASS_INVALID)
63f941c8d7SSimon Glass return (enum log_category_t)id;
64f941c8d7SSimon Glass
65f941c8d7SSimon Glass return LOGC_NONE;
66f941c8d7SSimon Glass }
67f941c8d7SSimon Glass
log_get_level_name(enum log_level_t level)68f941c8d7SSimon Glass const char *log_get_level_name(enum log_level_t level)
69f941c8d7SSimon Glass {
70f941c8d7SSimon Glass if (level >= LOGL_COUNT)
71f941c8d7SSimon Glass return "INVALID";
72f941c8d7SSimon Glass return log_level_name[level];
73f941c8d7SSimon Glass }
74f941c8d7SSimon Glass
log_get_level_by_name(const char * name)75f941c8d7SSimon Glass enum log_level_t log_get_level_by_name(const char *name)
76f941c8d7SSimon Glass {
77f941c8d7SSimon Glass int i;
78f941c8d7SSimon Glass
79f941c8d7SSimon Glass for (i = 0; i < LOGL_COUNT; i++) {
80f941c8d7SSimon Glass if (!strcasecmp(log_level_name[i], name))
81f941c8d7SSimon Glass return i;
82f941c8d7SSimon Glass }
83f941c8d7SSimon Glass
84f941c8d7SSimon Glass return LOGL_NONE;
85f941c8d7SSimon Glass }
86f941c8d7SSimon Glass
log_device_find_by_name(const char * drv_name)87e9c8d49dSSimon Glass static struct log_device *log_device_find_by_name(const char *drv_name)
88e9c8d49dSSimon Glass {
89e9c8d49dSSimon Glass struct log_device *ldev;
90e9c8d49dSSimon Glass
91e9c8d49dSSimon Glass list_for_each_entry(ldev, &gd->log_head, sibling_node) {
92e9c8d49dSSimon Glass if (!strcmp(drv_name, ldev->drv->name))
93e9c8d49dSSimon Glass return ldev;
94e9c8d49dSSimon Glass }
95e9c8d49dSSimon Glass
96e9c8d49dSSimon Glass return NULL;
97e9c8d49dSSimon Glass }
98e9c8d49dSSimon Glass
99e9c8d49dSSimon Glass /**
100e9c8d49dSSimon Glass * log_has_cat() - check if a log category exists within a list
101e9c8d49dSSimon Glass *
102e9c8d49dSSimon Glass * @cat_list: List of categories to check, at most LOGF_MAX_CATEGORIES entries
103e9c8d49dSSimon Glass * long, terminated by LC_END if fewer
104e9c8d49dSSimon Glass * @cat: Category to search for
105e9c8d49dSSimon Glass * @return true if @cat is in @cat_list, else false
106e9c8d49dSSimon Glass */
log_has_cat(enum log_category_t cat_list[],enum log_category_t cat)107e9c8d49dSSimon Glass static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
108e9c8d49dSSimon Glass {
109e9c8d49dSSimon Glass int i;
110e9c8d49dSSimon Glass
111e9c8d49dSSimon Glass for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) {
112e9c8d49dSSimon Glass if (cat_list[i] == cat)
113e9c8d49dSSimon Glass return true;
114e9c8d49dSSimon Glass }
115e9c8d49dSSimon Glass
116e9c8d49dSSimon Glass return false;
117e9c8d49dSSimon Glass }
118e9c8d49dSSimon Glass
119e9c8d49dSSimon Glass /**
120e9c8d49dSSimon Glass * log_has_file() - check if a file is with a list
121e9c8d49dSSimon Glass *
122e9c8d49dSSimon Glass * @file_list: List of files to check, separated by comma
123e9c8d49dSSimon Glass * @file: File to check for. This string is matched against the end of each
124e9c8d49dSSimon Glass * file in the list, i.e. ignoring any preceding path. The list is
125e9c8d49dSSimon Glass * intended to consist of relative pathnames, e.g. common/main.c,cmd/log.c
126e9c8d49dSSimon Glass * @return true if @file is in @file_list, else false
127e9c8d49dSSimon Glass */
log_has_file(const char * file_list,const char * file)128e9c8d49dSSimon Glass static bool log_has_file(const char *file_list, const char *file)
129e9c8d49dSSimon Glass {
130e9c8d49dSSimon Glass int file_len = strlen(file);
131e9c8d49dSSimon Glass const char *s, *p;
132e9c8d49dSSimon Glass int substr_len;
133e9c8d49dSSimon Glass
134e9c8d49dSSimon Glass for (s = file_list; *s; s = p + (*p != '\0')) {
135e9c8d49dSSimon Glass p = strchrnul(s, ',');
136e9c8d49dSSimon Glass substr_len = p - s;
137e9c8d49dSSimon Glass if (file_len >= substr_len &&
138e9c8d49dSSimon Glass !strncmp(file + file_len - substr_len, s, substr_len))
139e9c8d49dSSimon Glass return true;
140e9c8d49dSSimon Glass }
141e9c8d49dSSimon Glass
142e9c8d49dSSimon Glass return false;
143e9c8d49dSSimon Glass }
144e9c8d49dSSimon Glass
145e9c8d49dSSimon Glass /**
146e9c8d49dSSimon Glass * log_passes_filters() - check if a log record passes the filters for a device
147e9c8d49dSSimon Glass *
148e9c8d49dSSimon Glass * @ldev: Log device to check
149e9c8d49dSSimon Glass * @rec: Log record to check
150e9c8d49dSSimon Glass * @return true if @rec is not blocked by the filters in @ldev, false if it is
151e9c8d49dSSimon Glass */
log_passes_filters(struct log_device * ldev,struct log_rec * rec)152e9c8d49dSSimon Glass static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
153e9c8d49dSSimon Glass {
154e9c8d49dSSimon Glass struct log_filter *filt;
155e9c8d49dSSimon Glass
156e9c8d49dSSimon Glass /* If there are no filters, filter on the default log level */
157e9c8d49dSSimon Glass if (list_empty(&ldev->filter_head)) {
158e9c8d49dSSimon Glass if (rec->level > gd->default_log_level)
159e9c8d49dSSimon Glass return false;
160e9c8d49dSSimon Glass return true;
161e9c8d49dSSimon Glass }
162e9c8d49dSSimon Glass
163e9c8d49dSSimon Glass list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
164e9c8d49dSSimon Glass if (rec->level > filt->max_level)
165e9c8d49dSSimon Glass continue;
166e9c8d49dSSimon Glass if ((filt->flags & LOGFF_HAS_CAT) &&
167e9c8d49dSSimon Glass !log_has_cat(filt->cat_list, rec->cat))
168e9c8d49dSSimon Glass continue;
169e9c8d49dSSimon Glass if (filt->file_list &&
170e9c8d49dSSimon Glass !log_has_file(filt->file_list, rec->file))
171e9c8d49dSSimon Glass continue;
172e9c8d49dSSimon Glass return true;
173e9c8d49dSSimon Glass }
174e9c8d49dSSimon Glass
175e9c8d49dSSimon Glass return false;
176e9c8d49dSSimon Glass }
177e9c8d49dSSimon Glass
178e9c8d49dSSimon Glass /**
179e9c8d49dSSimon Glass * log_dispatch() - Send a log record to all log devices for processing
180e9c8d49dSSimon Glass *
181e9c8d49dSSimon Glass * The log record is sent to each log device in turn, skipping those which have
182e9c8d49dSSimon Glass * filters which block the record
183e9c8d49dSSimon Glass *
184e9c8d49dSSimon Glass * @rec: Log record to dispatch
185e9c8d49dSSimon Glass * @return 0 (meaning success)
186e9c8d49dSSimon Glass */
log_dispatch(struct log_rec * rec)187e9c8d49dSSimon Glass static int log_dispatch(struct log_rec *rec)
188e9c8d49dSSimon Glass {
189e9c8d49dSSimon Glass struct log_device *ldev;
190e9c8d49dSSimon Glass
191e9c8d49dSSimon Glass list_for_each_entry(ldev, &gd->log_head, sibling_node) {
192e9c8d49dSSimon Glass if (log_passes_filters(ldev, rec))
193e9c8d49dSSimon Glass ldev->drv->emit(ldev, rec);
194e9c8d49dSSimon Glass }
195e9c8d49dSSimon Glass
196e9c8d49dSSimon Glass return 0;
197e9c8d49dSSimon Glass }
198e9c8d49dSSimon Glass
_log(enum log_category_t cat,enum log_level_t level,const char * file,int line,const char * func,const char * fmt,...)199e9c8d49dSSimon Glass int _log(enum log_category_t cat, enum log_level_t level, const char *file,
200e9c8d49dSSimon Glass int line, const char *func, const char *fmt, ...)
201e9c8d49dSSimon Glass {
202e9c8d49dSSimon Glass char buf[CONFIG_SYS_CBSIZE];
203e9c8d49dSSimon Glass struct log_rec rec;
204e9c8d49dSSimon Glass va_list args;
205e9c8d49dSSimon Glass
206e9c8d49dSSimon Glass rec.cat = cat;
207e9c8d49dSSimon Glass rec.level = level;
208e9c8d49dSSimon Glass rec.file = file;
209e9c8d49dSSimon Glass rec.line = line;
210e9c8d49dSSimon Glass rec.func = func;
211e9c8d49dSSimon Glass va_start(args, fmt);
212e9c8d49dSSimon Glass vsnprintf(buf, sizeof(buf), fmt, args);
213e9c8d49dSSimon Glass va_end(args);
214e9c8d49dSSimon Glass rec.msg = buf;
215e9c8d49dSSimon Glass if (!gd || !(gd->flags & GD_FLG_LOG_READY)) {
216e9c8d49dSSimon Glass if (gd)
217e9c8d49dSSimon Glass gd->log_drop_count++;
218e9c8d49dSSimon Glass return -ENOSYS;
219e9c8d49dSSimon Glass }
220e9c8d49dSSimon Glass log_dispatch(&rec);
221e9c8d49dSSimon Glass
222e9c8d49dSSimon Glass return 0;
223e9c8d49dSSimon Glass }
224e9c8d49dSSimon Glass
log_add_filter(const char * drv_name,enum log_category_t cat_list[],enum log_level_t max_level,const char * file_list)225e9c8d49dSSimon Glass int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
226e9c8d49dSSimon Glass enum log_level_t max_level, const char *file_list)
227e9c8d49dSSimon Glass {
228e9c8d49dSSimon Glass struct log_filter *filt;
229e9c8d49dSSimon Glass struct log_device *ldev;
23045fac9fcSSimon Glass int ret;
231e9c8d49dSSimon Glass int i;
232e9c8d49dSSimon Glass
233e9c8d49dSSimon Glass ldev = log_device_find_by_name(drv_name);
234e9c8d49dSSimon Glass if (!ldev)
235e9c8d49dSSimon Glass return -ENOENT;
236e9c8d49dSSimon Glass filt = (struct log_filter *)calloc(1, sizeof(*filt));
237e9c8d49dSSimon Glass if (!filt)
238e9c8d49dSSimon Glass return -ENOMEM;
239e9c8d49dSSimon Glass
240e9c8d49dSSimon Glass if (cat_list) {
241e9c8d49dSSimon Glass filt->flags |= LOGFF_HAS_CAT;
242e9c8d49dSSimon Glass for (i = 0; ; i++) {
24345fac9fcSSimon Glass if (i == ARRAY_SIZE(filt->cat_list)) {
24445fac9fcSSimon Glass ret = -ENOSPC;
24545fac9fcSSimon Glass goto err;
24645fac9fcSSimon Glass }
247e9c8d49dSSimon Glass filt->cat_list[i] = cat_list[i];
248e9c8d49dSSimon Glass if (cat_list[i] == LOGC_END)
249e9c8d49dSSimon Glass break;
250e9c8d49dSSimon Glass }
251e9c8d49dSSimon Glass }
252e9c8d49dSSimon Glass filt->max_level = max_level;
253e9c8d49dSSimon Glass if (file_list) {
254e9c8d49dSSimon Glass filt->file_list = strdup(file_list);
25545fac9fcSSimon Glass if (!filt->file_list) {
25645fac9fcSSimon Glass ret = ENOMEM;
25745fac9fcSSimon Glass goto err;
25845fac9fcSSimon Glass }
259e9c8d49dSSimon Glass }
260e9c8d49dSSimon Glass filt->filter_num = ldev->next_filter_num++;
261e9c8d49dSSimon Glass list_add_tail(&filt->sibling_node, &ldev->filter_head);
262e9c8d49dSSimon Glass
263e9c8d49dSSimon Glass return filt->filter_num;
264e9c8d49dSSimon Glass
26545fac9fcSSimon Glass err:
266e9c8d49dSSimon Glass free(filt);
26745fac9fcSSimon Glass return ret;
268e9c8d49dSSimon Glass }
269e9c8d49dSSimon Glass
log_remove_filter(const char * drv_name,int filter_num)270e9c8d49dSSimon Glass int log_remove_filter(const char *drv_name, int filter_num)
271e9c8d49dSSimon Glass {
272e9c8d49dSSimon Glass struct log_filter *filt;
273e9c8d49dSSimon Glass struct log_device *ldev;
274e9c8d49dSSimon Glass
275e9c8d49dSSimon Glass ldev = log_device_find_by_name(drv_name);
276e9c8d49dSSimon Glass if (!ldev)
277e9c8d49dSSimon Glass return -ENOENT;
278e9c8d49dSSimon Glass
279e9c8d49dSSimon Glass list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
280e9c8d49dSSimon Glass if (filt->filter_num == filter_num) {
281e9c8d49dSSimon Glass list_del(&filt->sibling_node);
282e9c8d49dSSimon Glass free(filt);
283e9c8d49dSSimon Glass
284e9c8d49dSSimon Glass return 0;
285e9c8d49dSSimon Glass }
286e9c8d49dSSimon Glass }
287e9c8d49dSSimon Glass
288e9c8d49dSSimon Glass return -ENOENT;
289e9c8d49dSSimon Glass }
290e9c8d49dSSimon Glass
log_init(void)291e9c8d49dSSimon Glass int log_init(void)
292e9c8d49dSSimon Glass {
293e9c8d49dSSimon Glass struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
294e9c8d49dSSimon Glass const int count = ll_entry_count(struct log_driver, log_driver);
295e9c8d49dSSimon Glass struct log_driver *end = drv + count;
296e9c8d49dSSimon Glass
297e9c8d49dSSimon Glass /*
298e9c8d49dSSimon Glass * We cannot add runtime data to the driver since it is likely stored
299e9c8d49dSSimon Glass * in rodata. Instead, set up a 'device' corresponding to each driver.
300e9c8d49dSSimon Glass * We only support having a single device.
301e9c8d49dSSimon Glass */
302e9c8d49dSSimon Glass INIT_LIST_HEAD((struct list_head *)&gd->log_head);
303e9c8d49dSSimon Glass while (drv < end) {
304e9c8d49dSSimon Glass struct log_device *ldev;
305e9c8d49dSSimon Glass
306e9c8d49dSSimon Glass ldev = calloc(1, sizeof(*ldev));
307e9c8d49dSSimon Glass if (!ldev) {
308e9c8d49dSSimon Glass debug("%s: Cannot allocate memory\n", __func__);
309e9c8d49dSSimon Glass return -ENOMEM;
310e9c8d49dSSimon Glass }
311e9c8d49dSSimon Glass INIT_LIST_HEAD(&ldev->filter_head);
312e9c8d49dSSimon Glass ldev->drv = drv;
313e9c8d49dSSimon Glass list_add_tail(&ldev->sibling_node,
314e9c8d49dSSimon Glass (struct list_head *)&gd->log_head);
315e9c8d49dSSimon Glass drv++;
316e9c8d49dSSimon Glass }
317af1bc0cfSSimon Glass gd->flags |= GD_FLG_LOG_READY;
3182b1dc29aSSimon Glass if (!gd->default_log_level)
319*f0b05c95SSimon Glass gd->default_log_level = CONFIG_LOG_DEFAULT_LEVEL;
3203b73e8d0SSimon Glass gd->log_fmt = LOGF_DEFAULT;
321e9c8d49dSSimon Glass
322e9c8d49dSSimon Glass return 0;
323e9c8d49dSSimon Glass }
324