[PATCH 00/12] event: Add support for events/hooks

It is a common need in U-Boot to have one subsystem notify another when something happens. An example is reading a partition table when a new block device is set up.
It is also common to add weak functions and 'hook' functions to modify how U-Boot works. See for example ft_board_setup() and the like.
U-Boot would benefit from a generic mechanism to handle these cases, with the ability to hook into various 'events' in a subsystem-independent and transparent way.
This series provides a way to create and dispatch events, with a way of registering a 'spy' which watches for events of different types. This allows 'hook' functions to be created in a generic way.
It also includes a script to list the hooks in an image, which is a bit easier to debug than weak functions, as well as an 'event' command to do the same from within U-Boot.
These 'static' events can be used to replace hooks like misc_init_f(), for example. Also included is basic support for 'dynamic' events, where a spy can be registered at runtime. The need for this is still being figured out.
Enabling events uses a little over 200 bytes of extra code space, on 32-bit machines, a one-off cost.
Simon Glass (12): Makefile: Keep symbols in u-boot-init with LTO sandbox: start: Sort the header files binman: Expand elf support a little event: Add basic support for events event: Add a simple test event: Set up the event system on start-up event: Add events for device probe/remove event: Convert misc_init_f() to use events event: Convert arch_cpu_init_dm() to use events event: Add a command event: Add a script to decode the event-spy list event: Add documentation
MAINTAINERS | 10 ++ Makefile | 2 +- arch/Kconfig | 3 + arch/arm/Kconfig | 3 + arch/arm/include/asm/omap_common.h | 2 + arch/arm/mach-imx/imx8m/soc.c | 4 +- arch/arm/mach-imx/imx8ulp/soc.c | 4 +- arch/arm/mach-omap2/am33xx/board.c | 10 +- arch/arm/mach-omap2/hwinit-common.c | 5 +- arch/mips/Kconfig | 1 + arch/mips/mach-pic32/cpu.c | 4 +- arch/nios2/cpu/cpu.c | 4 +- arch/riscv/cpu/cpu.c | 5 +- arch/riscv/include/asm/system.h | 5 + arch/riscv/lib/spl.c | 3 +- arch/sandbox/cpu/start.c | 8 +- arch/x86/cpu/baytrail/cpu.c | 4 +- arch/x86/cpu/broadwell/cpu.c | 4 +- arch/x86/cpu/ivybridge/cpu.c | 4 +- arch/x86/cpu/quark/quark.c | 4 +- arch/x86/include/asm/fsp2/fsp_api.h | 8 ++ arch/x86/lib/fsp2/fsp_init.c | 4 +- arch/x86/lib/spl.c | 12 -- arch/x86/lib/tpl.c | 10 -- board/google/chromebook_coral/coral.c | 6 +- board/keymile/kmcent2/kmcent2.c | 5 +- cmd/Kconfig | 8 ++ cmd/Makefile | 1 + cmd/event.c | 27 ++++ common/Kconfig | 37 ++++- common/Makefile | 2 + common/board_f.c | 13 +- common/board_r.c | 1 + common/event.c | 196 ++++++++++++++++++++++++++ common/log.c | 1 + configs/chromebook_coral_defconfig | 1 + configs/galileo_defconfig | 1 + configs/kmcent2_defconfig | 2 +- configs/sandbox64_defconfig | 1 - configs/sandbox_defconfig | 1 - configs/sandbox_flattree_defconfig | 1 - configs/sandbox_spl_defconfig | 1 - configs/tools-only_defconfig | 1 - doc/develop/event.rst | 100 +++++++++++++ doc/develop/index.rst | 1 + doc/usage/event.rst | 49 +++++++ doc/usage/index.rst | 1 + drivers/core/Kconfig | 10 ++ drivers/core/device-remove.c | 8 ++ drivers/core/device.c | 9 ++ drivers/core/root.c | 5 + include/asm-generic/global_data.h | 13 ++ include/dm/device-internal.h | 10 ++ include/event.h | 182 ++++++++++++++++++++++++ include/event_internal.h | 35 +++++ include/init.h | 12 -- include/log.h | 2 + scripts/event_dump.py | 117 +++++++++++++++ test/common/Makefile | 1 + test/common/event.c | 85 +++++++++++ test/py/tests/test_event_dump.py | 19 +++ test/test-main.c | 4 + tools/binman/elf.py | 58 +++++++- 63 files changed, 1067 insertions(+), 83 deletions(-) create mode 100644 cmd/event.c create mode 100644 common/event.c create mode 100644 doc/develop/event.rst create mode 100644 doc/usage/event.rst create mode 100644 include/event.h create mode 100644 include/event_internal.h create mode 100755 scripts/event_dump.py create mode 100644 test/common/event.c create mode 100644 test/py/tests/test_event_dump.py

At present linker-list symbols in the u-boot-init group (e.g. start.o) are silent discarded with LTO enabled. Fix this so that it is possible to declare lists here also.
Signed-off-by: Simon Glass sjg@chromium.org ---
Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile b/Makefile index 074ba7349f2..73894c80004 100644 --- a/Makefile +++ b/Makefile @@ -1744,7 +1744,7 @@ quiet_cmd_keep_syms_lto_cc = KSLCC $@ cmd_keep_syms_lto_cc = \ $(CC) $(filter-out $(LTO_CFLAGS),$(c_flags)) -c -o $@ $<
-$(u-boot-keep-syms-lto_c): $(u-boot-main) +$(u-boot-keep-syms-lto_c): $(u-boot-main) $(u-boot-init) $(call if_changed,keep_syms_lto) $(u-boot-keep-syms-lto): $(u-boot-keep-syms-lto_c) $(call if_changed,keep_syms_lto_cc)

These header files don't follow the correct order. Fix this.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/sandbox/cpu/start.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 13b0731ec3a..12aace9a202 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -4,14 +4,13 @@ */
#include <common.h> +#include <cli.h> #include <command.h> -#include <dm/root.h> #include <efi_loader.h> #include <errno.h> #include <init.h> #include <log.h> #include <os.h> -#include <cli.h> #include <sort.h> #include <asm/getopt.h> #include <asm/global_data.h> @@ -19,6 +18,7 @@ #include <asm/malloc.h> #include <asm/sections.h> #include <asm/state.h> +#include <dm/root.h> #include <linux/ctype.h>
DECLARE_GLOBAL_DATA_PTR;

Allow finding a symbol by its address. Also export the function to get the file offset of a particular address, so it can be used by a script to be added.
Signed-off-by: Simon Glass sjg@chromium.org ---
tools/binman/elf.py | 58 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 7 deletions(-)
diff --git a/tools/binman/elf.py b/tools/binman/elf.py index de2bb4651fa..61315d5b63b 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -84,6 +84,57 @@ def GetSymbols(fname, patterns): # Sort dict by address return OrderedDict(sorted(syms.items(), key=lambda x: x[1].address))
+def _GetFileOffset(elf, addr): + """Get the file offset for an address + + Args: + elf (ELFFile): ELF file to check + addr (int): Address to search for + + Returns + int: Offset of that address in the ELF file, or None if not valid + """ + for seg in elf.iter_segments(): + seg_end = seg['p_vaddr'] + seg['p_filesz'] + if seg.header['p_type'] == 'PT_LOAD': + if addr >= seg['p_vaddr'] and addr < seg_end: + return addr - seg['p_vaddr'] + seg['p_offset'] + +def GetFileOffset(fname, addr): + """Get the file offset for an address + + Args: + fname (str): Filename of ELF file to check + addr (int): Address to search for + + Returns + int: Offset of that address in the ELF file, or None if not valid + """ + if not ELF_TOOLS: + raise ValueError('Python elftools package is not available') + with open(fname, 'rb') as fd: + elf = ELFFile(fd) + return _GetFileOffset(elf, addr) + +def GetSymbolFromAddress(fname, addr): + """Get the symbol at a particular address + + Args: + fname (str): Filename of ELF file to check + addr (int): Address to search for + + Returns: + str: Symbol name, or None if no symbol at that address + """ + if not ELF_TOOLS: + raise ValueError('Python elftools package is not available') + with open(fname, 'rb') as fd: + elf = ELFFile(fd) + syms = GetSymbols(fname, None) + for name, sym in syms.items(): + if sym.address == addr: + return name + def GetSymbolFileOffset(fname, patterns): """Get the symbols from an ELF file
@@ -96,13 +147,6 @@ def GetSymbolFileOffset(fname, patterns): key: Name of symbol value: Hex value of symbol """ - def _GetFileOffset(elf, addr): - for seg in elf.iter_segments(): - seg_end = seg['p_vaddr'] + seg['p_filesz'] - if seg.header['p_type'] == 'PT_LOAD': - if addr >= seg['p_vaddr'] and addr < seg_end: - return addr - seg['p_vaddr'] + seg['p_offset'] - if not ELF_TOOLS: raise ValueError('Python elftools package is not available')

Add a way to create and dispatch events without needing to allocate memory. Also add a way to 'spy' on events, thus allowing 'hooks' to be created.
Use a linker list for static events, which we can use to replace functions like arch_cpu_init_f(). Allow an EVENT_DEBUG option which makes it easier to see what is going on at runtime, but uses more code space.
Dynamic events allow the creation of a spy at runtime. This is not always necessary, but can be enabled with EVENT_DYNAMIC if needed.
A 'test' event is the only option for now.
Signed-off-by: Simon Glass sjg@chromium.org ---
MAINTAINERS | 6 + common/Kconfig | 31 +++++ common/Makefile | 2 + common/board_r.c | 1 + common/event.c | 186 ++++++++++++++++++++++++++++++ common/log.c | 1 + include/asm-generic/global_data.h | 13 +++ include/event.h | 160 +++++++++++++++++++++++++ include/event_internal.h | 35 ++++++ include/log.h | 2 + 10 files changed, 437 insertions(+) create mode 100644 common/event.c create mode 100644 include/event.h create mode 100644 include/event_internal.h
diff --git a/MAINTAINERS b/MAINTAINERS index f4bd3d79e22..3574ffa2a87 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -786,6 +786,12 @@ S: Maintained F: doc/usage/environment.rst F: scripts/env2string.awk
+EVENTS +M: Simon Glass sjg@chromium.org +S: Maintained +F: common/event.c +F: include/event.h + FASTBOOT S: Orphaned F: cmd/fastboot.c diff --git a/common/Kconfig b/common/Kconfig index 50ac4331f5c..9ebfdb4626b 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -492,6 +492,37 @@ config DISPLAY_BOARDINFO_LATE
menu "Start-up hooks"
+config EVENT + bool "General-purpose event-handling mechanism" + default y if SANDBOX + help + This enables sending and processing of events, to allow interested + parties to be alerted when something happens. This is an attempt to + step the flow of weak functions, hooks, functions in board_f.c + and board_r.c and the Kconfig options below. + + See doc/develop/event.rst for more information. + +if EVENT + +config EVENT_DYNAMIC + bool "Support event registration at runtime" + default y if SANDBOX + help + Enable this to support adding an event spy at runtime, without adding + it to the EVENT_SPy() linker list. This increases code size slightly + but provides more flexibility for boards and subsystems that need it. + +config EVENT_DEBUG + bool "Enable event debugging assistance" + default y if SANDBOX + help + Enable this get usefui features for seeing what is happening with + events, such as event-type names. This adds to the code size of + U-Boot so can be turned off for production builds. + +endif # EVENT + config ARCH_EARLY_INIT_R bool "Call arch-specific init soon after relocation" help diff --git a/common/Makefile b/common/Makefile index 24be05c368d..a3f5a28e0b7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -90,6 +90,8 @@ obj-y += malloc_simple.o endif endif
+obj-$(CONFIG_$(SPL_TPL_)EVENT) += event.o + obj-$(CONFIG_$(SPL_TPL_)HASH) += hash.o obj-$(CONFIG_IO_TRACE) += iotrace.o obj-y += memsize.o diff --git a/common/board_r.c b/common/board_r.c index 6d520662dbb..7daa8696c7f 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -629,6 +629,7 @@ static int run_main_loop(void) static init_fnc_t init_sequence_r[] = { initr_trace, initr_reloc, + CONFIG_IS_ENABLED(EVENT, (event_init,)) /* TODO: could x86/PPC have this also perhaps? */ #if defined(CONFIG_ARM) || defined(CONFIG_RISCV) initr_caches, diff --git a/common/event.c b/common/event.c new file mode 100644 index 00000000000..366be245696 --- /dev/null +++ b/common/event.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Events provide a general-purpose way to react to / subscribe to changes + * within U-Boot + * + * Copyright 2021 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#define LOG_CATEGORY LOGC_EVENT + +#include <common.h> +#include <event.h> +#include <event_internal.h> +#include <log.h> +#include <linker_lists.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <linux/list.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if CONFIG_IS_ENABLED(EVENT_DEBUG) +const char *const type_name[] = { + "none", + "test", +}; + +_Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size"); +#endif + +static const char *event_type_name(enum event_t type) +{ +#if CONFIG_IS_ENABLED(EVENT_DEBUG) + return type_name[type]; +#else + return "(unknown)"; +#endif +} + +static int notify_static(struct event *ev) +{ + struct evspy_info *start = + ll_entry_start(struct evspy_info, evspy_info); + const int n_ents = ll_entry_count(struct evspy_info, evspy_info); + struct evspy_info *spy; + + for (spy = start; spy != start + n_ents; spy++) { + if (spy->type == ev->type) { + int ret; + + log_debug("Sending event %x/%s to spy '%s'\n", ev->type, + event_type_name(ev->type), event_spy_id(spy)); + ret = spy->func(NULL, ev); + + /* + * TODO: Handle various return codes to + * + * - claim an event (no others will see it) + * - return an error from the event + */ + if (ret) + return log_msg_ret("spy", ret); + } + } + + return 0; +} + +static int notify_dynamic(struct event *ev) +{ + struct event_state *state = gd_event_state(); + struct event_spy *spy, *next; + + list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node) { + if (spy->type == ev->type) { + int ret; + + log_debug("Sending event %x/%s to spy '%s'\n", ev->type, + event_type_name(ev->type), spy->id); + ret = spy->func(spy->ctx, ev); + + /* + * TODO: Handle various return codes to + * + * - claim an event (no others will see it) + * - return an error from the event + */ + if (ret) + return log_msg_ret("spy", ret); + } + } + + return 0; +} + +int event_notify(enum event_t type, void *data, int size) +{ + struct event event; + int ret; + + event.type = type; + if (size > sizeof(event.data)) + return log_msg_ret("size", -E2BIG); + memcpy(&event.data, data, size); + + ret = notify_static(&event); + if (ret) + return log_msg_ret("dyn", ret); + + if (CONFIG_IS_ENABLED(EVENT_DYNAMIC)) { + ret = notify_dynamic(&event); + if (ret) + return log_msg_ret("dyn", ret); + } + + return 0; +} + +int event_notify_null(enum event_t type) +{ + return event_notify(type, NULL, 0); +} + +void event_show_spy_list(void) +{ + struct evspy_info *start = + ll_entry_start(struct evspy_info, evspy_info); + const int n_ents = ll_entry_count(struct evspy_info, evspy_info); + struct evspy_info *spy; + const int size = sizeof(ulong) * 2; + + printf("Seq %-24s %*s %s\n", "Type", size, "Function", "ID"); + for (spy = start; spy != start + n_ents; spy++) { + printf("%3x %-3x %-20s %*p %s\n", (uint)(spy - start), + spy->type, event_type_name(spy->type), size, spy->func, + event_spy_id(spy)); + } +} + +#if CONFIG_IS_ENABLED(EVENT_DYNAMIC) +static void spy_free(struct event_spy *spy) +{ + list_del(&spy->sibling_node); +} + +int event_register(const char *id, enum event_t type, event_handler_t func, void *ctx) +{ + struct event_state *state = gd_event_state(); + struct event_spy *spy; + + if (!CONFIG_IS_ENABLED(EVENT_DYNAMIC)) + return -ENOSYS; + spy = malloc(sizeof(*spy)); + if (!spy) + return log_msg_ret("alloc", -ENOMEM); + + spy->id = id; + spy->type = type; + spy->func = func; + spy->ctx = ctx; + list_add_tail(&spy->sibling_node, &state->spy_head); + + return 0; +} + +int event_uninit(void) +{ + struct event_state *state = gd_event_state(); + struct event_spy *spy, *next; + + list_for_each_entry_safe(spy, next, &state->spy_head, sibling_node) + spy_free(spy); + + return 0; +} + +int event_init(void) +{ + struct event_state *state = gd_event_state(); + + INIT_LIST_HEAD(&state->spy_head); + + return 0; +} +#endif /* EVENT_DYNAMIC */ diff --git a/common/log.c b/common/log.c index 1aaa6c1527b..aa2e780d2d7 100644 --- a/common/log.c +++ b/common/log.c @@ -28,6 +28,7 @@ static const char *const log_cat_name[] = { "devres", "acpi", "boot", + "event", };
_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE, diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 104282bd479..a73abd1b8d4 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -20,6 +20,7 @@ */
#ifndef __ASSEMBLY__ +#include <event_internal.h> #include <fdtdec.h> #include <membuff.h> #include <linux/list.h> @@ -463,6 +464,12 @@ struct global_data { */ char *smbios_version; #endif +#if CONFIG_IS_ENABLED(EVENT) + /** + * @event_state: Points to the current state of events + */ + struct event_state event_state; +#endif }; #ifndef DO_DEPS_ONLY static_assert(sizeof(struct global_data) == GD_SIZE); @@ -524,6 +531,12 @@ static_assert(sizeof(struct global_data) == GD_SIZE); #define gd_set_multi_dtb_fit(_dtb) #endif
+#if CONFIG_IS_ENABLED(EVENT_DYNAMIC) +#define gd_event_state() ((struct event_state *)&gd->event_state) +#else +#define gd_event_state() NULL +#endif + /** * enum gd_flags - global data flags * diff --git a/include/event.h b/include/event.h new file mode 100644 index 00000000000..ac1a42a9e10 --- /dev/null +++ b/include/event.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Events provide a general-purpose way to react to / subscribe to changes + * within U-Boot + * + * Copyright 2021 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#ifndef __event_h +#define __event_h + +/** + * enum event_t - Types of events supported by U-Boot + * + * @EVT_DM_PRE_PROBE: Device is about to be probed + */ +enum event_t { + EVT_NONE, + EVT_TEST, + + EVT_COUNT +}; + +union event_data { + /** + * struct event_data_test - test data + * + * @signal: A value to update the state with + */ + struct event_data_test { + int signal; + } test; +}; + +/** + * struct event - an event that can be sent and received + * + * @type: Event type + * @data: Data for this particular event + */ +struct event { + enum event_t type; + union event_data data; +}; + +/** Function type for event handlers */ +typedef int (*event_handler_t)(void *ctx, struct event *event); + +/** + * struct evspy_info - information about an event spy + * + * @func: Function to call when the event is activated (must be first) + * @type: Event type + * @id: Event id string + */ +struct evspy_info { + event_handler_t func; + enum event_t type; +#if CONFIG_IS_ENABLED(EVENT_DEBUG) + const char *id; +#endif +}; + +/* Declare a new event spy */ +#if CONFIG_IS_ENABLED(EVENT_DEBUG) +#define _ESPY_REC(_type, _func) { _func, _type, #_func, } +#else +#define _ESPY_REC(_type, _func) { _func, _type, } +#endif + +static inline const char *event_spy_id(struct evspy_info *spy) +{ +#if CONFIG_IS_ENABLED(EVENT_DEBUG) + return spy->id; +#else + return "?"; +#endif +} + +#define EVENT_SPY(_type, _func) \ + ll_entry_declare(struct evspy_info, _type, evspy_info) = \ + _ESPY_REC(_type, _func) + +/** + * event_register - register a new spy + * + * @id: Spy ID + * @type: Event type to subscribe to + * @func: Function to call when the event is sent + * @ctx: Context to pass to the function + * @return 0 if OK, -ve on error + */ +int event_register(const char *id, enum event_t type, event_handler_t func, + void *ctx); + +#if CONFIG_IS_ENABLED(EVENT) +/** + * event_notify() - notify spies about an event + * + * It is possible to pass in union event_data here but that may not be + * convenient if the data is elsewhere, or is one of the members of the union. + * So this uses a void * for @data, with a separate @size. + * + * @type: Event type + * @data: Event data to be sent (e.g. union_event_data) + * @size: Size of data in bytes + * @return 0 if OK, -ve on error + */ +int event_notify(enum event_t type, void *data, int size); + +/** + * event_notify_null() - notify spies about an event + * + * Data is NULL and the size is 0 + * + * @type: Event type + * @return 0 if OK, -ve on error + */ +int event_notify_null(enum event_t type); +#else +static inline int event_notify(enum event_t type, void *data, int size) +{ + return 0; +} + +static inline int event_notify_null(enum event_t type) +{ + return 0; +} +#endif + +#if CONFIG_IS_ENABLED(EVENT_DYNAMIC) +/** + * event_uninit() - Clean up dynamic events + * + * This removes all dynamic event handlers + */ +int event_uninit(void); + +/** + * event_uninit() - Set up dynamic events + * + * Init a list of dynamic event handlers, so that these can be added as + * needed + */ +int event_init(void); +#else +static inline int event_uninit(void) +{ + return 0; +} + +static inline int event_init(void) +{ + return 0; +} +#endif + +#endif diff --git a/include/event_internal.h b/include/event_internal.h new file mode 100644 index 00000000000..8432c6f0e5f --- /dev/null +++ b/include/event_internal.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Internal definitions for events + * + * Copyright 2021 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#ifndef __event_internal_h +#define __event_internal_h + +#include <event.h> +#include <linux/list.h> + +/** + * struct event_spy - a spy that watches for an event of a particular type + * + * @id: Spy ID + * @type: Event type to subscribe to + * @func: Function to call when the event is sent + * @ctx: Context to pass to the function + */ +struct event_spy { + struct list_head sibling_node; + const char *id; + enum event_t type; + event_handler_t func; + void *ctx; +}; + +struct event_state { + struct list_head spy_head; +}; + +#endif diff --git a/include/log.h b/include/log.h index e0e12ce1944..7169fff7aa5 100644 --- a/include/log.h +++ b/include/log.h @@ -98,6 +98,8 @@ enum log_category_t { LOGC_ACPI, /** @LOGC_BOOT: Related to boot process / boot image processing */ LOGC_BOOT, + /** @LOGC_EVENT: Related to event and event handling */ + LOGC_EVENT, /** @LOGC_COUNT: Number of log categories */ LOGC_COUNT, /** @LOGC_END: Sentinel value for lists of log categories */

Add a test for event registration and activation.
Signed-off-by: Simon Glass sjg@chromium.org ---
MAINTAINERS | 1 + test/common/Makefile | 1 + test/common/event.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ test/test-main.c | 4 ++++ 4 files changed, 53 insertions(+) create mode 100644 test/common/event.c
diff --git a/MAINTAINERS b/MAINTAINERS index 3574ffa2a87..bc8d53cf387 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -791,6 +791,7 @@ M: Simon Glass sjg@chromium.org S: Maintained F: common/event.c F: include/event.h +F: test/common/event.c
FASTBOOT S: Orphaned diff --git a/test/common/Makefile b/test/common/Makefile index 24c9145dccc..9087788ba6a 100644 --- a/test/common/Makefile +++ b/test/common/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += cmd_ut_common.o obj-$(CONFIG_AUTOBOOT) += test_autoboot.o +obj-$(CONFIG_EVENT) += event.o diff --git a/test/common/event.c b/test/common/event.c new file mode 100644 index 00000000000..dfaa66ea492 --- /dev/null +++ b/test/common/event.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Unit tests for event handling + * + * Copyright 2021 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <common.h> +#include <dm.h> +#include <event.h> +#include <test/common.h> +#include <test/test.h> +#include <test/ut.h> + +struct test_state { + struct udevice *dev; + int val; +}; + +static int h_adder(void *ctx, struct event *event) +{ + struct event_data_test *data = &event->data.test; + struct test_state *test_state = ctx; + + test_state->val += data->signal; + + return 0; +} + +static int test_event_base(struct unit_test_state *uts) +{ + struct test_state state; + int signal; + + state.val = 12; + ut_assertok(event_register("wibble", EVT_TEST, h_adder, &state)); + + signal = 17; + + /* Check that the handler is called */ + ut_assertok(event_notify(EVT_TEST, &signal, sizeof(signal))); + ut_asserteq(12 + 17, state.val); + + return 0; +} +COMMON_TEST(test_event_base, 0); diff --git a/test/test-main.c b/test/test-main.c index 3cdf6849c57..9c1fbb8fed6 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -7,6 +7,7 @@ #include <common.h> #include <console.h> #include <dm.h> +#include <event.h> #include <dm/root.h> #include <dm/test.h> #include <dm/uclass-internal.h> @@ -218,6 +219,8 @@ static int dm_test_restore(struct device_node *of_root) */ static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) { + ut_assertok(event_init()); + if (test->flags & UT_TESTF_DM) ut_assertok(dm_test_pre_run(uts));
@@ -260,6 +263,7 @@ static int test_post_run(struct unit_test_state *uts, struct unit_test *test) ut_unsilence_console(uts); if (test->flags & UT_TESTF_DM) ut_assertok(dm_test_post_run(uts)); + ut_assertok(event_uninit());
return 0; }

Call event_init() before relocation to get the event system running.
Signed-off-by: Simon Glass sjg@chromium.org ---
common/board_f.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/common/board_f.c b/common/board_f.c index dd69c3b6b77..ebb9b65c6be 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -19,6 +19,7 @@ #include <dm.h> #include <env.h> #include <env_internal.h> +#include <event.h> #include <fdtdec.h> #include <fs.h> #include <hang.h> @@ -828,6 +829,7 @@ static const init_fnc_t init_sequence_f[] = { initf_malloc, log_init, initf_bootstage, /* uses its own timer, so does not need DM */ + CONFIG_IS_ENABLED(EVENT, (event_init,)) #ifdef CONFIG_BLOBLIST bloblist_init, #endif

Generate events when devices are probed or removed, allowing hooks to be added for these situations.
This is controlled by the DM_EVENT config option.
Signed-off-by: Simon Glass sjg@chromium.org ---
common/event.c | 6 ++++++ drivers/core/Kconfig | 10 ++++++++++ drivers/core/device-remove.c | 8 ++++++++ drivers/core/device.c | 9 +++++++++ include/dm/device-internal.h | 10 ++++++++++ include/event.h | 15 ++++++++++++++ test/common/event.c | 38 ++++++++++++++++++++++++++++++++++++ 7 files changed, 96 insertions(+)
diff --git a/common/event.c b/common/event.c index 366be245696..737d3ac9eaa 100644 --- a/common/event.c +++ b/common/event.c @@ -24,6 +24,12 @@ DECLARE_GLOBAL_DATA_PTR; const char *const type_name[] = { "none", "test", + + /* Events related to driver model */ + "dm_pre_probe", + "dm_post_probe", + "dm_pre_remove", + "dm_post_remove", };
_Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size"); diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 8f7703c8b58..5c3400417f9 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -77,6 +77,16 @@ config DM_DEVICE_REMOVE it causes unplugged devices to linger around in the dm-tree, and it causes USB host controllers to not be stopped when booting the OS.
+config DM_EVENT + bool "Support events with driver model" + depends on DM + imply EVENT + default y if SANDBOX + help + This enables support for generating events related to driver model + operations, such as prbing or removing a device. Subsystems can + register a 'spy' function that is called when the event occurs. + config SPL_DM_DEVICE_REMOVE bool "Support device removal in SPL" depends on SPL_DM diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 69c50da44a9..3093c625ed4 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -207,6 +207,10 @@ int device_remove(struct udevice *dev, uint flags) if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)) return 0;
+ ret = device_notify(dev, EVT_DM_PRE_REMOVE); + if (ret) + return ret; + /* * If the child returns EKEYREJECTED, continue. It just means that it * didn't match the flags. @@ -256,6 +260,10 @@ int device_remove(struct udevice *dev, uint flags)
dev_bic_flags(dev, DM_FLAG_ACTIVATED);
+ ret = device_notify(dev, EVT_DM_POST_REMOVE); + if (ret) + goto err_remove; + return 0;
err_remove: diff --git a/drivers/core/device.c b/drivers/core/device.c index 4873c47d10b..0c7d5d1033c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -10,6 +10,7 @@
#include <common.h> #include <cpu_func.h> +#include <event.h> #include <log.h> #include <asm/global_data.h> #include <asm/io.h> @@ -493,6 +494,10 @@ int device_probe(struct udevice *dev) if (dev_get_flags(dev) & DM_FLAG_ACTIVATED) return 0;
+ ret = device_notify(dev, EVT_DM_PRE_PROBE); + if (ret) + return ret; + drv = dev->driver; assert(drv);
@@ -597,6 +602,10 @@ int device_probe(struct udevice *dev) dev->name, ret, errno_str(ret)); }
+ ret = device_notify(dev, EVT_DM_POST_PROBE); + if (ret) + return ret; + return 0; fail_uclass: if (device_remove(dev, DM_REMOVE_NORMAL)) { diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index e6b71cbfd2b..eab687c735a 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -10,6 +10,7 @@ #ifndef _DM_DEVICE_INTERNAL_H #define _DM_DEVICE_INTERNAL_H
+#include <event.h> #include <linker_lists.h> #include <dm/ofnode.h>
@@ -426,4 +427,13 @@ static inline void devres_release_all(struct udevice *dev) }
#endif /* ! CONFIG_DEVRES */ + +static inline int device_notify(const struct udevice *dev, enum event_t type) +{ +#if CONFIG_IS_ENABLED(DM_EVENT) + return event_notify(type, &dev, sizeof(dev)); +#else + return 0; +#endif +} #endif diff --git a/include/event.h b/include/event.h index ac1a42a9e10..16a5c192802 100644 --- a/include/event.h +++ b/include/event.h @@ -19,6 +19,12 @@ enum event_t { EVT_NONE, EVT_TEST,
+ /* Events related to driver model */ + EVT_DM_PRE_PROBE, + EVT_DM_POST_PROBE, + EVT_DM_PRE_REMOVE, + EVT_DM_POST_REMOVE, + EVT_COUNT };
@@ -31,6 +37,15 @@ union event_data { struct event_data_test { int signal; } test; + + /** + * struct event_dm - driver model event + * + * @dev: Device this event relates to + */ + struct event_dm { + struct udevice *dev; + } dm; };
/** diff --git a/test/common/event.c b/test/common/event.c index dfaa66ea492..6037ae2ce3b 100644 --- a/test/common/event.c +++ b/test/common/event.c @@ -45,3 +45,41 @@ static int test_event_base(struct unit_test_state *uts) return 0; } COMMON_TEST(test_event_base, 0); + +static int h_probe(void *ctx, struct event *event) +{ + struct test_state *test_state = ctx; + + test_state->dev = event->data.dm.dev; + switch (event->type) { + case EVT_DM_PRE_PROBE: + test_state->val |= 1; + break; + case EVT_DM_POST_PROBE: + test_state->val |= 2; + break; + default: + break; + } + + return 0; +} + +static int test_event_probe(struct unit_test_state *uts) +{ + struct test_state state; + struct udevice *dev; + + state.val = 0; + ut_assertok(event_register("pre", EVT_DM_PRE_PROBE, h_probe, &state)); + ut_assertok(event_register("post", EVT_DM_POST_PROBE, h_probe, &state)); + + /* Probe a device */ + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + + /* Check that the handler is called */ + ut_asserteq(3, state.val); + + return 0; +} +COMMON_TEST(test_event_probe, UT_TESTF_DM | UT_TESTF_SCAN_FDT);

This hook can be implmented using events, for the three boards that actually use it.
Add the event type and event handlers. Drop CONFIG_MISC_INIT_F since we can just use CONFIG_EVENT to control this. Since sandbox always enables CONFIG_EVENT, we can drop the defconfig lines there too.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/sandbox/cpu/start.c | 4 +++- board/google/chromebook_coral/coral.c | 6 ++++-- board/keymile/kmcent2/kmcent2.c | 5 ++++- common/Kconfig | 6 ------ common/board_f.c | 7 +++++-- common/event.c | 3 +++ configs/chromebook_coral_defconfig | 1 + configs/kmcent2_defconfig | 2 +- configs/sandbox64_defconfig | 1 - configs/sandbox_defconfig | 1 - configs/sandbox_flattree_defconfig | 1 - configs/sandbox_spl_defconfig | 1 - configs/tools-only_defconfig | 1 - include/event.h | 3 +++ include/init.h | 1 - 15 files changed, 24 insertions(+), 19 deletions(-)
diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 12aace9a202..684ef569cdc 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -8,6 +8,7 @@ #include <command.h> #include <efi_loader.h> #include <errno.h> +#include <event.h> #include <init.h> #include <log.h> #include <os.h> @@ -119,10 +120,11 @@ int sandbox_early_getopt_check(void) os_exit(0); }
-int misc_init_f(void) +int sandbox_misc_init_f(void *ctx, struct event *event) { return sandbox_early_getopt_check(); } +EVENT_SPY(EVT_MISC_INIT_F, sandbox_misc_init_f);
static int sandbox_cmdline_cb_help(struct sandbox_state *state, const char *arg) { diff --git a/board/google/chromebook_coral/coral.c b/board/google/chromebook_coral/coral.c index 53c5171d02b..34ec1803e47 100644 --- a/board/google/chromebook_coral/coral.c +++ b/board/google/chromebook_coral/coral.c @@ -10,6 +10,7 @@ #include <command.h> #include <cros_ec.h> #include <dm.h> +#include <event.h> #include <init.h> #include <log.h> #include <sysinfo.h> @@ -32,9 +33,9 @@ struct cros_gpio_info { int flags; };
-int misc_init_f(void) +static int coral_check_ll_boot(void *ctx, struct event *event) { - if (!ll_boot_init()) { + if (CONFIG_IS_ENABLED(COREBOOT_SYSINFO) && !ll_boot_init()) { printf("Running as secondary loader"); if (gd->arch.coreboot_table) { int ret; @@ -55,6 +56,7 @@ int misc_init_f(void)
return 0; } +EVENT_SPY(EVT_MISC_INIT_F, coral_check_ll_boot);
int arch_misc_init(void) { diff --git a/board/keymile/kmcent2/kmcent2.c b/board/keymile/kmcent2/kmcent2.c index 4f5164e63ca..768b2276ae2 100644 --- a/board/keymile/kmcent2/kmcent2.c +++ b/board/keymile/kmcent2/kmcent2.c @@ -6,6 +6,7 @@ * Copyright 2013 Freescale Semiconductor, Inc. */
+#include <event.h> #include <asm/cache.h> #include <asm/fsl_fdt.h> #include <asm/fsl_law.h> @@ -186,7 +187,7 @@ unsigned long get_board_sys_clk(unsigned long dummy) return 66666666; }
-int misc_init_f(void) +static int kmcent2_misc_init_f(void *ctx, struct event *event) { /* configure QRIO pis for i2c deblocking */ i2c_deblock_gpio_cfg(); @@ -214,6 +215,8 @@ int misc_init_f(void)
return 0; } +EVENT_SPY(EVT_MISC_INIT_F, kmcent2_misc_init_f); +
#define USED_SRDS_BANK 0 #define EXPECTED_SRDS_RFCK SRDS_PLLCR0_RFCK_SEL_100 diff --git a/common/Kconfig b/common/Kconfig index 9ebfdb4626b..f989dc38bc7 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -574,12 +574,6 @@ config LAST_STAGE_INIT U-Boot calls last_stage_init() before the command-line interpreter is started.
-config MISC_INIT_F - bool "Execute pre-relocation misc init" - help - Enabling this option calls the 'misc_init_f' function in the init - sequence just before DRAM is inited. - config MISC_INIT_R bool "Execute Misc Init" default y if ARCH_KEYSTONE || ARCH_SUNXI || MPC85xx diff --git a/common/board_f.c b/common/board_f.c index ebb9b65c6be..7fa3cbe08e3 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -818,6 +818,11 @@ __weak int clear_bss(void) return 0; }
+static int misc_init_f(void) +{ + return event_notify_null(EVT_MISC_INIT_F); +} + static const init_fnc_t init_sequence_f[] = { setup_mon_len, #ifdef CONFIG_OF_CONTROL @@ -877,9 +882,7 @@ static const init_fnc_t init_sequence_f[] = { show_board_info, #endif INIT_FUNC_WATCHDOG_INIT -#if defined(CONFIG_MISC_INIT_F) misc_init_f, -#endif INIT_FUNC_WATCHDOG_RESET #if CONFIG_IS_ENABLED(SYS_I2C_LEGACY) init_func_i2c, diff --git a/common/event.c b/common/event.c index 737d3ac9eaa..4270809d496 100644 --- a/common/event.c +++ b/common/event.c @@ -30,6 +30,9 @@ const char *const type_name[] = { "dm_post_probe", "dm_pre_remove", "dm_post_remove", + + /* init hooks */ + "misc_init_f", };
_Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size"); diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig index 2dd37f64c00..c74b2517645 100644 --- a/configs/chromebook_coral_defconfig +++ b/configs/chromebook_coral_defconfig @@ -35,6 +35,7 @@ CONFIG_USE_BOOTCOMMAND=y CONFIG_BOOTCOMMAND="tpm init; tpm startup TPM2_SU_CLEAR; read mmc 0:2 100000 0 80; setexpr loader *001004f0; setexpr size *00100518; setexpr blocks $size / 200; read mmc 0:2 100000 80 $blocks; setexpr setup $loader - 1000; setexpr cmdline_ptr $loader - 2000; setexpr.s cmdline *$cmdline_ptr; setexpr cmdline gsub %U \\${uuid}; if part uuid mmc 0:2 uuid; then zboot start 100000 0 0 0 $setup cmdline; zboot load; zboot setup; zboot dump; zboot go;fi" CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_EVENT=y CONFIG_LAST_STAGE_INIT=y CONFIG_BLOBLIST=y # CONFIG_TPL_BLOBLIST is not set diff --git a/configs/kmcent2_defconfig b/configs/kmcent2_defconfig index 9361e81e803..d64aa020f4c 100644 --- a/configs/kmcent2_defconfig +++ b/configs/kmcent2_defconfig @@ -14,10 +14,10 @@ CONFIG_FIT=y CONFIG_FIT_VERBOSE=y CONFIG_OF_BOARD_SETUP=y CONFIG_OF_STDOUT_VIA_ALIAS=y +CONFIG_EVENT=y CONFIG_BOARD_EARLY_INIT_F=y CONFIG_BOARD_EARLY_INIT_R=y CONFIG_LAST_STAGE_INIT=y -CONFIG_MISC_INIT_F=y CONFIG_HUSH_PARSER=y CONFIG_CMD_DM=y CONFIG_CMD_I2C=y diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index d849989cf61..d7d23ab5af8 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -23,7 +23,6 @@ CONFIG_CONSOLE_RECORD=y CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_PRE_CONSOLE_BUFFER=y CONFIG_DISPLAY_BOARDINFO_LATE=y -CONFIG_MISC_INIT_F=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index c390afe9de5..b4abc065395 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -32,7 +32,6 @@ CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_PRE_CONSOLE_BUFFER=y CONFIG_LOG=y CONFIG_DISPLAY_BOARDINFO_LATE=y -CONFIG_MISC_INIT_F=y CONFIG_STACKPROTECTOR=y CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index f184723a899..bfeb1651125 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -20,7 +20,6 @@ CONFIG_BOOTSTAGE_STASH_SIZE=0x4096 CONFIG_CONSOLE_RECORD=y CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_DISPLAY_BOARDINFO_LATE=y -CONFIG_MISC_INIT_F=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index f1a54ace9bc..4afa488b129 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -30,7 +30,6 @@ CONFIG_BOOTSTAGE_STASH_SIZE=0x4096 CONFIG_CONSOLE_RECORD=y CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_DISPLAY_BOARDINFO_LATE=y -CONFIG_MISC_INIT_F=y CONFIG_HANDOFF=y CONFIG_SPL_BOARD_INIT=y CONFIG_SPL_ENV_SUPPORT=y diff --git a/configs/tools-only_defconfig b/configs/tools-only_defconfig index 5ffc625b0dd..77cf2db6109 100644 --- a/configs/tools-only_defconfig +++ b/configs/tools-only_defconfig @@ -8,7 +8,6 @@ CONFIG_FIT=y CONFIG_FIT_SIGNATURE=y CONFIG_USE_BOOTCOMMAND=y CONFIG_BOOTCOMMAND="run distro_bootcmd" -CONFIG_MISC_INIT_F=y # CONFIG_CMD_BOOTD is not set # CONFIG_CMD_BOOTM is not set # CONFIG_CMD_ELF is not set diff --git a/include/event.h b/include/event.h index 16a5c192802..f1333af7bd4 100644 --- a/include/event.h +++ b/include/event.h @@ -25,6 +25,9 @@ enum event_t { EVT_DM_PRE_REMOVE, EVT_DM_POST_REMOVE,
+ /* Init hooks */ + EVT_MISC_INIT_F, + EVT_COUNT };
diff --git a/include/init.h b/include/init.h index f2cd46dead0..f1c3982319f 100644 --- a/include/init.h +++ b/include/init.h @@ -214,7 +214,6 @@ int init_cache_f_r(void); int print_cpuinfo(void); #endif int timer_init(void); -int misc_init_f(void);
#if defined(CONFIG_DTB_RESELECT) int embedded_dtb_select(void);

Instead of a special function, send an event after driver model is inited and adjust the boards which use this function.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/Kconfig | 3 +++ arch/arm/Kconfig | 3 +++ arch/arm/include/asm/omap_common.h | 2 ++ arch/arm/mach-imx/imx8m/soc.c | 4 +++- arch/arm/mach-imx/imx8ulp/soc.c | 4 +++- arch/arm/mach-omap2/am33xx/board.c | 10 ++++++---- arch/arm/mach-omap2/hwinit-common.c | 5 ++++- arch/mips/Kconfig | 1 + arch/mips/mach-pic32/cpu.c | 4 +++- arch/nios2/cpu/cpu.c | 4 +++- arch/riscv/cpu/cpu.c | 5 ++++- arch/riscv/include/asm/system.h | 5 +++++ arch/riscv/lib/spl.c | 3 ++- arch/x86/cpu/baytrail/cpu.c | 4 +++- arch/x86/cpu/broadwell/cpu.c | 4 +++- arch/x86/cpu/ivybridge/cpu.c | 4 +++- arch/x86/cpu/quark/quark.c | 4 +++- arch/x86/include/asm/fsp2/fsp_api.h | 8 ++++++++ arch/x86/lib/fsp2/fsp_init.c | 4 +++- arch/x86/lib/spl.c | 12 ------------ arch/x86/lib/tpl.c | 10 ---------- common/board_f.c | 6 ------ common/event.c | 1 + configs/galileo_defconfig | 1 + drivers/core/root.c | 5 +++++ include/event.h | 1 + include/init.h | 11 ----------- 27 files changed, 73 insertions(+), 55 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig index 39156067b2c..0734397d3d3 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -89,6 +89,7 @@ config NIOS2 bool "Nios II architecture" select CPU select DM + imply DM_EVENT select OF_CONTROL select SUPPORT_OF_CONTROL imply CMD_DM @@ -108,6 +109,7 @@ config RISCV select DM imply DM_SERIAL imply DM_ETH + imply DM_EVENT imply DM_MMC imply DM_SPI imply DM_SPI_FLASH @@ -230,6 +232,7 @@ config X86 imply CMD_SF_TEST imply CMD_ZBOOT imply DM_ETH + imply DM_EVENT imply DM_GPIO imply DM_KEYBOARD imply DM_MMC diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 85c964b7a18..1167bd488d2 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -766,6 +766,7 @@ config ARCH_OMAP2PLUS select SUPPORT_SPL imply TI_SYSC if DM && OF_CONTROL imply FIT + imply DM_EVENT
config ARCH_MESON bool "Amlogic Meson" @@ -823,6 +824,7 @@ config ARCH_IMX8M select DM select SUPPORT_SPL imply CMD_DM + imply DM_EVENT
config ARCH_IMX8ULP bool "NXP i.MX8ULP platform" @@ -833,6 +835,7 @@ config ARCH_IMX8ULP select SUPPORT_SPL select GPIO_EXTRA_HEADER imply CMD_DM + imply DM_EVENT
config ARCH_IMXRT bool "NXP i.MXRT platform" diff --git a/arch/arm/include/asm/omap_common.h b/arch/arm/include/asm/omap_common.h index de8fc99d047..0ebaf269e4c 100644 --- a/arch/arm/include/asm/omap_common.h +++ b/arch/arm/include/asm/omap_common.h @@ -833,6 +833,8 @@ static inline u8 is_dra76x_acd(void)
#ifndef __ASSEMBLY__ u32 omap_sys_boot_device(void); + +void early_system_init(void); #endif
#endif /* _OMAP_COMMON_H_ */ diff --git a/arch/arm/mach-imx/imx8m/soc.c b/arch/arm/mach-imx/imx8m/soc.c index 863508776dd..f270eecfe8b 100644 --- a/arch/arm/mach-imx/imx8m/soc.c +++ b/arch/arm/mach-imx/imx8m/soc.c @@ -7,6 +7,7 @@
#include <common.h> #include <cpu_func.h> +#include <event.h> #include <init.h> #include <log.h> #include <asm/arch/imx-regs.h> @@ -481,7 +482,7 @@ static void imx_set_wdog_powerdown(bool enable) writew(enable, &wdog3->wmcr); }
-int arch_cpu_init_dm(void) +static int imx8m_check_clock(void *ctx, struct event *event) { struct udevice *dev; int ret; @@ -498,6 +499,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, imx8m_check_clock);
int arch_cpu_init(void) { diff --git a/arch/arm/mach-imx/imx8ulp/soc.c b/arch/arm/mach-imx/imx8ulp/soc.c index bba6323f96f..fd3f8c29d8f 100644 --- a/arch/arm/mach-imx/imx8ulp/soc.c +++ b/arch/arm/mach-imx/imx8ulp/soc.c @@ -11,6 +11,7 @@ #include <asm/mach-imx/boot_mode.h> #include <asm/global_data.h> #include <efi_loader.h> +#include <event.h> #include <spl.h> #include <asm/arch/rdc.h> #include <asm/arch/s400_api.h> @@ -495,7 +496,7 @@ int arch_cpu_init(void) return 0; }
-int arch_cpu_init_dm(void) +static int imx8ulp_check_mu(void *ctx, struct event *event) { struct udevice *devp; int node, ret; @@ -510,6 +511,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, imx8ulp_check_mu);
#if defined(CONFIG_SPL_BUILD) __weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image) diff --git a/arch/arm/mach-omap2/am33xx/board.c b/arch/arm/mach-omap2/am33xx/board.c index c44667668e9..7a4818caa4c 100644 --- a/arch/arm/mach-omap2/am33xx/board.c +++ b/arch/arm/mach-omap2/am33xx/board.c @@ -11,6 +11,7 @@ #include <dm.h> #include <debug_uart.h> #include <errno.h> +#include <event.h> #include <init.h> #include <net.h> #include <ns16550.h> @@ -596,11 +597,12 @@ void board_init_f(ulong dummy)
#endif
-int arch_cpu_init_dm(void) +static int am33xx_dm_post_init(void *ctx, struct event *event) { hw_data_init(); -#if !CONFIG_IS_ENABLED(SKIP_LOWLEVEL_INIT) - early_system_init(); -#endif + if (!CONFIG_IS_ENABLED(SKIP_LOWLEVEL_INIT)) + early_system_init(); + return 0; } +EVENT_SPY(EVT_DM_POST_INIT, am33xx_dm_post_init); diff --git a/arch/arm/mach-omap2/hwinit-common.c b/arch/arm/mach-omap2/hwinit-common.c index 3da50f974dc..5d4445d5bca 100644 --- a/arch/arm/mach-omap2/hwinit-common.c +++ b/arch/arm/mach-omap2/hwinit-common.c @@ -12,6 +12,7 @@ */ #include <common.h> #include <debug_uart.h> +#include <event.h> #include <fdtdec.h> #include <init.h> #include <spl.h> @@ -239,11 +240,13 @@ void board_init_f(ulong dummy) } #endif
-int arch_cpu_init_dm(void) +int omap2_system_init(void *ctx, struct event *event) { early_system_init(); + return 0; } +EVENT_SPY(EVT_DM_POST_INIT, omap2_system_init);
/* * Routine: wait_for_command_complete diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 28234aa0bb6..06cae68ee57 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -130,6 +130,7 @@ config MACH_PIC32 config TARGET_BOSTON bool "Support Boston" select DM + imply DM_EVENT select DM_SERIAL select MIPS_CM select SYS_CACHE_SHIFT_6 diff --git a/arch/mips/mach-pic32/cpu.c b/arch/mips/mach-pic32/cpu.c index eac2fe5f8c9..9de4ec0ed63 100644 --- a/arch/mips/mach-pic32/cpu.c +++ b/arch/mips/mach-pic32/cpu.c @@ -7,6 +7,7 @@ #include <common.h> #include <clk.h> #include <dm.h> +#include <event.h> #include <init.h> #include <malloc.h> #include <asm/global_data.h> @@ -95,12 +96,13 @@ static void prefetch_init(void) }
/* arch specific CPU init after DM */ -int arch_cpu_init_dm(void) +int pic32_flash_prefetch(void *ctx, struct event *event) { /* flash prefetch */ prefetch_init(); return 0; } +EVENT_SPY(EVT_DM_POST_INIT, pic32_flash_prefetch);
/* Un-gate DDR2 modules (gated by default) */ static void ddr2_pmd_ungate(void) diff --git a/arch/nios2/cpu/cpu.c b/arch/nios2/cpu/cpu.c index b55c8fbc584..4dd9c10faa5 100644 --- a/arch/nios2/cpu/cpu.c +++ b/arch/nios2/cpu/cpu.c @@ -10,6 +10,7 @@ #include <cpu_func.h> #include <dm.h> #include <errno.h> +#include <event.h> #include <init.h> #include <irq_func.h> #include <asm/cache.h> @@ -63,7 +64,7 @@ static void copy_exception_trampoline(void) } #endif
-int arch_cpu_init_dm(void) +static int nios_cpu_setup(void *ctx, struct event *event) { struct udevice *dev; int ret; @@ -79,6 +80,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, nios_cpu_setup);
static int altera_nios2_get_desc(const struct udevice *dev, char *buf, int size) diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c index 8d90c5e6b8a..3ffcbbd23fa 100644 --- a/arch/riscv/cpu/cpu.c +++ b/arch/riscv/cpu/cpu.c @@ -7,9 +7,11 @@ #include <cpu.h> #include <dm.h> #include <dm/lists.h> +#include <event.h> #include <init.h> #include <log.h> #include <asm/encoding.h> +#include <asm/system.h> #include <dm/uclass-internal.h> #include <linux/bitops.h>
@@ -81,7 +83,7 @@ static void dummy_pending_ipi_clear(ulong hart, ulong arg0, ulong arg1) } #endif
-int arch_cpu_init_dm(void) +int riscv_cpu_setup(void *ctx, struct event *event) { int ret;
@@ -133,6 +135,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, riscv_cpu_setup);
int arch_early_init_r(void) { diff --git a/arch/riscv/include/asm/system.h b/arch/riscv/include/asm/system.h index a3404758235..9d8e43e3942 100644 --- a/arch/riscv/include/asm/system.h +++ b/arch/riscv/include/asm/system.h @@ -7,6 +7,8 @@ #ifndef __ASM_RISCV_SYSTEM_H #define __ASM_RISCV_SYSTEM_H
+struct event; + /* * Interrupt configuring macros. * @@ -14,4 +16,7 @@ * */
+/* Hook to set up the CPU (called from SPL too) */ +int riscv_cpu_setup(void *ctx, struct event *event); + #endif /* __ASM_RISCV_SYSTEM_H */ diff --git a/arch/riscv/lib/spl.c b/arch/riscv/lib/spl.c index 8baee07beac..f4d3b67e5dd 100644 --- a/arch/riscv/lib/spl.c +++ b/arch/riscv/lib/spl.c @@ -11,6 +11,7 @@ #include <spl.h> #include <asm/global_data.h> #include <asm/smp.h> +#include <asm/system.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -27,7 +28,7 @@ __weak void board_init_f(ulong dummy) if (ret) panic("spl_early_init() failed: %d\n", ret);
- arch_cpu_init_dm(); + riscv_cpu_setup(NULL, NULL);
preloader_console_init();
diff --git a/arch/x86/cpu/baytrail/cpu.c b/arch/x86/cpu/baytrail/cpu.c index 309a50a1161..68bf40ba8e8 100644 --- a/arch/x86/cpu/baytrail/cpu.c +++ b/arch/x86/cpu/baytrail/cpu.c @@ -8,6 +8,7 @@ #include <common.h> #include <cpu.h> #include <dm.h> +#include <event.h> #include <init.h> #include <log.h> #include <pci.h> @@ -44,7 +45,7 @@ static void hsuart_clock_set(void *base) * Configure the internal clock of both SIO HS-UARTs, if they are enabled * via FSP */ -int arch_cpu_init_dm(void) +static int baytrail_uart_init(void *ctx, struct event *event) { struct udevice *dev; void *base; @@ -63,6 +64,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, baytrail_uart_init);
static void set_max_freq(void) { diff --git a/arch/x86/cpu/broadwell/cpu.c b/arch/x86/cpu/broadwell/cpu.c index 3832a97f2c7..2adcf4b242c 100644 --- a/arch/x86/cpu/broadwell/cpu.c +++ b/arch/x86/cpu/broadwell/cpu.c @@ -8,6 +8,7 @@ #include <common.h> #include <dm.h> #include <cpu.h> +#include <event.h> #include <init.h> #include <log.h> #include <asm/cpu.h> @@ -24,7 +25,7 @@ #include <asm/arch/pch.h> #include <asm/arch/rcb.h>
-int arch_cpu_init_dm(void) +static int broadwell_init_cpu(void *ctx, struct event *event) { struct udevice *dev; int ret; @@ -41,6 +42,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, broadwell_init_cpu);
void set_max_freq(void) { diff --git a/arch/x86/cpu/ivybridge/cpu.c b/arch/x86/cpu/ivybridge/cpu.c index a02f4f9600e..cffc5d5b1d8 100644 --- a/arch/x86/cpu/ivybridge/cpu.c +++ b/arch/x86/cpu/ivybridge/cpu.c @@ -14,6 +14,7 @@ #include <cpu_func.h> #include <dm.h> #include <errno.h> +#include <event.h> #include <fdtdec.h> #include <init.h> #include <log.h> @@ -53,7 +54,7 @@ int arch_cpu_init(void) return x86_cpu_init_f(); }
-int arch_cpu_init_dm(void) +static int ivybridge_cpu_init(void *ctx, struct event *ev) { struct pci_controller *hose; struct udevice *bus, *dev; @@ -85,6 +86,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, ivybridge_cpu_init);
#define PCH_EHCI0_TEMP_BAR0 0xe8000000 #define PCH_EHCI1_TEMP_BAR0 0xe8000400 diff --git a/arch/x86/cpu/quark/quark.c b/arch/x86/cpu/quark/quark.c index 30b4711b9a5..2fd4ee3e69e 100644 --- a/arch/x86/cpu/quark/quark.c +++ b/arch/x86/cpu/quark/quark.c @@ -5,6 +5,7 @@
#include <common.h> #include <cpu_func.h> +#include <event.h> #include <init.h> #include <mmc.h> #include <asm/cache.h> @@ -247,7 +248,7 @@ int arch_cpu_init(void) return 0; }
-int arch_cpu_init_dm(void) +static int quark_init_dm(void *ctx, struct event *event) { /* * Initialize PCIe controller @@ -262,6 +263,7 @@ int arch_cpu_init_dm(void)
return 0; } +EVENT_SPY(EVT_DM_POST_INIT, quark_init_dm);
int checkcpu(void) { diff --git a/arch/x86/include/asm/fsp2/fsp_api.h b/arch/x86/include/asm/fsp2/fsp_api.h index af1e8857b97..07e50b271d5 100644 --- a/arch/x86/include/asm/fsp2/fsp_api.h +++ b/arch/x86/include/asm/fsp2/fsp_api.h @@ -60,4 +60,12 @@ int fsp_silicon_init(bool s3wake, bool use_spi_flash);
typedef asmlinkage int (*fsp_silicon_init_func)(struct fsps_upd *params);
+/** + * fsp_setup_pinctrl() - Set up the pinctrl for FSP + * + * @ctx: Event context (not used) + * @event: Event information (not used) + */ +int fsp_setup_pinctrl(void *ctx, struct event *event); + #endif diff --git a/arch/x86/lib/fsp2/fsp_init.c b/arch/x86/lib/fsp2/fsp_init.c index 5afdce1e0d4..b15926e8247 100644 --- a/arch/x86/lib/fsp2/fsp_init.c +++ b/arch/x86/lib/fsp2/fsp_init.c @@ -9,6 +9,7 @@ #include <bootstage.h> #include <cbfs.h> #include <dm.h> +#include <event.h> #include <init.h> #include <log.h> #include <spi.h> @@ -18,7 +19,7 @@ #include <dm/uclass-internal.h> #include <asm/fsp2/fsp_internal.h>
-int arch_cpu_init_dm(void) +int fsp_setup_pinctrl(void *ctx, struct event *event) { struct udevice *dev; ofnode node; @@ -41,6 +42,7 @@ int arch_cpu_init_dm(void)
return ret; } +EVENT_SPY(EVT_DM_POST_INIT, fsp_setup_pinctrl);
#if !defined(CONFIG_TPL_BUILD) binman_sym_declare(ulong, intel_fsp_m, image_pos); diff --git a/arch/x86/lib/spl.c b/arch/x86/lib/spl.c index b18c1cd6092..4260ccde14f 100644 --- a/arch/x86/lib/spl.c +++ b/arch/x86/lib/spl.c @@ -27,11 +27,6 @@
DECLARE_GLOBAL_DATA_PTR;
-__weak int arch_cpu_init_dm(void) -{ - return 0; -} - #ifdef CONFIG_TPL
static int set_max_freq(void) @@ -88,13 +83,6 @@ static int x86_spl_init(void) debug("%s: arch_cpu_init() failed\n", __func__); return ret; } -#ifndef CONFIG_TPL - ret = arch_cpu_init_dm(); - if (ret) { - debug("%s: arch_cpu_init_dm() failed\n", __func__); - return ret; - } -#endif preloader_console_init(); #if !defined(CONFIG_TPL) && !CONFIG_IS_ENABLED(CPU) ret = print_cpuinfo(); diff --git a/arch/x86/lib/tpl.c b/arch/x86/lib/tpl.c index 5b57e53c2dd..18b05b2f672 100644 --- a/arch/x86/lib/tpl.c +++ b/arch/x86/lib/tpl.c @@ -19,11 +19,6 @@
DECLARE_GLOBAL_DATA_PTR;
-__weak int arch_cpu_init_dm(void) -{ - return 0; -} - static int x86_tpl_init(void) { int ret; @@ -44,11 +39,6 @@ static int x86_tpl_init(void) debug("%s: arch_cpu_init() failed\n", __func__); return ret; } - ret = arch_cpu_init_dm(); - if (ret) { - debug("%s: arch_cpu_init_dm() failed\n", __func__); - return ret; - } preloader_console_init();
return 0; diff --git a/common/board_f.c b/common/board_f.c index 7fa3cbe08e3..bf51449bdcd 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -803,11 +803,6 @@ __weak int reserve_arch(void) return 0; }
-__weak int arch_cpu_init_dm(void) -{ - return 0; -} - __weak int checkcpu(void) { return 0; @@ -848,7 +843,6 @@ static const init_fnc_t init_sequence_f[] = { arch_cpu_init, /* basic arch cpu dependent setup */ mach_cpu_init, /* SoC/machine dependent CPU setup */ initf_dm, - arch_cpu_init_dm, #if defined(CONFIG_BOARD_EARLY_INIT_F) board_early_init_f, #endif diff --git a/common/event.c b/common/event.c index 4270809d496..9d67a060a02 100644 --- a/common/event.c +++ b/common/event.c @@ -26,6 +26,7 @@ const char *const type_name[] = { "test",
/* Events related to driver model */ + "dm_post_init", "dm_pre_probe", "dm_post_probe", "dm_pre_remove", diff --git a/configs/galileo_defconfig b/configs/galileo_defconfig index eaa90219b24..168898d994c 100644 --- a/configs/galileo_defconfig +++ b/configs/galileo_defconfig @@ -18,6 +18,7 @@ CONFIG_USE_BOOTCOMMAND=y CONFIG_BOOTCOMMAND="ext2load scsi 0:3 01000000 /boot/vmlinuz; zboot 01000000" CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_EVENT=y CONFIG_LAST_STAGE_INIT=y CONFIG_HUSH_PARSER=y CONFIG_CMD_CPU=y diff --git a/drivers/core/root.c b/drivers/core/root.c index 815173f86eb..62f7f7b971a 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -404,6 +404,11 @@ int dm_init_and_scan(bool pre_reloc_only) return ret; } } + if (CONFIG_IS_ENABLED(DM_EVENT)) { + ret = event_notify_null(EVT_DM_POST_INIT); + if (ret) + return log_msg_ret("ev", ret); + }
return 0; } diff --git a/include/event.h b/include/event.h index f1333af7bd4..f2d3639c82a 100644 --- a/include/event.h +++ b/include/event.h @@ -20,6 +20,7 @@ enum event_t { EVT_TEST,
/* Events related to driver model */ + EVT_DM_POST_INIT, EVT_DM_PRE_PROBE, EVT_DM_POST_PROBE, EVT_DM_PRE_REMOVE, diff --git a/include/init.h b/include/init.h index f1c3982319f..52c5606b887 100644 --- a/include/init.h +++ b/include/init.h @@ -42,17 +42,6 @@ void board_init_f(ulong dummy); */ int arch_cpu_init(void);
-/** - * arch_cpu_init_dm() - init CPU after driver model is available - * - * This is called immediately after driver model is available before - * relocation. This is similar to arch_cpu_init() but is able to reference - * devices - * - * Return: 0 if OK, -ve on error - */ -int arch_cpu_init_dm(void); - /** * mach_cpu_init() - SoC/machine dependent CPU setup *

Add a command to show the available events.
Signed-off-by: Simon Glass sjg@chromium.org ---
MAINTAINERS | 1 + cmd/Kconfig | 8 ++++++++ cmd/Makefile | 1 + cmd/event.c | 27 +++++++++++++++++++++++++++ include/event.h | 3 +++ 5 files changed, 40 insertions(+) create mode 100644 cmd/event.c
diff --git a/MAINTAINERS b/MAINTAINERS index bc8d53cf387..117a7d6c985 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -789,6 +789,7 @@ F: scripts/env2string.awk EVENTS M: Simon Glass sjg@chromium.org S: Maintained +F: cmd/event.c F: common/event.c F: include/event.h F: test/common/event.c diff --git a/cmd/Kconfig b/cmd/Kconfig index fd8f0227c89..5d2ef66fe4d 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2320,6 +2320,14 @@ config CMD_DIAG available tests and running either all the tests, or specific tests identified by name.
+config CMD_EVENT + bool "event - Show information about events" + default y if EVENT_DEBUG + help + This enables the 'event' command which provides information about + events and event-handler routines. This can help to device event + hadling. + config CMD_IRQ bool "irq - Show information about interrupts" depends on !ARM && !MIPS && !RISCV && !SH diff --git a/cmd/Makefile b/cmd/Makefile index 891819ae0f6..fde81266646 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_CMD_DIAG) += diag.o endif obj-$(CONFIG_CMD_ADTIMG) += adtimg.o obj-$(CONFIG_CMD_ABOOTIMG) += abootimg.o +obj-$(CONFIG_CMD_EVENT) += event.o obj-$(CONFIG_CMD_EXTENSION) += extension_board.o obj-$(CONFIG_CMD_ECHO) += echo.o obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o diff --git a/cmd/event.c b/cmd/event.c new file mode 100644 index 00000000000..9cac2023530 --- /dev/null +++ b/cmd/event.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Command-line access to events + * + * Copyright 2021 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <common.h> +#include <command.h> +#include <event.h> + +static int do_event_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + event_show_spy_list(); + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char event_help_text[] = + "event list - list event spies"; +#endif + +U_BOOT_CMD_WITH_SUBCMDS(event, "Events", event_help_text, + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_event_list)); diff --git a/include/event.h b/include/event.h index f2d3639c82a..17280335d8c 100644 --- a/include/event.h +++ b/include/event.h @@ -113,6 +113,9 @@ static inline const char *event_spy_id(struct evspy_info *spy) int event_register(const char *id, enum event_t type, event_handler_t func, void *ctx);
+/** event_show_spy_list( - Show a list of event spies */ +void event_show_spy_list(void); + #if CONFIG_IS_ENABLED(EVENT) /** * event_notify() - notify spies about an event

For debugging and dicoverability it is useful to be able to see a list of each event spy in a U-Boot ELF file. Add a script which shows this, along with the event type and the source location. This makes events a little easier to use than weak functions, for example.
Add a basic sandbox test as well. We could provide a test for other boards, but for now, few use events.
Signed-off-by: Simon Glass sjg@chromium.org ---
MAINTAINERS | 2 + scripts/event_dump.py | 117 +++++++++++++++++++++++++++++++ test/py/tests/test_event_dump.py | 19 +++++ 3 files changed, 138 insertions(+) create mode 100755 scripts/event_dump.py create mode 100644 test/py/tests/test_event_dump.py
diff --git a/MAINTAINERS b/MAINTAINERS index 117a7d6c985..c040fb83162 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -792,7 +792,9 @@ S: Maintained F: cmd/event.c F: common/event.c F: include/event.h +F: scripts/event_dump.py F: test/common/event.c +F: test/py/tests/test_event_dump.py
FASTBOOT S: Orphaned diff --git a/scripts/event_dump.py b/scripts/event_dump.py new file mode 100755 index 00000000000..ce006ae31d1 --- /dev/null +++ b/scripts/event_dump.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Google LLC +# Written by Simon Glass sjg@chromium.org + +"""Decode the evspy_info linker list in a U-Boot ELF image""" + +from argparse import ArgumentParser +import os +import re +import struct +import sys + +our_path = os.path.dirname(os.path.realpath(__file__)) +src_path = os.path.dirname(our_path) + +sys.path.insert(1, os.path.join(our_path, '../tools')) + +from binman import elf +from patman import tools + +PREFIX = '_u_boot_list_2_evspy_info_2_' +RE_EVTYPE = re.compile('%s(.*)' % PREFIX) + +def show_sym(fname, data, endian, evtype, sym): + """Show information about an evspy entry + + Args: + fname (str): Filename of ELF file + data (bytes): Data for this symbol + endian (str): Endianness to use ('little', 'big', 'auto') + evtype (str): Event type, e.g. 'MISC_INIT_F' + sym (elf.Symbol): Symbol to show + """ + def _unpack_val(sym_data, offset): + start = offset * func_size + val_data = sym_data[start:start + func_size] + fmt = '%s%s' % ('>' if endian == 'big' else '<', + 'L' if func_size == 4 else 'Q') + val = struct.unpack(fmt, val_data)[0] + return val + + # Get the data, which is a struct evspy_info + sym_data = data[sym.offset:sym.offset + sym.size] + + # Figure out the word size of the struct + func_size = 4 if sym.size < 16 else 8 + + # Read the function name for evspy_info->func + while True: + # Switch to big-endian if we see a failure + func_addr = _unpack_val(sym_data, 0) + func_name = elf.GetSymbolFromAddress(fname, func_addr) + if not func_name and endian == 'auto': + endian = 'big' + else: + break + has_id = sym.size in [12, 24] + if has_id: + # Find the address of evspy_info->id in the ELF + id_addr = _unpack_val(sym_data, 2) + + # Get the file offset for that address + id_ofs = elf.GetFileOffset(fname, id_addr) + + # Read out a nul-terminated string + id_data = data[id_ofs:id_ofs + 80] + pos = id_data.find(0) + if pos: + id_data = id_data[:pos] + id_str = id_data.decode('utf-8') + else: + id_str = None + + # Find the file/line for the function + cmd = ['addr2line', '-e', fname, '%x' % func_addr] + out = tools.Run(*cmd).strip() + + # Drop the full path if it is the current directory + if out.startswith(src_path): + out = out[len(src_path) + 1:] + print('%-20s %-30s %s' % (evtype, id_str or f'f:{func_name}', out)) + +def show_event_spy_list(fname, endian): + """Show a the event-spy- list from a U-Boot image + + Args: + fname (str): Filename of ELF file + endian (str): Endianness to use ('little', 'big', 'auto') + """ + syms = elf.GetSymbolFileOffset(fname, [PREFIX]) + data = tools.ReadFile(fname) + print('%-20s %-30s %s' % ('Event type', 'Id', 'Source location')) + print('%-20s %-30s %s' % ('-' * 20, '-' * 30, '-' * 30)) + for name, sym in syms.items(): + m_evtype = RE_EVTYPE.search(name) + evtype = m_evtype .group(1) + show_sym(fname, data, endian, evtype, sym) + +def main(argv): + """Main program + + Args: + argv (list of str): List of program arguments, excluding arvg[0] + """ + epilog = 'Show a list of even spies in a U-Boot EFL file' + parser = ArgumentParser(epilog=epilog) + parser.add_argument('elf', type=str, help='ELF file to decode') + parser.add_argument('-e', '--endian', type=str, default='auto', + help='Big-endian image') + parser.add_argument('-t', '--test', action='store_true', + help='Big-endian image') + args = parser.parse_args(argv) + show_event_spy_list(args.elf, args.endian) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/test/py/tests/test_event_dump.py b/test/py/tests/test_event_dump.py new file mode 100644 index 00000000000..6a5ea42a60a --- /dev/null +++ b/test/py/tests/test_event_dump.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass sjg@chromium.org + +import pytest +import u_boot_utils as util + +# This is only a partial test - coverting 64-bit sandbox. It does not test +# big-endian images, nor 32-bit images +@pytest.mark.boardspec('sandbox') +def test_event_dump(u_boot_console): + """Test that the "help" command can be executed.""" + cons = u_boot_console + sandbox = cons.config.build_dir + '/u-boot' + out = util.run_and_log(cons, ['scripts/event_dump.py', sandbox]) + expect = '''Event type Id Source location +-------------------- ------------------------------ ------------------------------ +EVT_MISC_INIT_F sandbox_misc_init_f arch/sandbox/cpu/start.c:''' + assert out[:len(expect)] == expect

Add documentation for events, including the event command.
Signed-off-by: Simon Glass sjg@chromium.org ---
doc/develop/event.rst | 100 ++++++++++++++++++++++++++++++++++++++++++ doc/develop/index.rst | 1 + doc/usage/event.rst | 49 +++++++++++++++++++++ doc/usage/index.rst | 1 + 4 files changed, 151 insertions(+) create mode 100644 doc/develop/event.rst create mode 100644 doc/usage/event.rst
diff --git a/doc/develop/event.rst b/doc/develop/event.rst new file mode 100644 index 00000000000..1069d247aa0 --- /dev/null +++ b/doc/develop/event.rst @@ -0,0 +1,100 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Events +====== + +U-Boot supports a way for various events to be handled by interested +subsystems. This provide a generic way to handle 'hooks' like setting up the +CPUs after driver model is active, or reading a partition table after a new +block device is probed. + +Rather than using weak functions and direct calls across subsystemss, it is +often easier to use an event. + +An event consists of a type (e.g. EVT_DM_POST_INIT) and some optional data, +in `union event_data`. An event spy can be creasted to watch for events of a +particular type. When the event is created, it is sent to each spy in turn. + + +Declaring a spy +--------------- + +To declare a spy, use something like this:: + + static int snow_setup_cpus(void *ctx, struct event *event) + { + /* do something */ + return 0; + } + EVENT_SPY(EVT_DM_POST_INIT, snow_setup_cpus); + +Your function is then called when `EVT_DM_POST_INIT` is emitted, i.e. after +driver model is inited (in SPL, or in U-Boot proper before and after +relocation). + +The `ctx` value is intended to pass context information to the handler. At +present this is not used by EVENT_SPY(). The `event` struction holds more +information about the event, if any. + +Each spy declaration uses at least two 32- or 64-bit words, depending on the +architecture. If `CONFIG_EVENT_DEBUG` is enabled, an extra word is used. +Enabling events uses a little over 200 bytes of extra code space, on 32-bit +machines. + +Logging +------- + +Logging of spy invocation is available through the `LOGC_EVENT` log category. +For quick one-off debugging, add this to the very top of `common/event.c`:: + + #define LOG_DEBUG + +Debugging +--------- + +To assist with debugging events, enable `CONFIG_EVENT_DEBUG` and +`CONFIG_CMD_EVENT`. The :doc:`../usage/event` command can then be used to +provide a spy list. + +It is also possible to list spy information from the U-Boot executable,, using +the `event_dump.py` script:: + + $ scripts/event_dump.py /tmp/b/sandbox/u-boot + Event type Id Source location + -------------------- ------------------------------ ------------------------------ + EVT_MISC_INIT_F f:sandbox_misc_init_f arch/sandbox/cpu/start.c:125 + +This shows each event spy in U-Boot, along with the event type, function name +(or ID) and source location. + +Note that if `CONFIG_EVENT_DEBUG` is not enabled, the event ID is not stored, +to save space. In this case the function is shown instead (with an `f:` +prefix as above). Since the ID is generally the same as the function name, +this does not matter much. + +The event type is decoded by the symbol used by U-Boot for the event linker +list. Symbols have the form:: + + _u_boot_list_2_evspy_info_2_EVT_MISC_INIT_F + +so the event type can be read from the end. To manually list spy information +in an image, use $(CROSS_COMPILE)nm:: + + nm u-boot |grep evspy |grep list + 00000000002d6300 D _u_boot_list_2_evspy_info_2_EVT_MISC_INIT_F + +Some attempt is made to use the word `spy` everywhere, so make the code easier +to find in U-Boot, which does not use this word otherwise. The plural of `spy` +is not used, for similar reasons. + +To do +----- + +Some ideas: + +- Consider putting the event context inside `struct event` +- Allow an event-spy function to have no arguments, to save code space +- Convert more of the init hooks to use events +- Use events for `ft_board_setup()` and the like +- Add event-spy flags for things like before/after relocation +- Support event-spy priority diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 9592d193fca..9cbe3f87bdf 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -17,6 +17,7 @@ Implementation distro driver-model/index environment + event global_data logging makefiles diff --git a/doc/usage/event.rst b/doc/usage/event.rst new file mode 100644 index 00000000000..c0f8acd727b --- /dev/null +++ b/doc/usage/event.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +event command +============= + +Synopsis +-------- + +:: + + event list + +Description +----------- + +The event command provides spy list. + +This shows the following information: + +Seq + Sequence number of the spy, numbered from 0 + +Type + Type of the spy, both as a number and a label. If `CONFIG_EVENT_DEBUG` is + not enabled, the label just shows `(unknown)`. + +Function + Address of the function to call + +ID + ID string for this event, if `CONFIG_EVENT_DEBUG` is enabled. Otherwise this + just shows `?`. + + +See :doc:`../develop/event` for more information on events. + +Example +------- + +:: + + => event list + Seq Type Function ID + 0 7 misc_init_f 55a070517c68 ? + +Configuration +------------- + +The event command is only available if CONFIG_CMD_EVENT=y. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 33761af96af..97c3ec2e039 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -28,6 +28,7 @@ Shell commands x86/cbsysinfo conitrace echo + event exception extension exit
participants (1)
-
Simon Glass