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