Merge branch 'master' of https://gitlab.denx.de/u-boot/custodians/u-boot-samsung
[platform/kernel/u-boot.git] / common / log.c
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 <log.h>
11 #include <malloc.h>
12 #include <dm/uclass.h>
13
14 DECLARE_GLOBAL_DATA_PTR;
15
16 static const char *log_cat_name[LOGC_COUNT - LOGC_NONE] = {
17         "none",
18         "arch",
19         "board",
20         "core",
21         "driver-model",
22         "device-tree",
23         "efi",
24         "alloc",
25         "sandbox",
26         "bloblist",
27         "devres",
28         "acpi",
29 };
30
31 static const char *log_level_name[LOGL_COUNT] = {
32         "EMERG",
33         "ALERT",
34         "CRIT",
35         "ERR",
36         "WARNING",
37         "NOTICE",
38         "INFO",
39         "DEBUG",
40         "CONTENT",
41         "IO",
42 };
43
44 const char *log_get_cat_name(enum log_category_t cat)
45 {
46         const char *name;
47
48         if (cat < 0 || cat >= LOGC_COUNT)
49                 return "<invalid>";
50         if (cat >= LOGC_NONE)
51                 return log_cat_name[cat - LOGC_NONE];
52
53 #if CONFIG_IS_ENABLED(DM)
54         name = uclass_get_name((enum uclass_id)cat);
55 #else
56         name = NULL;
57 #endif
58
59         return name ? name : "<missing>";
60 }
61
62 enum log_category_t log_get_cat_by_name(const char *name)
63 {
64         enum uclass_id id;
65         int i;
66
67         for (i = LOGC_NONE; i < LOGC_COUNT; i++)
68                 if (!strcmp(name, log_cat_name[i - LOGC_NONE]))
69                         return i;
70         id = uclass_get_by_name(name);
71         if (id != UCLASS_INVALID)
72                 return (enum log_category_t)id;
73
74         return LOGC_NONE;
75 }
76
77 const char *log_get_level_name(enum log_level_t level)
78 {
79         if (level >= LOGL_COUNT)
80                 return "INVALID";
81         return log_level_name[level];
82 }
83
84 enum log_level_t log_get_level_by_name(const char *name)
85 {
86         int i;
87
88         for (i = 0; i < LOGL_COUNT; i++) {
89                 if (!strcasecmp(log_level_name[i], name))
90                         return i;
91         }
92
93         return LOGL_NONE;
94 }
95
96 static struct log_device *log_device_find_by_name(const char *drv_name)
97 {
98         struct log_device *ldev;
99
100         list_for_each_entry(ldev, &gd->log_head, sibling_node) {
101                 if (!strcmp(drv_name, ldev->drv->name))
102                         return ldev;
103         }
104
105         return NULL;
106 }
107
108 /**
109  * log_has_cat() - check if a log category exists within a list
110  *
111  * @cat_list: List of categories to check, at most LOGF_MAX_CATEGORIES entries
112  *      long, terminated by LC_END if fewer
113  * @cat: Category to search for
114  * @return true if @cat is in @cat_list, else false
115  */
116 static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
117 {
118         int i;
119
120         for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) {
121                 if (cat_list[i] == cat)
122                         return true;
123         }
124
125         return false;
126 }
127
128 /**
129  * log_has_file() - check if a file is with a list
130  *
131  * @file_list: List of files to check, separated by comma
132  * @file: File to check for. This string is matched against the end of each
133  *      file in the list, i.e. ignoring any preceding path. The list is
134  *      intended to consist of relative pathnames, e.g. common/main.c,cmd/log.c
135  * @return true if @file is in @file_list, else false
136  */
137 static bool log_has_file(const char *file_list, const char *file)
138 {
139         int file_len = strlen(file);
140         const char *s, *p;
141         int substr_len;
142
143         for (s = file_list; *s; s = p + (*p != '\0')) {
144                 p = strchrnul(s, ',');
145                 substr_len = p - s;
146                 if (file_len >= substr_len &&
147                     !strncmp(file + file_len - substr_len, s, substr_len))
148                         return true;
149         }
150
151         return false;
152 }
153
154 /**
155  * log_passes_filters() - check if a log record passes the filters for a device
156  *
157  * @ldev: Log device to check
158  * @rec: Log record to check
159  * @return true if @rec is not blocked by the filters in @ldev, false if it is
160  */
161 static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
162 {
163         struct log_filter *filt;
164
165         if (rec->force_debug)
166                 return true;
167
168         /* If there are no filters, filter on the default log level */
169         if (list_empty(&ldev->filter_head)) {
170                 if (rec->level > gd->default_log_level)
171                         return false;
172                 return true;
173         }
174
175         list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
176                 if (rec->level > filt->max_level)
177                         continue;
178                 if ((filt->flags & LOGFF_HAS_CAT) &&
179                     !log_has_cat(filt->cat_list, rec->cat))
180                         continue;
181                 if (filt->file_list &&
182                     !log_has_file(filt->file_list, rec->file))
183                         continue;
184                 return true;
185         }
186
187         return false;
188 }
189
190 /**
191  * log_dispatch() - Send a log record to all log devices for processing
192  *
193  * The log record is sent to each log device in turn, skipping those which have
194  * filters which block the record
195  *
196  * @rec: Log record to dispatch
197  * @return 0 (meaning success)
198  */
199 static int log_dispatch(struct log_rec *rec)
200 {
201         struct log_device *ldev;
202         static int processing_msg;
203
204         /*
205          * When a log driver writes messages (e.g. via the network stack) this
206          * may result in further generated messages. We cannot process them here
207          * as this might result in infinite recursion.
208          */
209         if (processing_msg)
210                 return 0;
211
212         /* Emit message */
213         processing_msg = 1;
214         list_for_each_entry(ldev, &gd->log_head, sibling_node) {
215                 if ((ldev->flags & LOGDF_ENABLE) &&
216                     log_passes_filters(ldev, rec))
217                         ldev->drv->emit(ldev, rec);
218         }
219         processing_msg = 0;
220         return 0;
221 }
222
223 int _log(enum log_category_t cat, enum log_level_t level, const char *file,
224          int line, const char *func, const char *fmt, ...)
225 {
226         char buf[CONFIG_SYS_CBSIZE];
227         struct log_rec rec;
228         va_list args;
229
230         rec.cat = cat;
231         rec.level = level & LOGL_LEVEL_MASK;
232         rec.force_debug = level & LOGL_FORCE_DEBUG;
233         rec.file = file;
234         rec.line = line;
235         rec.func = func;
236         va_start(args, fmt);
237         vsnprintf(buf, sizeof(buf), fmt, args);
238         va_end(args);
239         rec.msg = buf;
240         if (!gd || !(gd->flags & GD_FLG_LOG_READY)) {
241                 if (gd)
242                         gd->log_drop_count++;
243                 return -ENOSYS;
244         }
245         log_dispatch(&rec);
246
247         return 0;
248 }
249
250 int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
251                    enum log_level_t max_level, const char *file_list)
252 {
253         struct log_filter *filt;
254         struct log_device *ldev;
255         int ret;
256         int i;
257
258         ldev = log_device_find_by_name(drv_name);
259         if (!ldev)
260                 return -ENOENT;
261         filt = calloc(1, sizeof(*filt));
262         if (!filt)
263                 return -ENOMEM;
264
265         if (cat_list) {
266                 filt->flags |= LOGFF_HAS_CAT;
267                 for (i = 0; ; i++) {
268                         if (i == ARRAY_SIZE(filt->cat_list)) {
269                                 ret = -ENOSPC;
270                                 goto err;
271                         }
272                         filt->cat_list[i] = cat_list[i];
273                         if (cat_list[i] == LOGC_END)
274                                 break;
275                 }
276         }
277         filt->max_level = max_level;
278         if (file_list) {
279                 filt->file_list = strdup(file_list);
280                 if (!filt->file_list) {
281                         ret = ENOMEM;
282                         goto err;
283                 }
284         }
285         filt->filter_num = ldev->next_filter_num++;
286         list_add_tail(&filt->sibling_node, &ldev->filter_head);
287
288         return filt->filter_num;
289
290 err:
291         free(filt);
292         return ret;
293 }
294
295 int log_remove_filter(const char *drv_name, int filter_num)
296 {
297         struct log_filter *filt;
298         struct log_device *ldev;
299
300         ldev = log_device_find_by_name(drv_name);
301         if (!ldev)
302                 return -ENOENT;
303
304         list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
305                 if (filt->filter_num == filter_num) {
306                         list_del(&filt->sibling_node);
307                         free(filt);
308
309                         return 0;
310                 }
311         }
312
313         return -ENOENT;
314 }
315
316 /**
317  * log_find_device_by_drv() - Find a device by its driver
318  *
319  * @drv: Log driver
320  * @return Device associated with that driver, or NULL if not found
321  */
322 static struct log_device *log_find_device_by_drv(struct log_driver *drv)
323 {
324         struct log_device *ldev;
325
326         list_for_each_entry(ldev, &gd->log_head, sibling_node) {
327                 if (ldev->drv == drv)
328                         return ldev;
329         }
330         /*
331          * It is quite hard to pass an invalid driver since passing an unknown
332          * LOG_GET_DRIVER(xxx) would normally produce a compilation error. But
333          * it is possible to pass NULL, for example, so this
334          */
335
336         return NULL;
337 }
338
339 int log_device_set_enable(struct log_driver *drv, bool enable)
340 {
341         struct log_device *ldev;
342
343         ldev = log_find_device_by_drv(drv);
344         if (!ldev)
345                 return -ENOENT;
346         if (enable)
347                 ldev->flags |= LOGDF_ENABLE;
348         else
349                 ldev->flags &= ~LOGDF_ENABLE;
350
351         return 0;
352 }
353
354 int log_init(void)
355 {
356         struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
357         const int count = ll_entry_count(struct log_driver, log_driver);
358         struct log_driver *end = drv + count;
359
360         /*
361          * We cannot add runtime data to the driver since it is likely stored
362          * in rodata. Instead, set up a 'device' corresponding to each driver.
363          * We only support having a single device.
364          */
365         INIT_LIST_HEAD((struct list_head *)&gd->log_head);
366         while (drv < end) {
367                 struct log_device *ldev;
368
369                 ldev = calloc(1, sizeof(*ldev));
370                 if (!ldev) {
371                         debug("%s: Cannot allocate memory\n", __func__);
372                         return -ENOMEM;
373                 }
374                 INIT_LIST_HEAD(&ldev->filter_head);
375                 ldev->drv = drv;
376                 ldev->flags = drv->flags;
377                 list_add_tail(&ldev->sibling_node,
378                               (struct list_head *)&gd->log_head);
379                 drv++;
380         }
381         gd->flags |= GD_FLG_LOG_READY;
382         if (!gd->default_log_level)
383                 gd->default_log_level = CONFIG_LOG_DEFAULT_LEVEL;
384         gd->log_fmt = log_get_default_format();
385
386         return 0;
387 }