Merge branch '2022-03-09-events-subsystem' into next

To quote the author:
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.
This commit is contained in:
Tom Rini 2022-03-10 08:28:40 -05:00
commit cb83a7a0c3
67 changed files with 1069 additions and 74 deletions

View file

@ -809,6 +809,16 @@ S: Maintained
F: doc/usage/environment.rst
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: scripts/event_dump.py
F: test/common/event.c
F: test/py/tests/test_event_dump.py
FASTBOOT
S: Orphaned
F: cmd/fastboot.c

View file

@ -94,6 +94,7 @@ config NIOS2
bool "Nios II architecture"
select CPU
select DM
imply DM_EVENT
select OF_CONTROL
select SUPPORT_OF_CONTROL
imply CMD_DM
@ -113,6 +114,7 @@ config RISCV
select DM
imply DM_SERIAL
imply DM_ETH
imply DM_EVENT
imply DM_MMC
imply DM_SPI
imply DM_SPI_FLASH
@ -238,6 +240,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

View file

@ -774,6 +774,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"
@ -818,6 +819,7 @@ config ARCH_IMX8
select MACH_IMX
select OF_CONTROL
select ENABLE_ARM_SOC_BOOT0_HOOK
imply DM_EVENT
config ARCH_IMX8M
bool "NXP i.MX8M platform"
@ -831,6 +833,7 @@ config ARCH_IMX8M
select DM
select SUPPORT_SPL
imply CMD_DM
imply DM_EVENT
config ARCH_IMX8ULP
bool "NXP i.MX8ULP platform"
@ -841,6 +844,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"

View file

@ -8,6 +8,7 @@
#include <cpu.h>
#include <cpu_func.h>
#include <dm.h>
#include <event.h>
#include <init.h>
#include <log.h>
#include <asm/cache.h>
@ -66,7 +67,7 @@ int arch_cpu_init(void)
return 0;
}
int arch_cpu_init_dm(void)
static int imx8_init_mu(void *ctx, struct event *event)
{
struct udevice *devp;
int node, ret;
@ -88,6 +89,7 @@ int arch_cpu_init_dm(void)
return 0;
}
EVENT_SPY(EVT_DM_POST_INIT, imx8_init_mu);
int print_bootinfo(void)
{

View file

@ -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>
@ -494,7 +495,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;
@ -511,6 +512,7 @@ int arch_cpu_init_dm(void)
return 0;
}
EVENT_SPY(EVT_DM_POST_INIT, imx8m_check_clock);
int arch_cpu_init(void)
{

View file

@ -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>
@ -569,7 +570,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;
@ -584,6 +585,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)

View file

@ -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,7 +597,7 @@ 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)
@ -604,3 +605,4 @@ int arch_cpu_init_dm(void)
#endif
return 0;
}
EVENT_SPY(EVT_DM_POST_INIT, am33xx_dm_post_init);

View file

@ -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)
static 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

View file

@ -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

View file

@ -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)
static 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)

View file

@ -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)

View file

@ -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)
{

View file

@ -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 */

View file

@ -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();

View file

@ -4,14 +4,14 @@
*/
#include <common.h>
#include <cli.h>
#include <command.h>
#include <dm/root.h>
#include <efi_loader.h>
#include <errno.h>
#include <event.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 +19,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;
@ -119,10 +120,11 @@ int sandbox_early_getopt_check(void)
os_exit(0);
}
int misc_init_f(void)
static 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)
{

View file

@ -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)
{

View file

@ -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)
{

View file

@ -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

View file

@ -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_pcie(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_pcie);
int checkcpu(void)
{

View file

@ -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

View file

@ -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);

View file

@ -17,6 +17,7 @@
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/cpu_common.h>
#include <asm/fsp2/fsp_api.h>
#include <asm/global_data.h>
#include <asm/mrccache.h>
#include <asm/mtrr.h>
@ -27,7 +28,7 @@
DECLARE_GLOBAL_DATA_PTR;
__weak int arch_cpu_init_dm(void)
__weak int fsp_setup_pinctrl(void *ctx, struct event *event)
{
return 0;
}
@ -89,7 +90,7 @@ static int x86_spl_init(void)
return ret;
}
#ifndef CONFIG_TPL
ret = arch_cpu_init_dm();
ret = fsp_setup_pinctrl(NULL, NULL);
if (ret) {
debug("%s: arch_cpu_init_dm() failed\n", __func__);
return ret;

View file

@ -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;

View file

@ -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,11 +33,12 @@ 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()) {
printf("Running as secondary loader");
if (gd->arch.coreboot_table) {
if (CONFIG_IS_ENABLED(COREBOOT_SYSINFO) &&
gd->arch.coreboot_table) {
int ret;
printf(" (found coreboot table at %lx)",
@ -55,6 +57,7 @@ int misc_init_f(void)
return 0;
}
EVENT_SPY(EVT_MISC_INIT_F, coral_check_ll_boot);
int arch_misc_init(void)
{

View file

@ -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>
@ -181,7 +182,7 @@ unsigned long get_serial_clock(unsigned long dummy)
return (gd->bus_clk / 2);
}
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();
@ -209,6 +210,7 @@ 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

View file

@ -4,6 +4,7 @@
*/
#include <common.h>
#include <event.h>
#include <i2c.h>
#include <asm/io.h>
#include <asm/arch/immap_ls102xa.h>
@ -109,12 +110,14 @@ int board_early_init_f(void)
return 0;
}
int misc_init_f(void)
static int pg_wcom_misc_init_f(void *ctx, struct event *event)
{
if (IS_ENABLED(CONFIG_PG_WCOM_UBOOT_UPDATE_SUPPORTED))
check_for_uboot_update();
return 0;
}
EVENT_SPY(EVT_MISC_INIT_F, pg_wcom_misc_init_f);
int board_init(void)
{

View file

@ -2367,6 +2367,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

View file

@ -53,6 +53,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

27
cmd/event.c Normal file
View file

@ -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));

View file

@ -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
@ -558,12 +589,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

View file

@ -89,6 +89,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

View file

@ -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>
@ -802,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;
@ -817,6 +813,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
@ -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 */
event_init,
#ifdef CONFIG_BLOBLIST
bloblist_init,
#endif
@ -841,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
@ -875,9 +876,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,

View file

@ -594,6 +594,7 @@ static int run_main_loop(void)
static init_fnc_t init_sequence_r[] = {
initr_trace,
initr_reloc,
event_init,
/* TODO: could x86/PPC have this also perhaps? */
#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
initr_caches,

196
common/event.c Normal file
View file

@ -0,0 +1,196 @@
// 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",
/* Events related to driver model */
"dm_post_init",
"dm_pre_probe",
"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");
#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 */

View file

@ -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,

View file

@ -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

View file

@ -16,10 +16,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

View file

@ -35,6 +35,7 @@ CONFIG_AUTOBOOT_STOP_STR=" "
CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 root=/dev/ram0"
CONFIG_SILENT_CONSOLE=y
CONFIG_EVENT=y
CONFIG_LAST_STAGE_INIT=y
CONFIG_MISC_INIT_R=y
CONFIG_CMD_IMLS=y

View file

@ -33,6 +33,7 @@ CONFIG_AUTOBOOT_STOP_STR=" "
CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 root=/dev/ram0"
CONFIG_SILENT_CONSOLE=y
CONFIG_EVENT=y
CONFIG_LAST_STAGE_INIT=y
CONFIG_MISC_INIT_R=y
CONFIG_CMD_IMLS=y

View file

@ -35,6 +35,7 @@ CONFIG_AUTOBOOT_STOP_STR=" "
CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 root=/dev/ram0"
CONFIG_SILENT_CONSOLE=y
CONFIG_EVENT=y
CONFIG_LAST_STAGE_INIT=y
CONFIG_MISC_INIT_R=y
CONFIG_CMD_IMLS=y

View file

@ -33,6 +33,7 @@ CONFIG_AUTOBOOT_STOP_STR=" "
CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 root=/dev/ram0"
CONFIG_SILENT_CONSOLE=y
CONFIG_EVENT=y
CONFIG_LAST_STAGE_INIT=y
CONFIG_MISC_INIT_R=y
CONFIG_CMD_IMLS=y

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -9,7 +9,6 @@ CONFIG_TIMESTAMP=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

66
doc/develop/event.rst Normal file
View file

@ -0,0 +1,66 @@
.. 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 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).
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 missing, so
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

View file

@ -17,6 +17,7 @@ Implementation
distro
driver-model/index
environment
event
global_data
logging
makefiles

49
doc/usage/event.rst Normal file
View file

@ -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.

View file

@ -29,6 +29,7 @@ Shell commands
x86/cbsysinfo
conitrace
echo
event
exception
extension
exit

View file

@ -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

View file

@ -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:

View file

@ -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)) {

View file

@ -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;
}

View file

@ -20,6 +20,7 @@
*/
#ifndef __ASSEMBLY__
#include <event_internal.h>
#include <fdtdec.h>
#include <membuff.h>
#include <linux/list.h>
@ -467,6 +468,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);
@ -532,6 +539,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
*

View file

@ -272,6 +272,4 @@
#define CONFIG_SYS_BOOTM_LEN (64 << 20) /* Increase max gunzip size */
#define CONFIG_SYS_BOOTMAPSZ (256 << 20) /* Increase map for Linux */
#define CONFIG_MISC_INIT_F
#endif

View file

@ -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

210
include/event.h Normal file
View file

@ -0,0 +1,210 @@
/* 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,
/* Events related to driver model */
EVT_DM_POST_INIT,
EVT_DM_PRE_PROBE,
EVT_DM_POST_PROBE,
EVT_DM_PRE_REMOVE,
EVT_DM_POST_REMOVE,
/* Init hooks */
EVT_MISC_INIT_F,
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_dm - driver model event
*
* @dev: Device this event relates to
*/
struct event_dm {
struct udevice *dev;
} dm;
};
/**
* 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
}
/*
* It seems that LTO will drop list entries if it decides they are not used,
* although the conditions that cause this are unclear.
*
* The example found is the following:
*
* static 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);
*
* where EVENT_SPY uses ll_entry_declare()
*
* In this case, LTO decides to drop the sandbox_misc_init_f() function
* (which is fine) but then drops the linker-list entry too. This means
* that the code no longer works, in this case sandbox no-longer checks its
* command-line arguments properly.
*
* Without LTO, the KEEP() command in the .lds file is enough to keep the
* entry around. But with LTO it seems that the entry has already been
* dropped before the link script is considered.
*
* The only solution I can think of is to mark linker-list entries as 'used'
* using an attribute. This should be safe, since we don't actually want to drop
* any of these. However this does slightly limit LTO's optimisation choices.
*/
#define EVENT_SPY(_type, _func) \
static __attribute__((used)) 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);
/** 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
*
* 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

35
include/event_internal.h Normal file
View file

@ -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

View file

@ -45,17 +45,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
*
@ -217,7 +206,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);

View file

@ -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 */

115
scripts/event_dump.py Executable file
View file

@ -0,0 +1,115 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0+
"""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.read_file(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:])

View file

@ -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

85
test/common/event.c Normal file
View file

@ -0,0 +1,85 @@
// 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);
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);

View file

@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2021 Google LLC
# Written by Simon Glass <sjg@chromium.org>
import pytest
import re
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 re.match(expect, out, re.MULTILINE) is not None

View file

@ -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;
}

View file

@ -85,6 +85,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
@ -97,13 +148,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')