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