diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Kconfig | 86 | ||||
-rw-r--r-- | common/Makefile | 2 | ||||
-rw-r--r-- | common/board_f.c | 23 | ||||
-rw-r--r-- | common/board_r.c | 27 | ||||
-rw-r--r-- | common/console.c | 7 | ||||
-rw-r--r-- | common/image.c | 9 | ||||
-rw-r--r-- | common/log.c | 245 | ||||
-rw-r--r-- | common/log_console.c | 23 | ||||
-rw-r--r-- | common/stdio.c | 6 | ||||
-rw-r--r-- | common/usb_hub.c | 4 |
10 files changed, 372 insertions, 60 deletions
diff --git a/common/Kconfig b/common/Kconfig index c50d6ebb2ad..4da095a4fd7 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -420,6 +420,92 @@ config SYS_STDIO_DEREGISTER endmenu +menu "Logging" + +config LOG + bool "Enable logging support" + help + This enables support for logging of status and debug messages. These + can be displayed on the console, recorded in a memory buffer, or + discarded if not needed. Logging supports various categories and + levels of severity. + +config SPL_LOG + bool "Enable logging support in SPL" + help + This enables support for logging of status and debug messages. These + can be displayed on the console, recorded in a memory buffer, or + discarded if not needed. Logging supports various categories and + levels of severity. + +config LOG_MAX_LEVEL + int "Maximum log level to record" + depends on LOG + default 5 + help + This selects the maximum log level that will be recorded. Any value + higher than this will be ignored. If possible log statements below + this level will be discarded at build time. Levels: + + 0 - panic + 1 - critical + 2 - error + 3 - warning + 4 - note + 5 - info + 6 - detail + 7 - debug + +config SPL_LOG_MAX_LEVEL + int "Maximum log level to record in SPL" + depends on SPL_LOG + default 3 + help + This selects the maximum log level that will be recorded. Any value + higher than this will be ignored. If possible log statements below + this level will be discarded at build time. Levels: + + 0 - panic + 1 - critical + 2 - error + 3 - warning + 4 - note + 5 - info + 6 - detail + 7 - debug + +config LOG_CONSOLE + bool "Allow log output to the console" + depends on LOG + default y + help + Enables a log driver which writes log records to the console. + Generally the console is the serial port or LCD display. Only the + log message is shown - other details like level, category, file and + line number are omitted. + +config LOG_SPL_CONSOLE + bool "Allow log output to the console in SPL" + depends on LOG_SPL + default y + help + Enables a log driver which writes log records to the console. + Generally the console is the serial port or LCD display. Only the + log message is shown - other details like level, category, file and + line number are omitted. + +config LOG_TEST + bool "Provide a test for logging" + depends on LOG + default y if SANDBOX + help + This enables a 'log test' command to test logging. It is normally + executed from a pytest and simply outputs logging information + in various different ways to test that the logging system works + correctly with varoius settings. + +endmenu + config DEFAULT_FDT_FILE string "Default fdt file" help diff --git a/common/Makefile b/common/Makefile index cec506fe3e1..14166209fe4 100644 --- a/common/Makefile +++ b/common/Makefile @@ -128,5 +128,7 @@ obj-y += cli.o obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o obj-$(CONFIG_CMD_DFU) += dfu.o obj-y += command.o +obj-$(CONFIG_$(SPL_)LOG) += log.o +obj-$(CONFIG_$(SPL_)LOG_CONSOLE) += log_console.o obj-y += s_record.o obj-y += xyzModem.o diff --git a/common/board_f.c b/common/board_f.c index 9220815441e..e46eceda7d0 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -19,7 +19,6 @@ #include <i2c.h> #include <initcall.h> #include <init_helpers.h> -#include <logbuff.h> #include <malloc.h> #include <mapmem.h> #include <os.h> @@ -296,20 +295,6 @@ static int setup_dest_addr(void) return 0; } -#if defined(CONFIG_LOGBUFFER) -static int reserve_logbuffer(void) -{ -#ifndef CONFIG_ALT_LB_ADDR - /* reserve kernel log buffer */ - gd->relocaddr -= LOGBUFF_RESERVE; - debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, - gd->relocaddr); -#endif - - return 0; -} -#endif - #ifdef CONFIG_PRAM /* reserve protected RAM */ static int reserve_pram(void) @@ -766,6 +751,7 @@ static const init_fnc_t init_sequence_f[] = { trace_early_init, #endif initf_malloc, + log_init, initf_bootstage, /* uses its own timer, so does not need DM */ initf_console_record, #if defined(CONFIG_HAVE_FSP) @@ -846,9 +832,6 @@ static const init_fnc_t init_sequence_f[] = { * - board info struct */ setup_dest_addr, -#if defined(CONFIG_LOGBUFFER) - reserve_logbuffer, -#endif #ifdef CONFIG_PRAM reserve_pram, #endif @@ -950,8 +933,10 @@ void board_init_f_r(void) * The pre-relocation drivers may be using memory that has now gone * away. Mark serial as unavailable - this will fall back to the debug * UART if available. + * + * Do the same with log drivers since the memory may not be available. */ - gd->flags &= ~GD_FLG_SERIAL_READY; + gd->flags &= ~(GD_FLG_SERIAL_READY | GD_FLG_LOG_READY); #ifdef CONFIG_TIMER gd->timer = NULL; #endif diff --git a/common/board_r.c b/common/board_r.c index a3b9bfb8ee4..09167c13cc8 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -30,7 +30,6 @@ #if defined(CONFIG_CMD_KGDB) #include <kgdb.h> #endif -#include <logbuff.h> #include <malloc.h> #include <mapmem.h> #ifdef CONFIG_BITBANGMII @@ -200,19 +199,6 @@ static int initr_addr_map(void) } #endif -#ifdef CONFIG_LOGBUFFER -unsigned long logbuffer_base(void) -{ - return gd->ram_top - LOGBUFF_LEN; -} - -static int initr_logbuffer(void) -{ - logbuff_init_ptrs(); - return 0; -} -#endif - #ifdef CONFIG_POST static int initr_post_backlog(void) { @@ -628,7 +614,7 @@ static int initr_ide(void) } #endif -#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) +#if defined(CONFIG_PRAM) /* * Export available size of memory for Linux, taking into account the * protected RAM at top of memory @@ -641,10 +627,6 @@ int initr_mem(void) # ifdef CONFIG_PRAM pram = env_get_ulong("pram", 10, CONFIG_PRAM); # endif -# if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR) - /* Also take the logbuffer into account (pram is in kB) */ - pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024; -# endif sprintf(memsz, "%ldk", (long int) ((gd->ram_size / 1024) - pram)); env_set("mem", memsz); @@ -709,6 +691,7 @@ static init_fnc_t init_sequence_r[] = { #endif initr_barrier, initr_malloc, + log_init, initr_bootstage, /* Needs malloc() but has its own timer */ initr_console_record, #ifdef CONFIG_SYS_NONCACHED_MEMORY @@ -753,9 +736,6 @@ static init_fnc_t init_sequence_r[] = { board_early_init_r, #endif INIT_FUNC_WATCHDOG_RESET -#ifdef CONFIG_LOGBUFFER - initr_logbuffer, -#endif #ifdef CONFIG_POST initr_post_backlog, #endif @@ -877,7 +857,7 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_bedbug, #endif -#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) +#if defined(CONFIG_PRAM) initr_mem, #endif #ifdef CONFIG_PS2KBD @@ -905,6 +885,7 @@ void board_init_r(gd_t *new_gd, ulong dest_addr) #if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) gd = new_gd; #endif + gd->flags &= ~GD_FLG_LOG_READY; #ifdef CONFIG_NEEDS_MANUAL_RELOC for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++) diff --git a/common/console.c b/common/console.c index d763f2c6846..0e0295514b2 100644 --- a/common/console.c +++ b/common/console.c @@ -489,6 +489,13 @@ static inline void print_pre_console_buffer(int flushpoint) {} void putc(const char c) { +#ifdef CONFIG_SANDBOX + /* sandbox can send characters to stdout before it has a console */ + if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { + os_putc(c); + return; + } +#endif #ifdef CONFIG_DEBUG_UART /* if we don't have a console yet, use the debug UART */ if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { diff --git a/common/image.c b/common/image.c index 4ec4744589f..4bcf6b3128a 100644 --- a/common/image.c +++ b/common/image.c @@ -15,10 +15,6 @@ #include <status_led.h> #endif -#ifdef CONFIG_LOGBUFFER -#include <logbuff.h> -#endif - #include <rtc.h> #include <environment.h> @@ -1154,11 +1150,6 @@ int boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len, } -#ifdef CONFIG_LOGBUFFER - /* Prevent initrd from overwriting logbuffer */ - lmb_reserve(lmb, logbuffer_base() - LOGBUFF_OVERHEAD, LOGBUFF_RESERVE); -#endif - debug("## initrd_high = 0x%08lx, copy_to_ram = %d\n", initrd_high, initrd_copy_to_ram); diff --git a/common/log.c b/common/log.c new file mode 100644 index 00000000000..45e46dd5209 --- /dev/null +++ b/common/log.c @@ -0,0 +1,245 @@ +/* + * Logging support + * + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct log_device *log_device_find_by_name(const char *drv_name) +{ + struct log_device *ldev; + + list_for_each_entry(ldev, &gd->log_head, sibling_node) { + if (!strcmp(drv_name, ldev->drv->name)) + return ldev; + } + + return NULL; +} + +/** + * log_has_cat() - check if a log category exists within a list + * + * @cat_list: List of categories to check, at most LOGF_MAX_CATEGORIES entries + * long, terminated by LC_END if fewer + * @cat: Category to search for + * @return true if @cat is in @cat_list, else false + */ +static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat) +{ + int i; + + for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) { + if (cat_list[i] == cat) + return true; + } + + return false; +} + +/** + * log_has_file() - check if a file is with a list + * + * @file_list: List of files to check, separated by comma + * @file: File to check for. This string is matched against the end of each + * file in the list, i.e. ignoring any preceding path. The list is + * intended to consist of relative pathnames, e.g. common/main.c,cmd/log.c + * @return true if @file is in @file_list, else false + */ +static bool log_has_file(const char *file_list, const char *file) +{ + int file_len = strlen(file); + const char *s, *p; + int substr_len; + + for (s = file_list; *s; s = p + (*p != '\0')) { + p = strchrnul(s, ','); + substr_len = p - s; + if (file_len >= substr_len && + !strncmp(file + file_len - substr_len, s, substr_len)) + return true; + } + + return false; +} + +/** + * log_passes_filters() - check if a log record passes the filters for a device + * + * @ldev: Log device to check + * @rec: Log record to check + * @return true if @rec is not blocked by the filters in @ldev, false if it is + */ +static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec) +{ + struct log_filter *filt; + + /* If there are no filters, filter on the default log level */ + if (list_empty(&ldev->filter_head)) { + if (rec->level > gd->default_log_level) + return false; + return true; + } + + list_for_each_entry(filt, &ldev->filter_head, sibling_node) { + if (rec->level > filt->max_level) + continue; + if ((filt->flags & LOGFF_HAS_CAT) && + !log_has_cat(filt->cat_list, rec->cat)) + continue; + if (filt->file_list && + !log_has_file(filt->file_list, rec->file)) + continue; + return true; + } + + return false; +} + +/** + * log_dispatch() - Send a log record to all log devices for processing + * + * The log record is sent to each log device in turn, skipping those which have + * filters which block the record + * + * @rec: Log record to dispatch + * @return 0 (meaning success) + */ +static int log_dispatch(struct log_rec *rec) +{ + struct log_device *ldev; + + list_for_each_entry(ldev, &gd->log_head, sibling_node) { + if (log_passes_filters(ldev, rec)) + ldev->drv->emit(ldev, rec); + } + + return 0; +} + +int _log(enum log_category_t cat, enum log_level_t level, const char *file, + int line, const char *func, const char *fmt, ...) +{ + char buf[CONFIG_SYS_CBSIZE]; + struct log_rec rec; + va_list args; + + rec.cat = cat; + rec.level = level; + rec.file = file; + rec.line = line; + rec.func = func; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + rec.msg = buf; + if (!gd || !(gd->flags & GD_FLG_LOG_READY)) { + if (gd) + gd->log_drop_count++; + return -ENOSYS; + } + log_dispatch(&rec); + + return 0; +} + +int log_add_filter(const char *drv_name, enum log_category_t cat_list[], + enum log_level_t max_level, const char *file_list) +{ + struct log_filter *filt; + struct log_device *ldev; + int i; + + ldev = log_device_find_by_name(drv_name); + if (!ldev) + return -ENOENT; + filt = (struct log_filter *)calloc(1, sizeof(*filt)); + if (!filt) + return -ENOMEM; + + if (cat_list) { + filt->flags |= LOGFF_HAS_CAT; + for (i = 0; ; i++) { + if (i == ARRAY_SIZE(filt->cat_list)) + return -ENOSPC; + filt->cat_list[i] = cat_list[i]; + if (cat_list[i] == LOGC_END) + break; + } + } + filt->max_level = max_level; + if (file_list) { + filt->file_list = strdup(file_list); + if (!filt->file_list) + goto nomem; + } + filt->filter_num = ldev->next_filter_num++; + list_add_tail(&filt->sibling_node, &ldev->filter_head); + + return filt->filter_num; + +nomem: + free(filt); + return -ENOMEM; +} + +int log_remove_filter(const char *drv_name, int filter_num) +{ + struct log_filter *filt; + struct log_device *ldev; + + ldev = log_device_find_by_name(drv_name); + if (!ldev) + return -ENOENT; + + list_for_each_entry(filt, &ldev->filter_head, sibling_node) { + if (filt->filter_num == filter_num) { + list_del(&filt->sibling_node); + free(filt); + + return 0; + } + } + + return -ENOENT; +} + +int log_init(void) +{ + struct log_driver *drv = ll_entry_start(struct log_driver, log_driver); + const int count = ll_entry_count(struct log_driver, log_driver); + struct log_driver *end = drv + count; + + /* + * We cannot add runtime data to the driver since it is likely stored + * in rodata. Instead, set up a 'device' corresponding to each driver. + * We only support having a single device. + */ + INIT_LIST_HEAD((struct list_head *)&gd->log_head); + while (drv < end) { + struct log_device *ldev; + + ldev = calloc(1, sizeof(*ldev)); + if (!ldev) { + debug("%s: Cannot allocate memory\n", __func__); + return -ENOMEM; + } + INIT_LIST_HEAD(&ldev->filter_head); + ldev->drv = drv; + list_add_tail(&ldev->sibling_node, + (struct list_head *)&gd->log_head); + drv++; + } + gd->flags |= GD_FLG_LOG_READY; + gd->default_log_level = LOGL_INFO; + + return 0; +} diff --git a/common/log_console.c b/common/log_console.c new file mode 100644 index 00000000000..5af73bd8be4 --- /dev/null +++ b/common/log_console.c @@ -0,0 +1,23 @@ +/* + * Logging support + * + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <log.h> + +static int log_console_emit(struct log_device *ldev, struct log_rec *rec) +{ + puts(rec->msg); + + return 0; +} + +LOG_DRIVER(console) = { + .name = "console", + .emit = log_console_emit, +}; diff --git a/common/stdio.c b/common/stdio.c index ee4f0bda9ea..2e5143a0255 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -17,9 +17,6 @@ #include <malloc.h> #include <stdio_dev.h> #include <serial.h> -#ifdef CONFIG_LOGBUFFER -#include <logbuff.h> -#endif #if defined(CONFIG_SYS_I2C) #include <i2c.h> @@ -381,9 +378,6 @@ int stdio_add_devices(void) #if defined(CONFIG_KEYBOARD) && !defined(CONFIG_DM_KEYBOARD) drv_keyboard_init (); #endif -#ifdef CONFIG_LOGBUFFER - drv_logbuff_init (); -#endif drv_system_init (); serial_stdio_init (); #ifdef CONFIG_USB_TTY diff --git a/common/usb_hub.c b/common/usb_hub.c index 024dadb2774..b46dfa16ccf 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -625,7 +625,7 @@ static int usb_hub_configure(struct usb_device *dev) short hubCharacteristics; struct usb_hub_descriptor *descriptor; struct usb_hub_device *hub; - __maybe_unused struct usb_hub_status *hubsts; + struct usb_hub_status *hubsts; int ret; hub = usb_get_hub_device(dev); @@ -779,9 +779,7 @@ static int usb_hub_configure(struct usb_device *dev) return ret; } -#ifdef DEBUG hubsts = (struct usb_hub_status *)buffer; -#endif debug("get_hub_status returned status %X, change %X\n", le16_to_cpu(hubsts->wHubStatus), |