aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-10-30 14:17:23 -0400
committerTom Rini <trini@konsulko.com>2020-10-30 14:17:23 -0400
commit7820052fdeb17f159037879fc1b51b0fd704c180 (patch)
tree67d7bdff892fae5f81ead9fa0487e406b2ca55e9
parent8d7f3fcb4a20bade1a940cea3e3b6983587d0fc9 (diff)
parent8ba0f89abe6f60802da7ff5f630f16a9fbc27df2 (diff)
downloadu-boot-7820052fdeb17f159037879fc1b51b0fd704c180.tar.gz
Merge branch '2020-10-30-misc-changes'
- Additional log improvements - SPI flash environment improvements
-rw-r--r--MAINTAINERS1
-rw-r--r--cmd/Kconfig1
-rw-r--r--cmd/log.c352
-rw-r--r--common/log.c57
-rw-r--r--configs/aristainetos2_defconfig1
-rw-r--r--configs/aristainetos2b_defconfig1
-rw-r--r--configs/aristainetos2bcsl_defconfig1
-rw-r--r--configs/aristainetos2c_defconfig1
-rw-r--r--doc/api/getopt.rst8
-rw-r--r--doc/api/index.rst1
-rw-r--r--doc/develop/logging.rst224
-rw-r--r--env/Kconfig8
-rw-r--r--env/common.c42
-rw-r--r--env/sf.c100
-rw-r--r--include/env.h18
-rw-r--r--include/getopt.h130
-rw-r--r--include/log.h220
-rw-r--r--include/test/log.h3
-rw-r--r--lib/Kconfig5
-rw-r--r--lib/Makefile1
-rw-r--r--lib/getopt.c125
-rw-r--r--test/lib/Makefile1
-rw-r--r--test/lib/getopt.c123
-rw-r--r--test/log/Makefile1
-rw-r--r--test/log/log_filter.c108
-rw-r--r--test/log/log_test.c530
-rw-r--r--test/log/syslog_test.h2
-rw-r--r--test/py/tests/test_log.py104
28 files changed, 1626 insertions, 543 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 1505e74559..874cf2c0e5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -758,6 +758,7 @@ T: git https://gitlab.denx.de/u-boot/u-boot.git
F: common/log*
F: cmd/log.c
F: doc/develop/logging.rst
+F: lib/getopt.c
F: test/log/
F: test/py/tests/test_log.py
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 9f36290796..1595de999b 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2239,6 +2239,7 @@ config CMD_KGDB
config CMD_LOG
bool "log - Generation, control and access to logging"
select LOG
+ select GETOPT
help
This provides access to logging features. It allows the output of
log data to be controlled to a limited extent (setting up the default
diff --git a/cmd/log.c b/cmd/log.c
index 6afe6ead25..ca1c51e067 100644
--- a/cmd/log.c
+++ b/cmd/log.c
@@ -7,27 +7,298 @@
#include <common.h>
#include <command.h>
#include <dm.h>
+#include <getopt.h>
#include <log.h>
+#include <malloc.h>
static char log_fmt_chars[LOGF_COUNT] = "clFLfm";
+static enum log_level_t parse_log_level(char *const arg)
+{
+ enum log_level_t ret;
+ ulong level;
+
+ if (!strict_strtoul(arg, 10, &level)) {
+ if (level > _LOG_MAX_LEVEL) {
+ printf("Only log levels <= %d are supported\n",
+ _LOG_MAX_LEVEL);
+ return LOGL_NONE;
+ }
+ return level;
+ }
+
+ ret = log_get_level_by_name(arg);
+ if (ret == LOGL_NONE)
+ printf("Unknown log level \"%s\"\n", arg);
+ return ret;
+}
+
static int do_log_level(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
+ enum log_level_t log_level;
+
if (argc > 1) {
- long log_level = simple_strtol(argv[1], NULL, 10);
+ log_level = parse_log_level(argv[1]);
- if (log_level < 0 || log_level > _LOG_MAX_LEVEL) {
- printf("Only log levels <= %d are supported\n",
- _LOG_MAX_LEVEL);
+ if (log_level == LOGL_NONE)
return CMD_RET_FAILURE;
- }
gd->default_log_level = log_level;
} else {
- printf("Default log level: %d\n", gd->default_log_level);
+ for (log_level = LOGL_FIRST; log_level <= _LOG_MAX_LEVEL;
+ log_level++)
+ printf("%s%s\n", log_get_level_name(log_level),
+ log_level == gd->default_log_level ?
+ " (default)" : "");
}
- return 0;
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_categories(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum log_category_t cat;
+ const char *name;
+
+ for (cat = LOGC_FIRST; cat < LOGC_COUNT; cat++) {
+ name = log_get_cat_name(cat);
+ /*
+ * Invalid category names (e.g. <invalid> or <missing>) begin
+ * with '<'.
+ */
+ if (name[0] == '<')
+ continue;
+ printf("%s\n", name);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_drivers(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct log_device *ldev;
+
+ list_for_each_entry(ldev, &gd->log_head, sibling_node)
+ printf("%s\n", ldev->drv->name);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_filter_list(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int opt;
+ const char *drv_name = "console";
+ struct getopt_state gs;
+ struct log_filter *filt;
+ struct log_device *ldev;
+
+ getopt_init_state(&gs);
+ while ((opt = getopt(&gs, argc, argv, "d:")) > 0) {
+ switch (opt) {
+ case 'd':
+ drv_name = gs.arg;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if (gs.index != argc)
+ return CMD_RET_USAGE;
+
+ ldev = log_device_find_by_name(drv_name);
+ if (!ldev) {
+ printf("Could not find log device for \"%s\"\n", drv_name);
+ return CMD_RET_FAILURE;
+ }
+
+ /* <3> < 6 > <2+1 + 7 > < 16 > < unbounded... */
+ printf("num policy level categories files\n");
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
+ printf("%3d %6.6s %s %-7.7s ", filt->filter_num,
+ filt->flags & LOGFF_DENY ? "deny" : "allow",
+ filt->flags & LOGFF_LEVEL_MIN ? ">=" : "<=",
+ log_get_level_name(filt->level));
+
+ if (filt->flags & LOGFF_HAS_CAT) {
+ int i;
+
+ if (filt->cat_list[0] != LOGC_END)
+ printf("%16.16s %s\n",
+ log_get_cat_name(filt->cat_list[0]),
+ filt->file_list ? filt->file_list : "");
+
+ for (i = 1; i < LOGF_MAX_CATEGORIES &&
+ filt->cat_list[i] != LOGC_END; i++)
+ printf("%21c %16.16s\n", ' ',
+ log_get_cat_name(filt->cat_list[i]));
+ } else {
+ printf("%16c %s\n", ' ',
+ filt->file_list ? filt->file_list : "");
+ }
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_filter_add(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool level_set = false;
+ bool print_num = false;
+ bool type_set = false;
+ char *file_list = NULL;
+ const char *drv_name = "console";
+ int opt, err;
+ int cat_count = 0;
+ int flags = 0;
+ enum log_category_t cat_list[LOGF_MAX_CATEGORIES + 1];
+ enum log_level_t level = LOGL_MAX;
+ struct getopt_state gs;
+
+ getopt_init_state(&gs);
+ while ((opt = getopt(&gs, argc, argv, "Ac:d:Df:l:L:p")) > 0) {
+ switch (opt) {
+ case 'A':
+#define do_type() do { \
+ if (type_set) { \
+ printf("Allow or deny set twice\n"); \
+ return CMD_RET_USAGE; \
+ } \
+ type_set = true; \
+} while (0)
+ do_type();
+ break;
+ case 'c': {
+ enum log_category_t cat;
+
+ if (cat_count >= LOGF_MAX_CATEGORIES) {
+ printf("Too many categories\n");
+ return CMD_RET_FAILURE;
+ }
+
+ cat = log_get_cat_by_name(gs.arg);
+ if (cat == LOGC_NONE) {
+ printf("Unknown category \"%s\"\n", gs.arg);
+ return CMD_RET_FAILURE;
+ }
+
+ cat_list[cat_count++] = cat;
+ break;
+ }
+ case 'd':
+ drv_name = gs.arg;
+ break;
+ case 'D':
+ do_type();
+ flags |= LOGFF_DENY;
+ break;
+ case 'f':
+ file_list = gs.arg;
+ break;
+ case 'l':
+#define do_level() do { \
+ if (level_set) { \
+ printf("Log level set twice\n"); \
+ return CMD_RET_USAGE; \
+ } \
+ level = parse_log_level(gs.arg); \
+ if (level == LOGL_NONE) \
+ return CMD_RET_FAILURE; \
+ level_set = true; \
+} while (0)
+ do_level();
+ break;
+ case 'L':
+ do_level();
+ flags |= LOGFF_LEVEL_MIN;
+ break;
+ case 'p':
+ print_num = true;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if (gs.index != argc)
+ return CMD_RET_USAGE;
+
+ cat_list[cat_count] = LOGC_END;
+ err = log_add_filter_flags(drv_name, cat_count ? cat_list : NULL, level,
+ file_list, flags);
+ if (err < 0) {
+ printf("Could not add filter (err = %d)\n", err);
+ return CMD_RET_FAILURE;
+ } else if (print_num) {
+ printf("%d\n", err);
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_log_filter_remove(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool all = false;
+ int opt, err;
+ ulong filter_num;
+ const char *drv_name = "console";
+ struct getopt_state gs;
+
+ getopt_init_state(&gs);
+ while ((opt = getopt(&gs, argc, argv, "ad:")) > 0) {
+ switch (opt) {
+ case 'a':
+ all = true;
+ break;
+ case 'd':
+ drv_name = gs.arg;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+ }
+
+ if (all) {
+ struct log_filter *filt, *tmp_filt;
+ struct log_device *ldev;
+
+ if (gs.index != argc)
+ return CMD_RET_USAGE;
+
+ ldev = log_device_find_by_name(drv_name);
+ if (!ldev) {
+ printf("Could not find log device for \"%s\"\n",
+ drv_name);
+ return CMD_RET_FAILURE;
+ }
+
+ list_for_each_entry_safe(filt, tmp_filt, &ldev->filter_head,
+ sibling_node) {
+ list_del(&filt->sibling_node);
+ free(filt);
+ }
+ } else {
+ if (gs.index + 1 != argc)
+ return CMD_RET_USAGE;
+
+ if (strict_strtoul(argv[gs.index], 10, &filter_num)) {
+ printf("Invalid filter number \"%s\"\n", argv[gs.index]);
+ return CMD_RET_FAILURE;
+ }
+
+ err = log_remove_filter(drv_name, filter_num);
+ if (err) {
+ printf("Could not remove filter (err = %d)\n", err);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ return CMD_RET_SUCCESS;
}
static int do_log_format(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -103,39 +374,31 @@ static int do_log_rec(struct cmd_tbl *cmdtp, int flag, int argc,
return 0;
}
-static struct cmd_tbl log_sub[] = {
- U_BOOT_CMD_MKENT(level, CONFIG_SYS_MAXARGS, 1, do_log_level, "", ""),
-#ifdef CONFIG_LOG_TEST
- U_BOOT_CMD_MKENT(test, 2, 1, do_log_test, "", ""),
-#endif
- U_BOOT_CMD_MKENT(format, CONFIG_SYS_MAXARGS, 1, do_log_format, "", ""),
- U_BOOT_CMD_MKENT(rec, CONFIG_SYS_MAXARGS, 1, do_log_rec, "", ""),
-};
-
-static int do_log(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
-{
- struct cmd_tbl *cp;
-
- if (argc < 2)
- return CMD_RET_USAGE;
-
- /* drop initial "log" arg */
- argc--;
- argv++;
-
- cp = find_cmd_tbl(argv[0], log_sub, ARRAY_SIZE(log_sub));
- if (cp)
- return cp->cmd(cmdtp, flag, argc, argv);
-
- return CMD_RET_USAGE;
-}
-
#ifdef CONFIG_SYS_LONGHELP
static char log_help_text[] =
- "level - get/set log level\n"
-#ifdef CONFIG_LOG_TEST
- "log test - run log tests\n"
-#endif
+ "level [<level>] - get/set log level\n"
+ "categories - list log categories\n"
+ "drivers - list log drivers\n"
+ "log filter-list [OPTIONS] - list all filters for a log driver\n"
+ "\t-d <driver> - Specify the log driver to list filters from; defaults\n"
+ "\t to console\n"
+ "log filter-add [OPTIONS] - add a new filter to a driver\n"
+ "\t-A - Allow messages matching this filter; mutually exclusive with -D\n"
+ "\t This is the default.\n"
+ "\t-c <category> - Category to match; may be specified multiple times\n"
+ "\t-d <driver> - Specify the log driver to add the filter to; defaults\n"
+ "\t to console\n"
+ "\t-D - Deny messages matching this filter; mutually exclusive with -A\n"
+ "\t-f <files_list> - A comma-separated list of files to match\n"
+ "\t-l <level> - Match log levels less than or equal to <level>;\n"
+ "\t mutually-exclusive with -L\n"
+ "\t-L <level> - Match log levels greather than or equal to <level>;\n"
+ "\t mutually-exclusive with -l\n"
+ "\t-p - Print the filter number on success\n"
+ "log filter-remove [OPTIONS] [<num>] - Remove filter number <num>\n"
+ "\t-a - Remove ALL filters\n"
+ "\t-d <driver> - Specify the log driver to remove the filter from;\n"
+ "\t defaults to console\n"
"log format <fmt> - set log output format. <fmt> is a string where\n"
"\teach letter indicates something that should be displayed:\n"
"\tc=category, l=level, F=file, L=line number, f=function, m=msg\n"
@@ -145,7 +408,14 @@ static char log_help_text[] =
;
#endif
-U_BOOT_CMD(
- log, CONFIG_SYS_MAXARGS, 1, do_log,
- "log system", log_help_text
+U_BOOT_CMD_WITH_SUBCMDS(log, "log system", log_help_text,
+ U_BOOT_SUBCMD_MKENT(level, 2, 1, do_log_level),
+ U_BOOT_SUBCMD_MKENT(categories, 1, 1, do_log_categories),
+ U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_log_drivers),
+ U_BOOT_SUBCMD_MKENT(filter-list, 3, 1, do_log_filter_list),
+ U_BOOT_SUBCMD_MKENT(filter-add, CONFIG_SYS_MAXARGS, 1,
+ do_log_filter_add),
+ U_BOOT_SUBCMD_MKENT(filter-remove, 4, 1, do_log_filter_remove),
+ U_BOOT_SUBCMD_MKENT(format, 2, 1, do_log_format),
+ U_BOOT_SUBCMD_MKENT(rec, 7, 1, do_log_rec),
);
diff --git a/common/log.c b/common/log.c
index 9f98e9aff8..4b6f3fcd04 100644
--- a/common/log.c
+++ b/common/log.c
@@ -13,7 +13,7 @@
DECLARE_GLOBAL_DATA_PTR;
-static const char *log_cat_name[] = {
+static const char *const log_cat_name[] = {
"none",
"arch",
"board",
@@ -31,7 +31,7 @@ static const char *log_cat_name[] = {
_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE,
"log_cat_name size");
-static const char *log_level_name[] = {
+static const char *const log_level_name[] = {
"EMERG",
"ALERT",
"CRIT",
@@ -99,7 +99,7 @@ enum log_level_t log_get_level_by_name(const char *name)
return LOGL_NONE;
}
-static struct log_device *log_device_find_by_name(const char *drv_name)
+struct log_device *log_device_find_by_name(const char *drv_name)
{
struct log_device *ldev;
@@ -111,15 +111,7 @@ static struct log_device *log_device_find_by_name(const char *drv_name)
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)
+bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
{
int i;
@@ -131,16 +123,7 @@ static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
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)
+bool log_has_file(const char *file_list, const char *file)
{
int file_len = strlen(file);
const char *s, *p;
@@ -179,15 +162,25 @@ static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
}
list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
- if (rec->level > filt->max_level)
+ if (filt->flags & LOGFF_LEVEL_MIN) {
+ if (rec->level < filt->level)
+ continue;
+ } else if (rec->level > filt->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;
+
+ if (filt->flags & LOGFF_DENY)
+ return false;
+ else
+ return true;
}
return false;
@@ -263,8 +256,9 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file,
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)
+int log_add_filter_flags(const char *drv_name, enum log_category_t cat_list[],
+ enum log_level_t level, const char *file_list,
+ int flags)
{
struct log_filter *filt;
struct log_device *ldev;
@@ -278,6 +272,7 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
if (!filt)
return -ENOMEM;
+ filt->flags = flags;
if (cat_list) {
filt->flags |= LOGFF_HAS_CAT;
for (i = 0; ; i++) {
@@ -290,16 +285,20 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
break;
}
}
- filt->max_level = max_level;
+ filt->level = level;
if (file_list) {
filt->file_list = strdup(file_list);
if (!filt->file_list) {
- ret = ENOMEM;
+ ret = -ENOMEM;
goto err;
}
}
filt->filter_num = ldev->next_filter_num++;
- list_add_tail(&filt->sibling_node, &ldev->filter_head);
+ /* Add deny filters to the beginning of the list */
+ if (flags & LOGFF_DENY)
+ list_add(&filt->sibling_node, &ldev->filter_head);
+ else
+ list_add_tail(&filt->sibling_node, &ldev->filter_head);
return filt->filter_num;
diff --git a/configs/aristainetos2_defconfig b/configs/aristainetos2_defconfig
index 81863f601d..35e4b09715 100644
--- a/configs/aristainetos2_defconfig
+++ b/configs/aristainetos2_defconfig
@@ -58,6 +58,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y
diff --git a/configs/aristainetos2b_defconfig b/configs/aristainetos2b_defconfig
index 2e59c3014e..d47a074645 100644
--- a/configs/aristainetos2b_defconfig
+++ b/configs/aristainetos2b_defconfig
@@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y
diff --git a/configs/aristainetos2bcsl_defconfig b/configs/aristainetos2bcsl_defconfig
index 8e6d2994fb..30139621d8 100644
--- a/configs/aristainetos2bcsl_defconfig
+++ b/configs/aristainetos2bcsl_defconfig
@@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y
diff --git a/configs/aristainetos2c_defconfig b/configs/aristainetos2c_defconfig
index 372d70567a..50cadb749a 100644
--- a/configs/aristainetos2c_defconfig
+++ b/configs/aristainetos2c_defconfig
@@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y
diff --git a/doc/api/getopt.rst b/doc/api/getopt.rst
new file mode 100644
index 0000000000..773f79aeb6
--- /dev/null
+++ b/doc/api/getopt.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+
+Option Parsing
+==============
+
+.. kernel-doc:: include/getopt.h
+ :internal:
diff --git a/doc/api/index.rst b/doc/api/index.rst
index 787b6778e5..ae4a1b6c63 100644
--- a/doc/api/index.rst
+++ b/doc/api/index.rst
@@ -8,6 +8,7 @@ U-Boot API documentation
dfu
efi
+ getopt
linker_lists
pinctrl
rng
diff --git a/doc/develop/logging.rst b/doc/develop/logging.rst
index 528280c3e8..7fdd1132ef 100644
--- a/doc/develop/logging.rst
+++ b/doc/develop/logging.rst
@@ -21,26 +21,13 @@ is visible from the basic console output.
U-Boot's logging feature aims to satisfy this goal for both users and
developers.
-
Logging levels
--------------
-There are a number logging levels available, in increasing order of verbosity:
-
-* LOGL_EMERG - Printed before U-Boot halts
-* LOGL_ALERT - Indicates action must be taken immediate or U-Boot will crash
-* LOGL_CRIT - Indicates a critical error that will cause boot failure
-* LOGL_ERR - Indicates an error that may cause boot failure
-* LOGL_WARNING - Warning about an unexpected condition
-* LOGL_NOTE - Important information about progress
-* LOGL_INFO - Information about normal boot progress
-* LOGL_DEBUG - Debug information (useful for debugging a driver or subsystem)
-* LOGL_DEBUG_CONTENT - Debug message showing full message content
-* LOGL_DEBUG_IO - Debug message showing hardware I/O access
+There are a number logging levels available.
-To continue a log message in a separate call of function log() use
-
-* LOGL_CONT - Use same log level as in previous call
+.. kernel-doc:: include/log.h
+ :identifiers: log_level_t
Logging category
----------------
@@ -49,19 +36,8 @@ Logging can come from a wide variety of places within U-Boot. Each log message
has a category which is intended to allow messages to be filtered according to
their source.
-The following main categories are defined:
-
-* LOGC_NONE - Unknown category (e.g. a debug() statement)
-* UCLASS\_... - Related to a particular uclass (e.g. UCLASS_USB)
-* LOGC_ARCH - Related to architecture-specific code
-* LOGC_BOARD - Related to board-specific code
-* LOGC_CORE - Related to core driver-model support
-* LOGC_DT - Related to device tree control
-* LOGC_EFI - Related to EFI implementation
-
-To continue a log message in a separate call of function log() use
-
-* LOGC_CONT - Use same category as in previous call
+.. kernel-doc:: include/log.h
+ :identifiers: log_category_t
Enabling logging
----------------
@@ -78,7 +54,6 @@ If CONFIG_LOG is not set, then no logging will be available.
The above have SPL and TPL versions also, e.g. CONFIG_SPL_LOG_MAX_LEVEL and
CONFIG_TPL_LOG_MAX_LEVEL.
-
Temporary logging within a single file
--------------------------------------
@@ -89,12 +64,52 @@ Sometimes it is useful to turn on logging just in one file. You can use this
#define LOG_DEBUG
to enable building in of all logging statements in a single file. Put it at
-the top of the file, before any #includes. This overrides any log-level setting
-in U-Boot, including CONFIG_LOG_DEFAULT_LEVEL, but just for that file.
+the top of the file, before any #includes.
+
+To actually get U-Boot to output this you need to also set the default logging
+level - e.g. set CONFIG_LOG_DEFAULT_LEVEL to 7 (:c:type:`LOGL_DEBUG`) or more.
+Otherwise debug output is suppressed and will not be generated.
+
+Using DEBUG
+-----------
+
+U-Boot has traditionally used a #define called DEBUG to enable debugging on a
+file-by-file basis. The debug() macro compiles to a printf() statement if
+DEBUG is enabled, and an empty statement if not.
+
+With logging enabled, debug() statements are interpreted as logging output
+with a level of LOGL_DEBUG and a category of LOGC_NONE.
+
+The logging facilities are intended to replace DEBUG, but if DEBUG is defined
+at the top of a file, then it takes precedence. This means that debug()
+statements will result in output to the console and this output will not be
+logged.
+
+Logging statements
+------------------
+
+The main logging function is:
+.. code-block:: c
+
+ log(category, level, format_string, ...)
+
+Also debug() and error() will generate log records - these use LOG_CATEGORY
+as the category, so you should #define this right at the top of the source
+file to ensure the category is correct.
+
+You can also define CONFIG_LOG_ERROR_RETURN to enable the log_ret() macro. This
+can be used whenever your function returns an error value:
+
+.. code-block:: c
+
+ return log_ret(uclass_first_device(UCLASS_MMC, &dev));
+
+This will write a log record when an error code is detected (a value < 0). This
+can make it easier to trace errors that are generated deep in the call stack.
Convenience functions
----------------------
+~~~~~~~~~~~~~~~~~~~~~
A number of convenience functions are available to shorten the code needed
for logging:
@@ -122,36 +137,6 @@ or
Remember that all uclasses IDs are log categories too.
-
-Log command
------------
-
-The 'log' command provides access to several features:
-
-* level - access the default log level
-* format - access the console log format
-* rec - output a log record
-* test - run tests
-
-Type 'help log' for details.
-
-
-Using DEBUG
------------
-
-U-Boot has traditionally used a #define called DEBUG to enable debugging on a
-file-by-file basis. The debug() macro compiles to a printf() statement if
-DEBUG is enabled, and an empty statement if not.
-
-With logging enabled, debug() statements are interpreted as logging output
-with a level of LOGL_DEBUG and a category of LOGC_NONE.
-
-The logging facilities are intended to replace DEBUG, but if DEBUG is defined
-at the top of a file, then it takes precedence. This means that debug()
-statements will result in output to the console and this output will not be
-logged.
-
-
Logging destinations
--------------------
@@ -165,60 +150,84 @@ enabled or disabled independently:
The syslog driver sends the value of environmental variable 'log_hostname' as
HOSTNAME if available.
-
-Log format
-----------
-
-You can control the log format using the 'log format' command. The basic
-format is::
-
- LEVEL.category,file.c:123-func() message
-
-In the above, file.c:123 is the filename where the log record was generated and
-func() is the function name. By default ('log format default') only the
-function name and message are displayed on the console. You can control which
-fields are present, but not the field order.
-
-
Filters
-------
-Filters are attached to log drivers to control what those drivers emit. Only
-records that pass through the filter make it to the driver.
+Filters are attached to log drivers to control what those drivers emit. FIlters
+can either allow or deny a log message when they match it. Only records which
+are allowed by a filter make it to the driver.
Filters can be based on several criteria:
-* maximum log level
+* minimum or maximum log level
* in a set of categories
* in a set of files
If no filters are attached to a driver then a default filter is used, which
limits output to records with a level less than CONFIG_MAX_LOG_LEVEL.
+Log command
+-----------
-Logging statements
-------------------
+The 'log' command provides access to several features:
-The main logging function is:
+* level - list log levels or set the default log level
+* categories - list log categories
+* drivers - list log drivers
+* filter-list - list filters
+* filter-add - add a new filter
+* filter-remove - remove filters
+* format - access the console log format
+* rec - output a log record
-.. code-block:: c
+Type 'help log' for details.
- log(category, level, format_string, ...)
+Log format
+~~~~~~~~~~
-Also debug() and error() will generate log records - these use LOG_CATEGORY
-as the category, so you should #define this right at the top of the source
-file to ensure the category is correct.
+You can control the log format using the 'log format' command. The basic
+format is::
-You can also define CONFIG_LOG_ERROR_RETURN to enable the log_ret() macro. This
-can be used whenever your function returns an error value:
+ LEVEL.category,file.c:123-func() message
-.. code-block:: c
+In the above, file.c:123 is the filename where the log record was generated and
+func() is the function name. By default ('log format default') only the message
+is displayed on the console. You can control which fields are present, but not
+the field order.
- return log_ret(uclass_first_device(UCLASS_MMC, &dev));
+Adding Filters
+~~~~~~~~~~~~~~
-This will write a log record when an error code is detected (a value < 0). This
-can make it easier to trace errors that are generated deep in the call stack.
+To add new filters at runtime, use the 'log filter-add' command. For example, to
+suppress messages from the SPI and MMC subsystems, run::
+
+ log filter-add -D -c spi -c mmc
+You will also need to add another filter to allow other messages (because the
+default filter no longer applies)::
+
+ log filter-add -A -l info
+
+Log levels may be either symbolic names (like above) or numbers. For example, to
+disable all debug and above (log level 7) messages from ``drivers/core/lists.c``
+and ``drivers/core/ofnode.c``, run::
+
+ log filter-add -D -f drivers/core/lists.c,drivers/core/ofnode.c -L 7
+
+To view active filters, use the 'log filter-list' command. Some example output
+is::
+
+ => log filter-list
+ num policy level categories files
+ 2 deny >= DEBUG drivers/core/lists.c,drivers/core/ofnode.c
+ 0 deny <= IO spi
+ mmc
+ 1 allow <= INFO
+
+Note that filters are processed in-order from top to bottom, not in the order of
+their filter number. Filters are added to the top of the list if they deny when
+they match, and to the bottom if they allow when they match. For more
+information, consult the usage of the 'log' command, by running 'help log'.
Code size
---------
@@ -235,13 +244,12 @@ The last option turns every debug() statement into a logging call, which
bloats the code hugely. The advantage is that it is then possible to enable
all logging within U-Boot.
-
To Do
-----
There are lots of useful additions that could be made. None of the below is
-implemented! If you do one, please add a test in test/py/tests/test_log.py
-
+implemented! If you do one, please add a test in test/log/log_test.c
+log filter-add -D -f drivers/core/lists.c,drivers/core/ofnode.c -l 6
Convenience functions to support setting the category:
* log_arch(level, format_string, ...) - category LOGC_ARCH
@@ -262,25 +270,15 @@ Convert error() statements in the code to log() statements
Figure out what to do with BUG(), BUG_ON() and warn_non_spl()
-Figure out what to do with assert()
-
Add a way to browse log records
Add a way to record log records for browsing using an external tool
-Add commands to add and remove filters
-
Add commands to add and remove log devices
Allow sharing of printf format strings in log records to reduce storage size
for large numbers of log records
-Add a command-line option to sandbox to set the default logging level
-
-Convert core driver model code to use logging
-
-Convert uclasses to use logging with the correct category
-
Consider making log() calls emit an automatic newline, perhaps with a logn()
function to avoid that
@@ -291,9 +289,9 @@ number dropped due to them being generated before the log system was ready.
Add a printf() format string pragma so that log statements are checked properly
-Enhance the log console driver to show level / category / file / line
-information
-
-Add a command to add new log records and delete existing records.
+Add a command to delete existing log records.
-Provide additional log() functions - e.g. logc() to specify the category
+Logging API
+-----------
+.. kernel-doc:: include/log.h
+ :internal:
diff --git a/env/Kconfig b/env/Kconfig
index aa63fae021..67ce93061b 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -376,6 +376,14 @@ config ENV_SPI_MODE
Value of the SPI work mode for environment.
See include/spi.h for value.
+config ENV_SPI_EARLY
+ bool "Access Environment in SPI flashes before relocation"
+ depends on ENV_IS_IN_SPI_FLASH
+ help
+ Enable this if you want to use Environment in SPI flash
+ before relocation. Call env_init() and than you can use
+ env_get_f() for accessing Environment variables.
+
config ENV_IS_IN_UBI
bool "Environment in a UBI volume"
depends on !CHAIN_OF_TRUST
diff --git a/env/common.c b/env/common.c
index ed18378000..6c32a9b479 100644
--- a/env/common.c
+++ b/env/common.c
@@ -141,12 +141,11 @@ int env_import(const char *buf, int check, int flags)
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
static unsigned char env_flags;
-int env_import_redund(const char *buf1, int buf1_read_fail,
- const char *buf2, int buf2_read_fail,
- int flags)
+int env_check_redund(const char *buf1, int buf1_read_fail,
+ const char *buf2, int buf2_read_fail)
{
int crc1_ok, crc2_ok;
- env_t *ep, *tmp_env1, *tmp_env2;
+ env_t *tmp_env1, *tmp_env2;
tmp_env1 = (env_t *)buf1;
tmp_env2 = (env_t *)buf2;
@@ -159,14 +158,13 @@ int env_import_redund(const char *buf1, int buf1_read_fail,
}
if (buf1_read_fail && buf2_read_fail) {
- env_set_default("bad env area", 0);
return -EIO;
} else if (!buf1_read_fail && buf2_read_fail) {
gd->env_valid = ENV_VALID;
- return env_import((char *)tmp_env1, 1, flags);
+ return -EINVAL;
} else if (buf1_read_fail && !buf2_read_fail) {
gd->env_valid = ENV_REDUND;
- return env_import((char *)tmp_env2, 1, flags);
+ return -ENOENT;
}
crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
@@ -175,7 +173,6 @@ int env_import_redund(const char *buf1, int buf1_read_fail,
tmp_env2->crc;
if (!crc1_ok && !crc2_ok) {
- env_set_default("bad CRC", 0);
return -ENOMSG; /* needed for env_load() */
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = ENV_VALID;
@@ -195,12 +192,37 @@ int env_import_redund(const char *buf1, int buf1_read_fail,
gd->env_valid = ENV_VALID;
}
+ return 0;
+}
+
+int env_import_redund(const char *buf1, int buf1_read_fail,
+ const char *buf2, int buf2_read_fail,
+ int flags)
+{
+ env_t *ep;
+ int ret;
+
+ ret = env_check_redund(buf1, buf1_read_fail, buf2, buf2_read_fail);
+
+ if (ret == -EIO) {
+ env_set_default("bad env area", 0);
+ return -EIO;
+ } else if (ret == -EINVAL) {
+ return env_import((char *)buf1, 1, flags);
+ } else if (ret == -ENOENT) {
+ return env_import((char *)buf2, 1, flags);
+ } else if (ret == -ENOMSG) {
+ env_set_default("bad CRC", 0);
+ return -ENOMSG;
+ }
+
if (gd->env_valid == ENV_VALID)
- ep = tmp_env1;
+ ep = (env_t *)buf1;
else
- ep = tmp_env2;
+ ep = (env_t *)buf2;
env_flags = ep->flags;
+
return env_import((char *)ep, 0, flags);
}
#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
diff --git a/env/sf.c b/env/sf.c
index 937778aa37..2eb2de1a4e 100644
--- a/env/sf.c
+++ b/env/sf.c
@@ -287,7 +287,10 @@ __weak void *env_sf_get_env_addr(void)
#endif
#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
-static int env_sf_init(void)
+/*
+ * check if Environment on CONFIG_ENV_ADDR is valid.
+ */
+static int env_sf_init_addr(void)
{
env_t *env_ptr = (env_t *)env_sf_get_env_addr();
@@ -303,12 +306,103 @@ static int env_sf_init(void)
}
#endif
+#if defined(CONFIG_ENV_SPI_EARLY)
+/*
+ * early load environment from SPI flash (before relocation)
+ * and check if it is valid.
+ */
+static int env_sf_init_early(void)
+{
+ int ret;
+ int read1_fail;
+ int read2_fail;
+ int crc1_ok;
+ env_t *tmp_env2 = NULL;
+ env_t *tmp_env1;
+
+ /*
+ * if malloc is not ready yet, we cannot use
+ * this part yet.
+ */
+ if (!gd->malloc_limit)
+ return -ENOENT;
+
+ tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
+ CONFIG_ENV_SIZE);
+ if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
+ tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
+ CONFIG_ENV_SIZE);
+
+ if (!tmp_env1 || !tmp_env2)
+ goto out;
+
+ ret = setup_flash_device();
+ if (ret)
+ goto out;
+
+ read1_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
+ CONFIG_ENV_SIZE, tmp_env1);
+
+ if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
+ read2_fail = spi_flash_read(env_flash,
+ CONFIG_ENV_OFFSET_REDUND,
+ CONFIG_ENV_SIZE, tmp_env2);
+ ret = env_check_redund((char *)tmp_env1, read1_fail,
+ (char *)tmp_env2, read2_fail);
+
+ if (ret == -EIO || ret == -ENOMSG)
+ goto err_read;
+
+ if (gd->env_valid == ENV_VALID)
+ gd->env_addr = (unsigned long)&tmp_env1->data;
+ else
+ gd->env_addr = (unsigned long)&tmp_env2->data;
+ } else {
+ if (read1_fail)
+ goto err_read;
+
+ crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
+ tmp_env1->crc;
+ if (!crc1_ok)
+ goto err_read;
+
+ /* if valid -> this is our env */
+ gd->env_valid = ENV_VALID;
+ gd->env_addr = (unsigned long)&tmp_env1->data;
+ }
+
+ return 0;
+err_read:
+ spi_flash_free(env_flash);
+ env_flash = NULL;
+ free(tmp_env1);
+ if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
+ free(tmp_env2);
+out:
+ /* env is not valid. always return 0 */
+ gd->env_valid = ENV_INVALID;
+ return 0;
+}
+#endif
+
+static int env_sf_init(void)
+{
+#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
+ return env_sf_init_addr();
+#elif defined(CONFIG_ENV_SPI_EARLY)
+ return env_sf_init_early();
+#endif
+ /*
+ * we must return with 0 if there is nothing done,
+ * else env_set_inited() get not called in env_init()
+ */
+ return 0;
+}
+
U_BOOT_ENV_LOCATION(sf) = {
.location = ENVL_SPI_FLASH,
ENV_NAME("SPIFlash")
.load = env_sf_load,
.save = CONFIG_IS_ENABLED(SAVEENV) ? ENV_SAVE_PTR(env_sf_save) : NULL,
-#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
.init = env_sf_init,
-#endif
};
diff --git a/include/env.h b/include/env.h
index af405955b0..c15339a93f 100644
--- a/include/env.h
+++ b/include/env.h
@@ -319,6 +319,24 @@ int env_import(const char *buf, int check, int flags);
int env_export(struct environment_s *env_out);
/**
+ * env_check_redund() - check the two redundant environments
+ * and find out, which is the valid one.
+ *
+ * @buf1: First environment (struct environemnt_s *)
+ * @buf1_read_fail: 0 if buf1 is valid, non-zero if invalid
+ * @buf2: Second environment (struct environemnt_s *)
+ * @buf2_read_fail: 0 if buf2 is valid, non-zero if invalid
+ * @return 0 if OK,
+ * -EIO if no environment is valid,
+ * -EINVAL if read of second entry is good
+ * -ENOENT if read of first entry is good
+ * -ENOMSG if the CRC was bad
+ */
+
+int env_check_redund(const char *buf1, int buf1_read_fail,
+ const char *buf2, int buf2_read_fail);
+
+/**
* env_import_redund() - Select and import one of two redundant environments
*
* @buf1: First environment (struct environemnt_s *)
diff --git a/include/getopt.h b/include/getopt.h
new file mode 100644
index 0000000000..6f5811e64b
--- /dev/null
+++ b/include/getopt.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * getopt.h - a simple getopt(3) implementation.
+ *
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ */
+
+#ifndef __GETOPT_H
+#define __GETOPT_H
+
+/**
+ * struct getopt_state - Saved state across getopt() calls
+ */
+struct getopt_state {
+ /**
+ * @index: Index of the next unparsed argument of @argv. If getopt() has
+ * parsed all of @argv, then @index will equal @argc.
+ */
+ int index;
+ /* private: */
+ /** @arg_index: Index within the current argument */
+ int arg_index;
+ union {
+ /* public: */
+ /**
+ * @opt: Option being parsed when an error occurs. @opt is only
+ * valid when getopt() returns ``?`` or ``:``.
+ */
+ int opt;
+ /**
+ * @arg: The argument to an option, NULL if there is none. @arg
+ * is only valid when getopt() returns an option character.
+ */
+ char *arg;
+ /* private: */
+ };
+};
+
+/**
+ * getopt_init_state() - Initialize a &struct getopt_state
+ * @gs: The state to initialize
+ *
+ * This must be called before using @gs with getopt().
+ */
+void getopt_init_state(struct getopt_state *gs);
+
+int __getopt(struct getopt_state *gs, int argc, char *const argv[],
+ const char *optstring, bool silent);
+
+/**
+ * getopt() - Parse short command-line options
+ * @gs: Internal state and out-of-band return arguments. This must be
+ * initialized with getopt_init_context() beforehand.
+ * @argc: Number of arguments, not including the %NULL terminator
+ * @argv: Argument list, terminated by %NULL
+ * @optstring: Option specification, as described below
+ *
+ * getopt() parses short options. Short options are single characters. They may
+ * be followed by a required argument or an optional argument. Arguments to
+ * options may occur in the same argument as an option (like ``-larg``), or
+ * in the following argument (like ``-l arg``). An argument containing
+ * options begins with a ``-``. If an option expects no arguments, then it may
+ * be immediately followed by another option (like ``ls -alR``).
+ *
+ * @optstring is a list of accepted options. If an option is followed by ``:``
+ * in @optstring, then it expects a mandatory argument. If an option is followed
+ * by ``::`` in @optstring, it expects an optional argument. @gs.arg points
+ * to the argument, if one is parsed.
+ *
+ * getopt() stops parsing options when it encounters the first non-option
+ * argument, when it encounters the argument ``--``, or when it runs out of
+ * arguments. For example, in ``ls -l foo -R``, option parsing will stop when
+ * getopt() encounters ``foo``, if ``l`` does not expect an argument. However,
+ * the whole list of arguments would be parsed if ``l`` expects an argument.
+ *
+ * An example invocation of getopt() might look like::
+ *
+ * char *argv[] = { "program", "-cbx", "-a", "foo", "bar", 0 };
+ * int opt, argc = ARRAY_SIZE(argv) - 1;
+ * struct getopt_state gs;
+ *
+ * getopt_init_state(&gs);
+ * while ((opt = getopt(&gs, argc, argv, "a::b:c")) != -1)
+ * printf("opt = %c, index = %d, arg = \"%s\"\n", opt, gs.index, gs.arg);
+ * printf("%d argument(s) left\n", argc - gs.index);
+ *
+ * and would produce an output of::
+ *
+ * opt = c, index = 1, arg = "<NULL>"
+ * opt = b, index = 2, arg = "x"
+ * opt = a, index = 4, arg = "foo"
+ * 1 argument(s) left
+ *
+ * For further information, refer to the getopt(3) man page.
+ *
+ * Return:
+ * * An option character if an option is found. @gs.arg is set to the
+ * argument if there is one, otherwise it is set to ``NULL``.
+ * * ``-1`` if there are no more options, if a non-option argument is
+ * encountered, or if an ``--`` argument is encountered.
+ * * ``'?'`` if we encounter an option not in @optstring. @gs.opt is set to
+ * the unknown option.
+ * * ``':'`` if an argument is required, but no argument follows the
+ * option. @gs.opt is set to the option missing its argument.
+ *
+ * @gs.index is always set to the index of the next unparsed argument in @argv.
+ */
+static inline int getopt(struct getopt_state *gs, int argc,
+ char *const argv[], const char *optstring)
+{
+ return __getopt(gs, argc, argv, optstring, false);
+}
+
+/**
+ * getopt_silent() - Parse short command-line options silently
+ * @gs: State
+ * @argc: Argument count
+ * @argv: Argument list
+ * @optstring: Option specification
+ *
+ * Same as getopt(), except no error messages are printed.
+ */
+static inline int getopt_silent(struct getopt_state *gs, int argc,
+ char *const argv[], const char *optstring)
+{
+ return __getopt(gs, argc, argv, optstring, true);
+}
+
+#endif /* __GETOPT_H */
diff --git a/include/log.h b/include/log.h
index 58787a3532..4d0692f155 100644
--- a/include/log.h
+++ b/include/log.h
@@ -17,56 +17,92 @@
struct cmd_tbl;
-/** Log levels supported, ranging from most to least important */
+/**
+ * enum log_level_t - Log levels supported, ranging from most to least important
+ */
enum log_level_t {
- LOGL_EMERG = 0, /* U-Boot is unstable */
- LOGL_ALERT, /* Action must be taken immediately */
- LOGL_CRIT, /* Critical conditions */
- LOGL_ERR, /* Error that prevents something from working */
- LOGL_WARNING, /* Warning may prevent optimial operation */
- LOGL_NOTICE, /* Normal but significant condition, printf() */
- LOGL_INFO, /* General information message */
- LOGL_DEBUG, /* Basic debug-level message */
- LOGL_DEBUG_CONTENT, /* Debug message showing full message content */
- LOGL_DEBUG_IO, /* Debug message showing hardware I/O access */
-
+ /** @LOGL_EMERG: U-Boot is unstable */
+ LOGL_EMERG = 0,
+ /** @LOGL_ALERT: Action must be taken immediately */
+ LOGL_ALERT,
+ /** @LOGL_CRIT: Critical conditions */
+ LOGL_CRIT,
+ /** @LOGL_ERR: Error that prevents something from working */
+ LOGL_ERR,
+ /** @LOGL_WARNING: Warning may prevent optimial operation */
+ LOGL_WARNING,
+ /** @LOGL_NOTICE: Normal but significant condition, printf() */
+ LOGL_NOTICE,
+ /** @LOGL_INFO: General information message */
+ LOGL_INFO,
+ /** @LOGL_DEBUG: Basic debug-level message */
+ LOGL_DEBUG,
+ /** @LOGL_DEBUG_CONTENT: Debug message showing full message content */
+ LOGL_DEBUG_CONTENT,
+ /** @LOGL_DEBUG_IO: Debug message showing hardware I/O access */
+ LOGL_DEBUG_IO,
+
+ /** @LOGL_COUNT: Total number of valid log levels */
LOGL_COUNT,
+ /** @LOGL_NONE: Used to indicate that there is no valid log level */
LOGL_NONE,
- LOGL_LEVEL_MASK = 0xf, /* Mask for valid log levels */
- LOGL_FORCE_DEBUG = 0x10, /* Mask to force output due to LOG_DEBUG */
+ /** @LOGL_LEVEL_MASK: Mask for valid log levels */
+ LOGL_LEVEL_MASK = 0xf,
+ /** @LOGL_FORCE_DEBUG: Mask to force output due to LOG_DEBUG */
+ LOGL_FORCE_DEBUG = 0x10,
+ /** @LOGL_FIRST: The first, most-important log level */
LOGL_FIRST = LOGL_EMERG,
+ /** @LOGL_MAX: The last, least-important log level */
LOGL_MAX = LOGL_DEBUG_IO,
- LOGL_CONT = -1, /* Use same log level as in previous call */
+ /** @LOGL_CONT: Use same log level as in previous call */
+ LOGL_CONT = -1,
};
/**
- * Log categories supported. Most of these correspond to uclasses (i.e.
- * enum uclass_id) but there are also some more generic categories.
+ * enum log_category_t - Log categories supported.
+ *
+ * Log categories between %LOGC_FIRST and %LOGC_NONE correspond to uclasses
+ * (i.e. &enum uclass_id), but there are also some more generic categories.
*
* Remember to update log_cat_name[] after adding a new category.
*/
enum log_category_t {
+ /** @LOGC_FIRST: First log category */
LOGC_FIRST = 0, /* First part mirrors UCLASS_... */
+ /** @LOGC_NONE: Default log category */
LOGC_NONE = UCLASS_COUNT, /* First number is after all uclasses */
- LOGC_ARCH, /* Related to arch-specific code */
- LOGC_BOARD, /* Related to board-specific code */
- LOGC_CORE, /* Related to core features (non-driver-model) */
- LOGC_DM, /* Core driver-model */
- LOGC_DT, /* Device-tree */
- LOGC_EFI, /* EFI implementation */
- LOGC_ALLOC, /* Memory allocation */
- LOGC_SANDBOX, /* Related to the sandbox board */
- LOGC_BLOBLIST, /* Bloblist */
- LOGC_DEVRES, /* Device resources (devres_... functions) */
- /* Advanced Configuration and Power Interface (ACPI) */
+ /** @LOGC_ARCH: Related to arch-specific code */
+ LOGC_ARCH,
+ /** @LOGC_BOARD: Related to board-specific code */
+ LOGC_BOARD,
+ /** @LOGC_CORE: Related to core features (non-driver-model) */
+ LOGC_CORE,
+ /** @LOGC_DM: Core driver-model */
+ LOGC_DM,
+ /** @LOGC_DT: Device-tree */
+ LOGC_DT,
+ /** @LOGC_EFI: EFI implementation */
+ LOGC_EFI,
+ /** @LOGC_ALLOC: Memory allocation */
+ LOGC_ALLOC,
+ /** @LOGC_SANDBOX: Related to the sandbox board */
+ LOGC_SANDBOX,
+ /** @LOGC_BLOBLIST: Bloblist */
+ LOGC_BLOBLIST,
+ /** @LOGC_DEVRES: Device resources (``devres_...`` functions) */
+ LOGC_DEVRES,
+ /** @LOGC_ACPI: Advanced Configuration and Power Interface (ACPI) */
LOGC_ACPI,
- LOGC_COUNT, /* Number of log categories */
- LOGC_END, /* Sentinel value for a list of log categories */
- LOGC_CONT = -1, /* Use same category as in previous call */
+ /** @LOGC_COUNT: Number of log categories */
+ LOGC_COUNT,
+ /** @LOGC_END: Sentinel value for lists of log categories */
+ LOGC_END,
+ /** @LOGC_CONT: Use same category as in previous call */
+ LOGC_CONT = -1,
};
/* Helper to cast a uclass ID to a log category */
@@ -85,7 +121,7 @@ static inline int log_uc_cat(enum uclass_id id)
* @func: Function where log record was generated
* @fmt: printf() format string for log record
* @...: Optional parameters, according to the format string @fmt
- * @return 0 if log record was emitted, -ve on error
+ * Return: 0 if log record was emitted, -ve on error
*/
int _log(enum log_category_t cat, enum log_level_t level, const char *file,
int line, const char *func, const char *fmt, ...)
@@ -240,7 +276,7 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line,
* full pathname as it may be huge. Only use this when the user should be
* warning, similar to BUG_ON() in linux.
*
- * @return true if assertion succeeded (condition is true), else false
+ * Return: true if assertion succeeded (condition is true), else false
*/
#define assert_noisy(x) \
({ bool _val = (x); \
@@ -324,8 +360,9 @@ enum log_device_flags {
*/
struct log_driver {
const char *name;
+
/**
- * emit() - emit a log record
+ * @emit: emit a log record
*
* Called by the log system to pass a log record to a particular driver
* for processing. The filter is checked before calling this function.
@@ -361,21 +398,32 @@ enum {
LOGF_MAX_CATEGORIES = 5, /* maximum categories per filter */
};
+/**
+ * enum log_filter_flags - Flags which modify a filter
+ */
enum log_filter_flags {
- LOGFF_HAS_CAT = 1 << 0, /* Filter has a category list */
+ /** @LOGFF_HAS_CAT: Filter has a category list */
+ LOGFF_HAS_CAT = 1 << 0,
+ /** @LOGFF_DENY: Filter denies matching messages */
+ LOGFF_DENY = 1 << 1,
+ /** @LOGFF_LEVEL_MIN: Filter's level is a minimum, not a maximum */
+ LOGFF_LEVEL_MIN = 1 << 2,
};
/**
* struct log_filter - criterial to filter out log messages
*
+ * If a message matches all criteria, then it is allowed. If LOGFF_DENY is set,
+ * then it is denied instead.
+ *
* @filter_num: Sequence number of this filter. This is returned when adding a
* new filter, and must be provided when removing a previously added
* filter.
- * @flags: Flags for this filter (LOGFF_...)
- * @cat_list: List of categories to allow (terminated by LOGC_none). If empty
- * then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries
+ * @flags: Flags for this filter (``LOGFF_...``)
+ * @cat_list: List of categories to allow (terminated by %LOGC_END). If empty
+ * then all categories are permitted. Up to %LOGF_MAX_CATEGORIES entries
* can be provided
- * @max_level: Maximum log level to allow
+ * @level: Maximum (or minimum, if %LOGFF_MIN_LEVEL) log level to allow
* @file_list: List of files to allow, separated by comma. If NULL then all
* files are permitted
* @sibling_node: Next filter in the list of filters for this log device
@@ -384,7 +432,7 @@ struct log_filter {
int filter_num;
int flags;
enum log_category_t cat_list[LOGF_MAX_CATEGORIES];
- enum log_level_t max_level;
+ enum log_level_t level;
const char *file_list;
struct list_head sibling_node;
};
@@ -400,8 +448,9 @@ struct log_filter {
* log_get_cat_name() - Get the name of a category
*
* @cat: Category to look up
- * @return category name (which may be a uclass driver name) if found, or
- * "<invalid>" if invalid, or "<missing>" if not found
+ * Return: category name (which may be a uclass driver name) if found, or
+ * "<invalid>" if invalid, or "<missing>" if not found. All error
+ * responses begin with '<'.
*/
const char *log_get_cat_name(enum log_category_t cat);
@@ -409,7 +458,7 @@ const char *log_get_cat_name(enum log_category_t cat);
* log_get_cat_by_name() - Look up a category by name
*
* @name: Name to look up
- * @return category ID, or LOGC_NONE if not found
+ * Return: Category, or %LOGC_NONE if not found
*/
enum log_category_t log_get_cat_by_name(const char *name);
@@ -417,7 +466,7 @@ enum log_category_t log_get_cat_by_name(const char *name);
* log_get_level_name() - Get the name of a log level
*
* @level: Log level to look up
- * @return log level name (in ALL CAPS)
+ * Return: Log level name (in ALL CAPS)
*/
const char *log_get_level_name(enum log_level_t level);
@@ -425,10 +474,41 @@ const char *log_get_level_name(enum log_level_t level);
* log_get_level_by_name() - Look up a log level by name
*
* @name: Name to look up
- * @return log level ID, or LOGL_NONE if not found
+ * Return: Log level, or %LOGL_NONE if not found
*/
enum log_level_t log_get_level_by_name(const char *name);
+/**
+ * log_device_find_by_name() - Look up a log device by its driver's name
+ *
+ * @drv_name: Name of the driver
+ * Return: the log device, or %NULL if not found
+ */
+struct log_device *log_device_find_by_name(const char *drv_name);
+
+/**
+ * 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``
+ */
+bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat);
+
+/**
+ * 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``
+ */
+bool log_has_file(const char *file_list, const char *file);
+
/* Log format flags (bit numbers) for gd->log_fmt. See log_fmt_chars */
enum log_fmt {
LOGF_CAT = 0,
@@ -446,21 +526,48 @@ enum log_fmt {
int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
/**
+ * log_add_filter_flags() - Add a new filter to a log device, specifying flags
+ *
+ * @drv_name: Driver name to add the filter to (since each driver only has a
+ * single device)
+ * @flags: Flags for this filter (``LOGFF_...``)
+ * @cat_list: List of categories to allow (terminated by %LOGC_END). If empty
+ * then all categories are permitted. Up to %LOGF_MAX_CATEGORIES entries
+ * can be provided
+ * @level: Maximum (or minimum, if %LOGFF_LEVEL_MIN) log level to allow
+ * @file_list: List of files to allow, separated by comma. If NULL then all
+ * files are permitted
+ * Return:
+ * the sequence number of the new filter (>=0) if the filter was added, or a
+ * -ve value on error
+ */
+int log_add_filter_flags(const char *drv_name, enum log_category_t cat_list[],
+ enum log_level_t level, const char *file_list,
+ int flags);
+
+/**
* log_add_filter() - Add a new filter to a log device
*
* @drv_name: Driver name to add the filter to (since each driver only has a
* single device)
- * @cat_list: List of categories to allow (terminated by LOGC_none). If empty
- * then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries
+ * @cat_list: List of categories to allow (terminated by %LOGC_END). If empty
+ * then all categories are permitted. Up to %LOGF_MAX_CATEGORIES entries
* can be provided
* @max_level: Maximum log level to allow
- * @file_list: List of files to allow, separated by comma. If NULL then all
+ * @file_list: List of files to allow, separated by comma. If %NULL then all
* files are permitted
- * @return the sequence number of the new filter (>=0) if the filter was added,
- * or a -ve value on error
+ * Return:
+ * the sequence number of the new filter (>=0) if the filter was added, or a
+ * -ve value on error
*/
-int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
- enum log_level_t max_level, const char *file_list);
+static inline int log_add_filter(const char *drv_name,
+ enum log_category_t cat_list[],
+ enum log_level_t max_level,
+ const char *file_list)
+{
+ return log_add_filter_flags(drv_name, cat_list, max_level, file_list,
+ 0);
+}
/**
* log_remove_filter() - Remove a filter from a log device
@@ -468,8 +575,9 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
* @drv_name: Driver name to remove the filter from (since each driver only has
* a single device)
* @filter_num: Filter number to remove (as returned by log_add_filter())
- * @return 0 if the filter was removed, -ENOENT if either the driver or the
- * filter number was not found
+ * Return:
+ * 0 if the filter was removed, -%ENOENT if either the driver or the filter
+ * number was not found
*/
int log_remove_filter(const char *drv_name, int filter_num);
@@ -490,7 +598,7 @@ int log_device_set_enable(struct log_driver *drv, bool enable);
/**
* log_init() - Set up the log system ready for use
*
- * @return 0 if OK, -ENOMEM if out of memory
+ * Return: 0 if OK, -%ENOMEM if out of memory
*/
int log_init(void);
#else
@@ -504,7 +612,7 @@ static inline int log_init(void)
* log_get_default_format() - get default log format
*
* The default log format is configurable via
- * CONFIG_LOGF_FILE, CONFIG_LOGF_LINE, CONFIG_LOGF_FUNC.
+ * %CONFIG_LOGF_FILE, %CONFIG_LOGF_LINE, and %CONFIG_LOGF_FUNC.
*
* Return: default log format
*/
diff --git a/include/test/log.h b/include/test/log.h
index c661cde75a..e902891450 100644
--- a/include/test/log.h
+++ b/include/test/log.h
@@ -10,7 +10,10 @@
#include <test/test.h>
+#define LOGF_TEST (BIT(LOGF_FUNC) | BIT(LOGF_MSG))
+
/* Declare a new logging test */
#define LOG_TEST(_name) UNIT_TEST(_name, 0, log_test)
+#define LOG_TEST_FLAGS(_name, _flags) UNIT_TEST(_name, _flags, log_test)
#endif /* __TEST_LOG_H__ */
diff --git a/lib/Kconfig b/lib/Kconfig
index 37aae73a26..79651eaad1 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -550,6 +550,11 @@ config SPL_HEXDUMP
This enables functions for printing dumps of binary data in
SPL.
+config GETOPT
+ bool "Enable getopt"
+ help
+ This enables functions for parsing command-line options.
+
config OF_LIBFDT
bool "Enable the FDT library"
default y if OF_CONTROL
diff --git a/lib/Makefile b/lib/Makefile
index 0cd7bea282..7c7fb9aae7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -106,6 +106,7 @@ obj-y += string.o
obj-y += tables_csum.o
obj-y += time.o
obj-y += hexdump.o
+obj-$(CONFIG_GETOPT) += getopt.o
obj-$(CONFIG_TRACE) += trace.o
obj-$(CONFIG_LIB_UUID) += uuid.o
obj-$(CONFIG_LIB_RAND) += rand.o
diff --git a/lib/getopt.c b/lib/getopt.c
new file mode 100644
index 0000000000..8b4515dc19
--- /dev/null
+++ b/lib/getopt.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * getopt.c - a simple getopt(3) implementation. See getopt.h for explanation.
+ *
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ */
+
+#define LOG_CATEGORY LOGC_CORE
+
+#include <common.h>
+#include <getopt.h>
+#include <log.h>
+
+void getopt_init_state(struct getopt_state *gs)
+{
+ gs->index = 1;
+ gs->arg_index = 1;
+}
+
+int __getopt(struct getopt_state *gs, int argc, char *const argv[],
+ const char *optstring, bool silent)
+{
+ char curopt; /* current option character */
+ const char *curoptp; /* pointer to the current option in optstring */
+
+ while (1) {
+ log_debug("arg_index: %d index: %d\n", gs->arg_index,
+ gs->index);
+
+ /* `--` indicates the end of options */
+ if (gs->arg_index == 1 && argv[gs->index] &&
+ !strcmp(argv[gs->index], "--")) {
+ gs->index++;
+ return -1;
+ }
+
+ /* Out of arguments */
+ if (gs->index >= argc)
+ return -1;
+
+ /* Can't parse non-options */
+ if (*argv[gs->index] != '-')
+ return -1;
+
+ /* We have found an option */
+ curopt = argv[gs->index][gs->arg_index];
+ if (curopt)
+ break;
+ /*
+ * no more options in current argv[] element; try the next one
+ */
+ gs->index++;
+ gs->arg_index = 1;
+ }
+
+ /* look up current option in optstring */
+ curoptp = strchr(optstring, curopt);
+
+ if (!curoptp) {
+ if (!silent)
+ printf("%s: invalid option -- %c\n", argv[0], curopt);
+ gs->opt = curopt;
+ gs->arg_index++;
+ return '?';
+ }
+
+ if (*(curoptp + 1) != ':') {
+ /* option with no argument. Just return it */
+ gs->arg = NULL;
+ gs->arg_index++;
+ return curopt;
+ }
+
+ if (*(curoptp + 1) && *(curoptp + 2) == ':') {
+ /* optional argument */
+ if (argv[gs->index][gs->arg_index + 1]) {
+ /* optional argument with directly following arg */
+ gs->arg = argv[gs->index++] + gs->arg_index + 1;
+ gs->arg_index = 1;
+ return curopt;
+ }
+ if (gs->index + 1 == argc) {
+ /* We are at the last argv[] element */
+ gs->arg = NULL;
+ gs->index++;
+ return curopt;
+ }
+ if (*argv[gs->index + 1] != '-') {
+ /*
+ * optional argument with arg in next argv[] element
+ */
+ gs->index++;
+ gs->arg = argv[gs->index++];
+ gs->arg_index = 1;
+ return curopt;
+ }
+
+ /* no optional argument found */
+ gs->arg = NULL;
+ gs->arg_index = 1;
+ gs->index++;
+ return curopt;
+ }
+
+ if (argv[gs->index][gs->arg_index + 1]) {
+ /* required argument with directly following arg */
+ gs->arg = argv[gs->index++] + gs->arg_index + 1;
+ gs->arg_index = 1;
+ return curopt;
+ }
+
+ gs->index++;
+ gs->arg_index = 1;
+
+ if (gs->index >= argc || argv[gs->index][0] == '-') {
+ if (!silent)
+ printf("option requires an argument -- %c\n", curopt);
+ gs->opt = curopt;
+ return ':';
+ }
+
+ gs->arg = argv[gs->index++];
+ return curopt;
+}
diff --git a/test/lib/Makefile b/test/lib/Makefile
index 15cd512506..98a9abf40e 100644
--- a/test/lib/Makefile
+++ b/test/lib/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
obj-$(CONFIG_UT_LIB_RSA) += rsa.o
obj-$(CONFIG_AES) += test_aes.o
+obj-$(CONFIG_GETOPT) += getopt.o
diff --git a/test/lib/getopt.c b/test/lib/getopt.c
new file mode 100644
index 0000000000..3c68b93c8a
--- /dev/null
+++ b/test/lib/getopt.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ *
+ * Portions of these tests were inspired by glibc's posix/bug-getopt1.c and
+ * posix/tst-getopt-cancel.c
+ */
+
+#include <common.h>
+#include <getopt.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int do_test_getopt(struct unit_test_state *uts, int line,
+ struct getopt_state *gs, const char *optstring,
+ int args, char *argv[], int expected_count,
+ int expected[])
+{
+ int opt;
+
+ getopt_init_state(gs);
+ for (int i = 0; i < expected_count; i++) {
+ opt = getopt_silent(gs, args, argv, optstring);
+ if (expected[i] != opt) {
+ /*
+ * Fudge the line number so we can tell which test
+ * failed
+ */
+ ut_failf(uts, __FILE__, line, __func__,
+ "expected[i] == getopt()",
+ "Expected '%c' (%d) with i=%d, got '%c' (%d)",
+ expected[i], expected[i], i, opt, opt);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ opt = getopt_silent(gs, args, argv, optstring);
+ if (opt != -1) {
+ ut_failf(uts, __FILE__, line, __func__,
+ "getopt() != -1",
+ "Expected -1, got '%c' (%d)", opt, opt);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+#define test_getopt(optstring, argv, expected) do { \
+ int ret = do_test_getopt(uts, __LINE__, &gs, optstring, \
+ ARRAY_SIZE(argv) - 1, argv, \
+ ARRAY_SIZE(expected), expected); \
+ if (ret) \
+ return ret; \
+} while (0)
+
+static int lib_test_getopt(struct unit_test_state *uts)
+{
+ struct getopt_state gs;
+
+ /* Happy path */
+ test_getopt("ab:c",
+ ((char *[]){ "program", "-cb", "x", "-a", "foo", 0 }),
+ ((int []){ 'c', 'b', 'a' }));
+ ut_asserteq(4, gs.index);
+
+ /* Make sure we pick up the optional argument */
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-cbx", "-a", "foo", 0 }),
+ ((int []){ 'c', 'b', 'a' }));
+ ut_asserteq(4, gs.index);
+
+ /* Test required arguments */
+ test_getopt("a:b", ((char *[]){ "program", "-a", 0 }),
+ ((int []){ ':' }));
+ ut_asserteq('a', gs.opt);
+ test_getopt("a:b", ((char *[]){ "program", "-b", "-a", 0 }),
+ ((int []){ 'b', ':' }));
+ ut_asserteq('a', gs.opt);
+
+ /* Test invalid arguments */
+ test_getopt("ab:c", ((char *[]){ "program", "-d", 0 }),
+ ((int []){ '?' }));
+ ut_asserteq('d', gs.opt);
+
+ /* Test arg */
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-a", 0 }),
+ ((int []){ 'a' }));
+ ut_asserteq(2, gs.index);
+ ut_assertnull(gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-afoo", 0 }),
+ ((int []){ 'a' }));
+ ut_asserteq(2, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-a", "foo", 0 }),
+ ((int []){ 'a' }));
+ ut_asserteq(3, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-bfoo", 0 }),
+ ((int []){ 'b' }));
+ ut_asserteq(2, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-b", "foo", 0 }),
+ ((int []){ 'b' }));
+ ut_asserteq(3, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ return 0;
+}
+LIB_TEST(lib_test_getopt, 0);
diff --git a/test/log/Makefile b/test/log/Makefile
index fdf529582d..88bc573e9f 100644
--- a/test/log/Makefile
+++ b/test/log/Makefile
@@ -3,6 +3,7 @@
# Copyright (c) 2017 Google, Inc
obj-$(CONFIG_LOG_TEST) += log_test.o
+obj-$(CONFIG_CMD_LOG) += log_filter.o
ifdef CONFIG_UT_LOG
diff --git a/test/log/log_filter.c b/test/log/log_filter.c
new file mode 100644
index 0000000000..e8a6e01a5c
--- /dev/null
+++ b/test/log/log_filter.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <console.h>
+#include <log.h>
+#include <test/log.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Test invalid options */
+static int log_test_filter_invalid(struct unit_test_state *uts)
+{
+ ut_asserteq(1, run_command("log filter-add -AD", 0));
+ ut_asserteq(1, run_command("log filter-add -l1 -L1", 0));
+ ut_asserteq(1, run_command("log filter-add -l1 -L1", 0));
+ ut_asserteq(1, run_command("log filter-add -lfoo", 0));
+ ut_asserteq(1, run_command("log filter-add -cfoo", 0));
+ ut_asserteq(1, run_command("log filter-add -ccore -ccore -ccore -ccore "
+ "-ccore -ccore", 0));
+
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_filter_invalid, UT_TESTF_CONSOLE_REC);
+
+/* Test adding and removing filters */
+static int log_test_filter(struct unit_test_state *uts)
+{
+ bool any_found = false;
+ bool filt1_found = false;
+ bool filt2_found = false;
+ char cmd[32];
+ struct log_filter *filt;
+ struct log_device *ldev;
+ ulong filt1, filt2;
+
+#define create_filter(args, filter_num) do {\
+ ut_assertok(console_record_reset_enable()); \
+ ut_assertok(run_command("log filter-add -p " args, 0)); \
+ ut_assert_skipline(); \
+ ut_assertok(strict_strtoul(uts->actual_str, 10, &(filter_num))); \
+ ut_assert_console_end(); \
+} while (0)
+
+ create_filter("", filt1);
+ create_filter("-DL warning -cmmc -cspi -ffile", filt2);
+
+ ldev = log_device_find_by_name("console");
+ ut_assertnonnull(ldev);
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
+ if (filt->filter_num == filt1) {
+ filt1_found = true;
+ ut_asserteq(0, filt->flags);
+ ut_asserteq(LOGL_MAX, filt->level);
+ ut_assertnull(filt->file_list);
+ } else if (filt->filter_num == filt2) {
+ filt2_found = true;
+ ut_asserteq(LOGFF_HAS_CAT | LOGFF_DENY |
+ LOGFF_LEVEL_MIN, filt->flags);
+ ut_asserteq(true, log_has_cat(filt->cat_list,
+ log_uc_cat(UCLASS_MMC)));
+ ut_asserteq(true, log_has_cat(filt->cat_list,
+ log_uc_cat(UCLASS_SPI)));
+ ut_asserteq(LOGL_WARNING, filt->level);
+ ut_asserteq_str("file", filt->file_list);
+ }
+ }
+ ut_asserteq(true, filt1_found);
+ ut_asserteq(true, filt2_found);
+
+#define remove_filter(filter_num) do { \
+ ut_assertok(console_record_reset_enable()); \
+ snprintf(cmd, sizeof(cmd), "log filter-remove %lu", filter_num); \
+ ut_assertok(run_command(cmd, 0)); \
+ ut_assert_console_end(); \
+} while (0)
+
+ remove_filter(filt1);
+ remove_filter(filt2);
+
+ filt1_found = false;
+ filt2_found = false;
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
+ if (filt->filter_num == filt1)
+ filt1_found = true;
+ else if (filt->filter_num == filt2)
+ filt2_found = true;
+ }
+ ut_asserteq(false, filt1_found);
+ ut_asserteq(false, filt2_found);
+
+ create_filter("", filt1);
+ create_filter("", filt2);
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("log filter-remove -a", 0));
+ ut_assert_console_end();
+
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node)
+ any_found = true;
+ ut_asserteq(false, any_found);
+
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_filter, UT_TESTF_CONSOLE_REC);
diff --git a/test/log/log_test.c b/test/log/log_test.c
index 6a60ff6be3..ea4fc6bc30 100644
--- a/test/log/log_test.c
+++ b/test/log/log_test.c
@@ -9,12 +9,17 @@
#include <common.h>
#include <command.h>
#include <log.h>
+#include <test/log.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
/* emit some sample log records in different ways, for testing */
-static int log_run(enum uclass_id cat, const char *file)
+static int do_log_run(int cat, const char *file)
{
int i;
+ gd->log_fmt = LOGF_TEST;
debug("debug\n");
for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
log(cat, i, "log %d\n", i);
@@ -22,203 +27,358 @@ static int log_run(enum uclass_id cat, const char *file)
i);
}
+ gd->log_fmt = log_get_default_format();
return 0;
}
-static int log_test(int testnum)
+#define log_run_cat(cat) do_log_run(cat, "file")
+#define log_run_file(file) do_log_run(UCLASS_SPI, file)
+#define log_run() do_log_run(UCLASS_SPI, "file")
+
+#define EXPECT_LOG BIT(0)
+#define EXPECT_DIRECT BIT(1)
+#define EXPECT_EXTRA BIT(2)
+
+static int do_check_log_entries(struct unit_test_state *uts, int flags, int min,
+ int max)
{
- int ret;
+ int i;
- printf("test %d\n", testnum);
- switch (testnum) {
- case 0: {
- /* Check a category filter using the first category */
- enum log_category_t cat_list[] = {
- log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI),
- LOGC_NONE, LOGC_END
- };
-
- ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
- if (ret < 0)
- return ret;
- log_run(UCLASS_MMC, "file");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 1: {
- /* Check a category filter using the second category */
- enum log_category_t cat_list[] = {
- log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI), LOGC_END
- };
-
- ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
- if (ret < 0)
- return ret;
- log_run(UCLASS_SPI, "file");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 2: {
- /* Check a category filter that should block log entries */
- enum log_category_t cat_list[] = {
- log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END
- };
-
- ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
- if (ret < 0)
- return ret;
- log_run(UCLASS_SPI, "file");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 3: {
- /* Check a passing file filter */
- ret = log_add_filter("console", NULL, LOGL_MAX, "file");
- if (ret < 0)
- return ret;
- log_run(UCLASS_SPI, "file");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 4: {
- /* Check a failing file filter */
- ret = log_add_filter("console", NULL, LOGL_MAX, "file");
- if (ret < 0)
- return ret;
- log_run(UCLASS_SPI, "file2");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 5: {
- /* Check a passing file filter (second in list) */
- ret = log_add_filter("console", NULL, LOGL_MAX, "file,file2");
- if (ret < 0)
- return ret;
- log_run(UCLASS_SPI, "file2");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 6: {
- /* Check a passing file filter */
- ret = log_add_filter("console", NULL, LOGL_MAX,
- "file,file2,log/log_test.c");
- if (ret < 0)
- return ret;
- log_run(UCLASS_SPI, "file2");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 7: {
- /* Check a log level filter */
- ret = log_add_filter("console", NULL, LOGL_WARNING, NULL);
- if (ret < 0)
- return ret;
- log_run(UCLASS_SPI, "file");
- ret = log_remove_filter("console", ret);
- if (ret < 0)
- return ret;
- break;
- }
- case 8: {
- /* Check two filters, one of which passes everything */
- int filt1, filt2;
-
- ret = log_add_filter("console", NULL, LOGL_WARNING, NULL);
- if (ret < 0)
- return ret;
- filt1 = ret;
- ret = log_add_filter("console", NULL, LOGL_MAX, NULL);
- if (ret < 0)
- return ret;
- filt2 = ret;
- log_run(UCLASS_SPI, "file");
- ret = log_remove_filter("console", filt1);
- if (ret < 0)
- return ret;
- ret = log_remove_filter("console", filt2);
- if (ret < 0)
- return ret;
- break;
- }
- case 9: {
- /* Check three filters, which together pass everything */
- int filt1, filt2, filt3;
-
- ret = log_add_filter("console", NULL, LOGL_MAX, "file)");
- if (ret < 0)
- return ret;
- filt1 = ret;
- ret = log_add_filter("console", NULL, LOGL_MAX, "file2");
- if (ret < 0)
- return ret;
- filt2 = ret;
- ret = log_add_filter("console", NULL, LOGL_MAX,
- "log/log_test.c");
- if (ret < 0)
- return ret;
- filt3 = ret;
- log_run(UCLASS_SPI, "file2");
- ret = log_remove_filter("console", filt1);
- if (ret < 0)
- return ret;
- ret = log_remove_filter("console", filt2);
- if (ret < 0)
- return ret;
- ret = log_remove_filter("console", filt3);
- if (ret < 0)
- return ret;
- break;
- }
- case 10: {
- log_err("level %d\n", LOGL_EMERG);
- log_err("level %d\n", LOGL_ALERT);
- log_err("level %d\n", LOGL_CRIT);
- log_err("level %d\n", LOGL_ERR);
- log_warning("level %d\n", LOGL_WARNING);
- log_notice("level %d\n", LOGL_NOTICE);
- log_info("level %d\n", LOGL_INFO);
- log_debug("level %d\n", LOGL_DEBUG);
- log_content("level %d\n", LOGL_DEBUG_CONTENT);
- log_io("level %d\n", LOGL_DEBUG_IO);
- break;
- }
- case 11:
- log_err("default\n");
- ret = log_device_set_enable(LOG_GET_DRIVER(console), false);
- log_err("disabled\n");
- ret = log_device_set_enable(LOG_GET_DRIVER(console), true);
- log_err("enabled\n");
- break;
+ for (i = min; i <= max; i++) {
+ if (flags & EXPECT_LOG)
+ ut_assert_nextline("do_log_run() log %d", i);
+ if (flags & EXPECT_DIRECT)
+ ut_assert_nextline("func() _log %d", i);
}
+ if (flags & EXPECT_EXTRA)
+ for (; i <= LOGL_MAX ; i++)
+ ut_assert_nextline("func() _log %d", i);
+
+ ut_assert_console_end();
+ return 0;
+}
+
+#define check_log_entries_flags_levels(flags, min, max) do {\
+ int ret = do_check_log_entries(uts, flags, min, max); \
+ if (ret) \
+ return ret; \
+} while (0)
+
+#define check_log_entries_flags(flags) \
+ check_log_entries_flags_levels(flags, LOGL_FIRST, _LOG_MAX_LEVEL)
+#define check_log_entries() check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT)
+#define check_log_entries_extra() \
+ check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT | EXPECT_EXTRA)
+#define check_log_entries_none() check_log_entries_flags(0)
+
+/* Check a category filter using the first category */
+int log_test_cat_allow(struct unit_test_state *uts)
+{
+ enum log_category_t cat_list[] = {
+ log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI),
+ LOGC_NONE, LOGC_END
+ };
+ int filt;
+
+ filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_MMC);
+ check_log_entries_extra();
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_SPI);
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_cat_allow, UT_TESTF_CONSOLE_REC);
+
+/* Check a category filter that should block log entries */
+int log_test_cat_deny_implicit(struct unit_test_state *uts)
+{
+ enum log_category_t cat_list[] = {
+ log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END
+ };
+ int filt;
+
+ filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_SPI);
+ check_log_entries_none();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_cat_deny_implicit, UT_TESTF_CONSOLE_REC);
+
+/* Check passing and failing file filters */
+int log_test_file(struct unit_test_state *uts)
+{
+ int filt;
+
+ filt = log_add_filter("console", NULL, LOGL_MAX, "file");
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file");
+ check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_none();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file, UT_TESTF_CONSOLE_REC);
+
+/* Check a passing file filter (second in list) */
+int log_test_file_second(struct unit_test_state *uts)
+{
+ int filt;
+
+ filt = log_add_filter("console", NULL, LOGL_MAX, "file,file2");
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA);
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file_second, UT_TESTF_CONSOLE_REC);
+
+/* Check a passing file filter (middle of list) */
+int log_test_file_mid(struct unit_test_state *uts)
+{
+ int filt;
+
+ filt = log_add_filter("console", NULL, LOGL_MAX,
+ "file,file2,log/log_test.c");
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file_mid, UT_TESTF_CONSOLE_REC);
+
+/* Check a log level filter */
+int log_test_level(struct unit_test_state *uts)
+{
+ int filt;
+ filt = log_add_filter("console", NULL, LOGL_WARNING, NULL);
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT, LOGL_FIRST,
+ LOGL_WARNING);
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_level, UT_TESTF_CONSOLE_REC);
+
+/* Check two filters, one of which passes everything */
+int log_test_double(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter("console", NULL, LOGL_WARNING, NULL);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter("console", NULL, LOGL_MAX, NULL);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_double, UT_TESTF_CONSOLE_REC);
+
+/* Check three filters, which together pass everything */
+int log_test_triple(struct unit_test_state *uts)
+{
+ int filt1, filt2, filt3;
+
+ filt1 = log_add_filter("console", NULL, LOGL_MAX, "file)");
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter("console", NULL, LOGL_MAX, "file2");
+ ut_assert(filt2 >= 0);
+ filt3 = log_add_filter("console", NULL, LOGL_MAX, "log/log_test.c");
+ ut_assert(filt3 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ ut_assertok(log_remove_filter("console", filt3));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_triple, UT_TESTF_CONSOLE_REC);
+
+int do_log_test_helpers(struct unit_test_state *uts)
+{
+ int i;
+
+ ut_assertok(console_record_reset_enable());
+ log_err("level %d\n", LOGL_EMERG);
+ log_err("level %d\n", LOGL_ALERT);
+ log_err("level %d\n", LOGL_CRIT);
+ log_err("level %d\n", LOGL_ERR);
+ log_warning("level %d\n", LOGL_WARNING);
+ log_notice("level %d\n", LOGL_NOTICE);
+ log_info("level %d\n", LOGL_INFO);
+ log_debug("level %d\n", LOGL_DEBUG);
+ log_content("level %d\n", LOGL_DEBUG_CONTENT);
+ log_io("level %d\n", LOGL_DEBUG_IO);
+
+ for (i = LOGL_EMERG; i <= _LOG_MAX_LEVEL; i++)
+ ut_assert_nextline("%s() level %d", __func__, i);
+ ut_assert_console_end();
+ return 0;
+}
+
+int log_test_helpers(struct unit_test_state *uts)
+{
+ int ret;
+
+ gd->log_fmt = LOGF_TEST;
+ ret = do_log_test_helpers(uts);
+ gd->log_fmt = log_get_default_format();
+ return ret;
+}
+LOG_TEST_FLAGS(log_test_helpers, UT_TESTF_CONSOLE_REC);
+
+int do_log_test_disable(struct unit_test_state *uts)
+{
+ ut_assertok(console_record_reset_enable());
+ log_err("default\n");
+ ut_assert_nextline("%s() default", __func__);
+
+ ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), false));
+ log_err("disabled\n");
+
+ ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), true));
+ log_err("enabled\n");
+ ut_assert_nextline("%s() enabled", __func__);
+ ut_assert_console_end();
return 0;
}
-int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+int log_test_disable(struct unit_test_state *uts)
{
- int testnum = 0;
int ret;
- if (argc > 1)
- testnum = simple_strtoul(argv[1], NULL, 10);
+ gd->log_fmt = LOGF_TEST;
+ ret = do_log_test_disable(uts);
+ gd->log_fmt = log_get_default_format();
+ return ret;
+}
+LOG_TEST_FLAGS(log_test_disable, UT_TESTF_CONSOLE_REC);
+
+/* Check denying based on category */
+int log_test_cat_deny(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+ enum log_category_t cat_list[] = {
+ log_uc_cat(UCLASS_SPI), LOGC_END
+ };
+
+ filt1 = log_add_filter("console", cat_list, LOGL_MAX, NULL);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", cat_list, LOGL_MAX, NULL,
+ LOGFF_DENY);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_SPI);
+ check_log_entries_none();
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_cat_deny, UT_TESTF_CONSOLE_REC);
+
+/* Check denying based on file */
+int log_test_file_deny(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter("console", NULL, LOGL_MAX, "file");
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", NULL, LOGL_MAX, "file",
+ LOGFF_DENY);
+ ut_assert(filt2 >= 0);
- ret = log_test(testnum);
- if (ret)
- printf("Test failure (err=%d)\n", ret);
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file");
+ check_log_entries_none();
- return ret ? CMD_RET_FAILURE : 0;
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file_deny, UT_TESTF_CONSOLE_REC);
+
+/* Check denying based on level */
+int log_test_level_deny(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter("console", NULL, LOGL_INFO, NULL);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL,
+ LOGFF_DENY);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT,
+ LOGL_WARNING + 1, _LOG_MAX_LEVEL);
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_level_deny, UT_TESTF_CONSOLE_REC);
+
+/* Check matching based on minimum level */
+int log_test_min(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL,
+ LOGFF_LEVEL_MIN);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", NULL, LOGL_INFO, NULL,
+ LOGFF_DENY | LOGFF_LEVEL_MIN);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT,
+ LOGL_WARNING, LOGL_INFO - 1);
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
}
+LOG_TEST_FLAGS(log_test_min, UT_TESTF_CONSOLE_REC);
diff --git a/test/log/syslog_test.h b/test/log/syslog_test.h
index 1310257bfe..bfaa6daef8 100644
--- a/test/log/syslog_test.h
+++ b/test/log/syslog_test.h
@@ -8,8 +8,6 @@
#ifndef __SYSLOG_TEST_H
#define __SYSLOG_TEST_H
-#define LOGF_TEST (BIT(LOGF_FUNC) | BIT(LOGF_MSG))
-
/**
* struct sb_log_env - private data for sandbox ethernet driver
*
diff --git a/test/py/tests/test_log.py b/test/py/tests/test_log.py
index 275f9382d2..387b392ce9 100644
--- a/test/py/tests/test_log.py
+++ b/test/py/tests/test_log.py
@@ -10,110 +10,6 @@ and checks that the output is correct.
import pytest
-LOGL_FIRST, LOGL_WARNING, LOGL_INFO = (0, 4, 6)
-
-@pytest.mark.buildconfigspec('cmd_log')
-def test_log(u_boot_console):
- """Test that U-Boot logging works correctly."""
- def check_log_entries(lines, mask, max_level=LOGL_INFO):
- """Check that the expected log records appear in the output
-
- Args:
- lines: iterator containing lines to check
- mask: bit mask to select which lines to check for:
- bit 0: standard log line
- bit 1: _log line
- max_level: maximum log level to expect in the output
- """
- for i in range(max_level):
- if mask & 1:
- assert 'log_run() log %d' % i == next(lines)
- if mask & 3:
- assert 'func() _log %d' % i == next(lines)
-
- def run_test(testnum):
- """Run a particular test number (the 'log test' command)
-
- Args:
- testnum: Test number to run
- Returns:
- iterator containing the lines output from the command
- """
- output = u_boot_console.run_command('log format fm')
- assert output == ''
- with cons.log.section('basic'):
- output = u_boot_console.run_command('log test %d' % testnum)
- split = output.replace('\r', '').splitlines()
- lines = iter(split)
- assert 'test %d' % testnum == next(lines)
- return lines
-
- def test0():
- lines = run_test(0)
- check_log_entries(lines, 3)
-
- def test1():
- lines = run_test(1)
- check_log_entries(lines, 3)
-
- def test2():
- lines = run_test(2)
-
- def test3():
- lines = run_test(3)
- check_log_entries(lines, 2)
-
- def test4():
- lines = run_test(4)
- assert next(lines, None) == None
-
- def test5():
- lines = run_test(5)
- check_log_entries(lines, 2)
-
- def test6():
- lines = run_test(6)
- check_log_entries(lines, 3)
-
- def test7():
- lines = run_test(7)
- check_log_entries(lines, 3, LOGL_WARNING)
-
- def test8():
- lines = run_test(8)
- check_log_entries(lines, 3)
-
- def test9():
- lines = run_test(9)
- check_log_entries(lines, 3)
-
- def test10():
- lines = run_test(10)
- for i in range(7):
- assert 'log_test() level %d' % i == next(lines)
-
- def test11():
- """Test use of log_device_set_enable()"""
- lines = run_test(11)
- assert 'log_test() default'
- # disabled should not be displayed
- assert 'log_test() enabled'
-
- # TODO(sjg@chromium.org): Consider structuring this as separate tests
- cons = u_boot_console
- test0()
- test1()
- test2()
- test3()
- test4()
- test5()
- test6()
- test7()
- test8()
- test9()
- test10()
- test11()
-
@pytest.mark.buildconfigspec('cmd_log')
def test_log_format(u_boot_console):
"""Test the 'log format' and 'log rec' commands"""