/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef BOOTSTATE_H #define BOOTSTATE_H #include <assert.h> #include <stddef.h> /* Only declare main() when in ramstage. */ #if ENV_RAMSTAGE #include <main_decl.h> #endif /* * The boot state machine provides a mechanism for calls to be made through- * out the main boot process. The boot process is separated into discrete * states. Upon a state's entry and exit and callbacks can be made. For * example: * * Enter State * + * | * V * +-----------------+ * | Entry callbacks | * +-----------------+ * | State Actions | * +-----------------+ * | Exit callbacks | * +-------+---------+ * | * V * Next State * * Below is the current flow from top to bottom: * * start * | * BS_PRE_DEVICE * | * BS_DEV_INIT_CHIPS * | * BS_DEV_ENUMERATE * | * BS_DEV_RESOURCES * | * BS_DEV_ENABLE * | * BS_DEV_INIT * | * BS_POST_DEVICE * | * BS_OS_RESUME_CHECK -------- BS_OS_RESUME * | | * BS_WRITE_TABLES OS handoff * | * BS_PAYLOAD_LOAD * | * BS_PAYLOAD_BOOT * | * payload run * * Brief description of states: * BS_PRE_DEVICE - before any device tree actions take place * BS_DEV_INIT_CHIPS - init all chips in device tree * BS_DEV_ENUMERATE - device tree probing * BS_DEV_RESOURCES - device tree resource allocation and assignment * BS_DEV_ENABLE - device tree enabling/disabling of devices * BS_DEV_INIT - device tree device initialization * BS_POST_DEVICE - all device tree actions performed * BS_OS_RESUME_CHECK - check for OS resume * BS_OS_RESUME - resume to OS * BS_WRITE_TABLES - write coreboot tables * BS_PAYLOAD_LOAD - Load payload into memory * BS_PAYLOAD_BOOT - Boot to payload */ typedef enum { BS_PRE_DEVICE, BS_DEV_INIT_CHIPS, BS_DEV_ENUMERATE, BS_DEV_RESOURCES, BS_DEV_ENABLE, BS_DEV_INIT, BS_POST_DEVICE, BS_OS_RESUME_CHECK, BS_OS_RESUME, BS_WRITE_TABLES, BS_PAYLOAD_LOAD, BS_PAYLOAD_BOOT, } boot_state_t; /* The boot_state_sequence_t describes when a callback is to be made. It is * called either before a state is entered or when a state is exited. */ typedef enum { BS_ON_ENTRY, BS_ON_EXIT } boot_state_sequence_t; struct boot_state_callback { void *arg; void (*callback)(void *arg); /* For use internal to the boot state machine. */ struct boot_state_callback *next; #if CONFIG(DEBUG_BOOT_STATE) const char *location; #endif }; static inline const char *bscb_location(const struct boot_state_callback *bscb) { #if CONFIG(DEBUG_BOOT_STATE) return bscb->location; #else return dead_code_t(const char *); #endif } #if CONFIG(DEBUG_BOOT_STATE) #define BOOT_STATE_CALLBACK_LOC __FILE__ ":" STRINGIFY(__LINE__) #define BOOT_STATE_CALLBACK_INIT_DEBUG .location = BOOT_STATE_CALLBACK_LOC, #define INIT_BOOT_STATE_CALLBACK_DEBUG(bscb_) \ do { \ bscb_->location = BOOT_STATE_CALLBACK_LOC; \ } while (0) #else #define BOOT_STATE_CALLBACK_INIT_DEBUG #define INIT_BOOT_STATE_CALLBACK_DEBUG(bscb_) #endif #define BOOT_STATE_CALLBACK_INIT(func_, arg_) \ { \ .arg = arg_, \ .callback = func_, \ .next = NULL, \ BOOT_STATE_CALLBACK_INIT_DEBUG \ } #define BOOT_STATE_CALLBACK(name_, func_, arg_) \ struct boot_state_callback name_ = BOOT_STATE_CALLBACK_INIT(func_, arg_) /* Initialize an allocated boot_state_callback. */ #define INIT_BOOT_STATE_CALLBACK(bscb_, func_, arg_) \ do { \ INIT_BOOT_STATE_CALLBACK_DEBUG(bscb_) \ bscb_->callback = func_; \ bscb_->arg = arg_ \ } while (0) /* The following 2 functions schedule a callback to be called on entry/exit * to a given state. Note that there are no ordering guarantees between the * individual callbacks on a given state. 0 is returned on success < 0 on * error. */ int boot_state_sched_on_entry(struct boot_state_callback *bscb, boot_state_t state); int boot_state_sched_on_exit(struct boot_state_callback *bscb, boot_state_t state); /* Schedule an array of entries of size num. */ struct boot_state_init_entry; void boot_state_sched_entries(struct boot_state_init_entry *entries, size_t num); /* Block/Unblock the (state, seq) pair from transitioning. Returns 0 on * success < 0 when the phase of the (state,seq) has already ran. */ int boot_state_block(boot_state_t state, boot_state_sequence_t seq); int boot_state_unblock(boot_state_t state, boot_state_sequence_t seq); /* In order to schedule boot state callbacks at compile-time specify the * entries in an array using the BOOT_STATE_INIT_ENTRIES and * BOOT_STATE_INIT_ENTRY macros below. */ struct boot_state_init_entry { boot_state_t state; boot_state_sequence_t when; struct boot_state_callback bscb; }; #if ENV_RAMSTAGE #define BOOT_STATE_INIT_ATTR __attribute__((used, section(".bs_init"))) #else #define BOOT_STATE_INIT_ATTR __attribute__((unused)) #endif #define BOOT_STATE_INIT_ENTRY(state_, when_, func_, arg_) \ static struct boot_state_init_entry func_ ##_## state_ ##_## when_ = \ { \ .state = state_, \ .when = when_, \ .bscb = BOOT_STATE_CALLBACK_INIT(func_, arg_), \ }; \ static struct boot_state_init_entry * \ bsie_ ## func_ ##_## state_ ##_## when_ BOOT_STATE_INIT_ATTR = \ &func_ ##_## state_ ##_## when_; \ _Static_assert(!(state_ == BS_PAYLOAD_BOOT && when_ == BS_ON_EXIT), \ "Invalid bootstate hook"); \ _Static_assert(!(state_ == BS_OS_RESUME && when_ == BS_ON_EXIT), \ "Invalid bootstate hook"); /* Hook per arch when coreboot is exiting to payload or ACPI OS resume. It's * the very last thing done before the transition. */ void arch_bootstate_coreboot_exit(void); #endif /* BOOTSTATE_H */