Martin L Roth has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/87189?usp=email )
Change subject: Documentation: Add Ramstage Bootstates ......................................................................
Documentation: Add Ramstage Bootstates
Change-Id: I18801967be50e2f318b4404d08c171ffa7e92bbc Signed-off-by: Martin Roth gaumless@gmail.com --- M Documentation/internals/index.md A Documentation/internals/ramstage_bootstates.md 2 files changed, 515 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/89/87189/1
diff --git a/Documentation/internals/index.md b/Documentation/internals/index.md index 4b4d011..a4792bb 100644 --- a/Documentation/internals/index.md +++ b/Documentation/internals/index.md @@ -18,6 +18,7 @@ ```{toctree} :maxdepth: 1
+Ramstage Bootstates & Bootstate Callbacks <ramstage_bootstates.md> Timestamps <timestamps.md> Timers, Stopwatch, and delays <stopwatch.md> Threads <threads.md> diff --git a/Documentation/internals/ramstage_bootstates.md b/Documentation/internals/ramstage_bootstates.md new file mode 100644 index 0000000..e77ecf2 --- /dev/null +++ b/Documentation/internals/ramstage_bootstates.md @@ -0,0 +1,514 @@ +# coreboot Ramstage Bootstates & Bootstate Callbacks + +## Introduction + +The coreboot boot process is divided into several discrete phases, one +of which is **ramstage**. Ramstage is the phase where the main hardware +initialization and device setup occurs after memory initialization. +Within ramstage, a state machine called the **bootstate machine** +manages the sequence of operations needed to initialize the system, +configure devices, and prepare to load and execute the payload (such as +a bootloader, operating system, or firmware utility). + +The bootstate machine provides a structured and extensible way to +organize code execution during the boot process. It allows for clear +separation of concerns between different initialization phases and +provides hooks for component-specific code to run at well-defined +points. + +**Important Note:** The exact execution order of multiple callbacks +registered for the same state and sequence (entry/exit) is not +guaranteed. This means that you cannot depend on one call for the +state/sequence in any other calls to the same state/sequence. If this +ordering is required, join the calls to the two functions into a single +function which specifies the order and create a callback to call the +top-level function instead of the two individual callbacks. + + +## Bootstate Machine Architecture + +The bootstate machine's public API is defined in +`src/include/bootstate.h`, and its core implementation resides in +`src/lib/hardwaremain.c`. At its core, it consists of: + +1. A series of sequential states that represent phases of the boot process +2. A mechanism for callback registration to execute code during state transitions +3. A framework for blocking and unblocking state transitions +4. Timing and debugging facilities to measure and report performance during boot + + +### Key Data Structures + +The primary public data structure for interacting with the bootstate +machine is `struct boot_state_callback`. The internal implementation +also uses `struct boot_state` and `struct boot_phase`. + + +#### Boot State Callback (Public API) + +Callbacks that run during state transitions are defined by this +structure in `src/include/bootstate.h`: + +```c +struct boot_state_callback { + void *arg; // Argument to pass to the callback + void (*callback)(void *arg); // Function pointer to the callback + struct boot_state_callback *next; // Next callback in linked list (internal use) +#if CONFIG(DEBUG_BOOT_STATE) + const char *location; // Source location for debugging +#endif +}; +``` + +#### Boot State Sequence (Public API) + +The boot state sequence type, defined in `src/include/bootstate.h`, +specifies when a callback should run relative to the state's main +action: + +```c +typedef enum { + BS_ON_ENTRY, // Execute before state function + BS_ON_EXIT // Execute after state function +} boot_state_sequence_t; +``` + + +#### Boot State (Internal Implementation) + +The main internal data structure in `src/lib/hardwaremain.c` is +`struct boot_state`, which defines a single state in the bootstate +machine: + +```c +struct boot_state { + const char *name; // Human-readable name of the state + boot_state_t id; // Enumerated identifier for the state + u8 post_code; // POST code to output during state execution + struct boot_phase phases[2]; // Entry and exit phases (internal use) + boot_state_t (*run_state)(void *arg); // Function to execute during the state + void *arg; // Argument to pass to the run_state function + int num_samples; // Counter for timing samples (internal use) + bool complete; // Flag indicating if state has completed (internal use) +}; +``` + + +#### Boot Phase (Internal Implementation) + +Each boot state has two internal phases ("entry" and "exit") represented +by `struct boot_phase` in `src/lib/hardwaremain.c`: + +```c +struct boot_phase { + struct boot_state_callback *callbacks; // Linked list of callbacks + int blockers; // Counter for blocking state transition +}; +``` + +## Bootstate Sequence + +The bootstate machine defines the following sequence of states, executed +in order by the `bs_walk_state_machine` function in +`src/lib/hardwaremain.c`. The sequence is defined by the `boot_state_t` +enum in `src/include/bootstate.h`: + +1. **BS_PRE_DEVICE**: Initial state before any device operations begin +2. **BS_DEV_INIT_CHIPS**: Early chip initialization for critical components +3. **BS_DEV_ENUMERATE**: Device enumeration (discovering devices on buses) +4. **BS_DEV_RESOURCES**: Resource allocation for devices +5. **BS_DEV_ENABLE**: Enabling devices that were discovered +6. **BS_DEV_INIT**: Device initialization +7. **BS_POST_DEVICE**: All device operations have been completed +8. **BS_OS_RESUME_CHECK**: Check if we're resuming from a sleep state +9. **BS_OS_RESUME**: Handle OS resume process (if needed) +10. **BS_WRITE_TABLES**: Write system tables (e.g., ACPI, SMBIOS) +11. **BS_PAYLOAD_LOAD**: Load the payload into memory +12. **BS_PAYLOAD_BOOT**: Boot the payload + +This sequence forms the backbone of the ramstage execution flow. Each +state performs a specific task, runs associated callbacks, and +transitions to the next state upon completion, unless blocked. + + +## Bootstate Details + +### BS_PRE_DEVICE + +**Purpose**: Serves as the initial state before any device tree +operations begin. + +**Key Functions**: +- `bs_pre_device()`: Sets up initial environment and transitions to next + state. + +**Usage**: This state is used for initializing core components that need +to be set up before any device operations. Examples include: +- Setting up global NVRAM variables +- Initializing debugging facilities +- Preparing ACPI tables or other critical system structures + + +### BS_DEV_INIT_CHIPS + +**Purpose**: Initializes critical chips early in the boot process. + +**Key Functions**: +- `bs_dev_init_chips()`: Calls `dev_initialize_chips()` to initialize + all chips in the device tree. + +**Notes**: Chip initialization can disable unused devices, which is why +it happens before device enumeration. + + +### BS_DEV_ENUMERATE + +**Purpose**: Discovers devices in the system. + +**Key Functions**: +- `bs_dev_enumerate()`: Calls `dev_enumerate()` to probe and identify + devices. + +**Notes**: During this phase, the system scans buses and detects +connected devices. + + +### BS_DEV_RESOURCES + +**Purpose**: Allocates and assigns resources (I/O, memory, IRQs) to +devices. + +**Key Functions**: +- `bs_dev_resources()`: Calls `dev_configure()` to compute and assign + bus resources. + +**Notes**: Resource allocation resolves conflicts and ensures each +device has the resources it needs. + + +### BS_DEV_ENABLE + +**Purpose**: Enables devices in the system. + +**Key Functions**: +- `bs_dev_enable()`: Calls `dev_enable()` to enable devices on the bus. + +**Notes**: Some devices may be selectively disabled based on hardware +configuration or policy. + + +### BS_DEV_INIT + +**Purpose**: Initializes enabled devices. + +**Key Functions**: +- `bs_dev_init()`: Calls `dev_initialize()` to initialize devices on the + bus. + +**Notes**: This state performs device-specific initialization routines +for all enabled devices. + + +### BS_POST_DEVICE + +**Purpose**: Final state after all device operations have completed. + +**Key Functions**: +- `bs_post_device()`: Calls `dev_finalize()` to complete any final + device operations. + +**Notes**: This state serves as a checkpoint that all device +initialization is complete. + + +### BS_OS_RESUME_CHECK + +**Purpose**: Checks if the system should resume from a sleep state. + +**Key Functions**: +- `bs_os_resume_check()`: Looks for a wake vector to determine if resume + is needed. + +**Notes**: This state branches the boot flow based on whether the system +is resuming from a sleep state. + +### BS_OS_RESUME + +**Purpose**: Handles the OS resume process. + +**Key Functions**: +- `bs_os_resume()`: Calls `acpi_resume()` with the wake vector to resume + the OS. + +**Notes**: After successful resume, control is transferred to the OS and +does not return to coreboot. + + +### BS_WRITE_TABLES + +**Purpose**: Writes configuration tables for the payload or OS. + +**Key Functions**: +- `bs_write_tables()`: Calls `write_tables()` to generate system tables. + +**Notes**: Tables include ACPI, SMBIOS, and other system configuration +data. + + +### BS_PAYLOAD_LOAD + +**Purpose**: Loads the payload into memory. + +**Key Functions**: +- `bs_payload_load()`: Calls `payload_load()` to load the payload. + +**Notes**: The payload could be a bootloader, an operating system kernel, +or a firmware utility. + + +### BS_PAYLOAD_BOOT + +**Purpose**: Final state that boots the loaded payload. + +**Key Functions**: +- `bs_payload_boot()`: Calls `payload_run()` to execute the payload. + +**Notes**: After successful execution, control is transferred to the +payload and does not return to coreboot. If execution returns (which +indicates an error), a boot failure message is printed. + + +## Driving the State Machine + +The state machine is driven by the `main()` function in +`src/lib/hardwaremain.c`. After initial setup (like initializing the +console and CBMEM), it calls `bs_walk_state_machine()`. + +`bs_walk_state_machine()` loops through the defined boot states: +1. It identifies the current state. +2. Runs all `BS_ON_ENTRY` callbacks for that state. +3. Executes the state's specific function (e.g., `bs_dev_enumerate()`). +4. Runs all `BS_ON_EXIT` callbacks for that state. +5. Transitions to the next state returned by the state function. + +This loop continues until the final state (`BS_PAYLOAD_BOOT` or +`BS_OS_RESUME`) transfers control away from coreboot. + + +## External Functions (Public API) + +The bootstate machine provides several functions in +`src/include/bootstate.h` for interacting with states: + + +### Callback Registration + +```c +int boot_state_sched_on_entry(struct boot_state_callback *bscb, boot_state_t state_id); +``` +Schedules a callback to run when entering a state (`BS_ON_ENTRY`). + +```c +int boot_state_sched_on_exit(struct boot_state_callback *bscb, boot_state_t state_id); +``` +Schedules a callback to run when exiting a state (`BS_ON_EXIT`). + + +### State Transition Control + +```c +int boot_state_block(boot_state_t state, boot_state_sequence_t seq); +``` +Blocks a state transition from occurring after the specified sequence +(entry or exit callbacks). The transition will pause until the block is +removed. + +```c +int boot_state_unblock(boot_state_t state, boot_state_sequence_t seq); +``` +Removes a previously set block on a state transition. + + +### Static Callback Registration + +For registering callbacks at compile time, use the `BOOT_STATE_INIT_ENTRY` +macro defined in `src/include/bootstate.h`: + +```c +BOOT_STATE_INIT_ENTRY(state, when, func, arg) +``` + +This macro creates a static entry in a special section (`.bs_init`) of +the binary. These entries are processed early in `main()` by +`boot_state_schedule_static_entries()` to register the callbacks before +the state machine starts running. + + +## Configuration Options + +The bootstate machine behavior can be modified through Kconfig options: + + +### DEBUG_BOOT_STATE + +``` +config DEBUG_BOOT_STATE + bool "Debug boot state machine" + default n + help + Control debugging of the boot state machine. When selected displays + the state boundaries in ramstage. +``` + +When enabled, this option causes the bootstate machine to output +debugging information via `printk`, including: +- State transition notifications (`Entering/Exiting <state> state.`) +- Callback execution details (address, source location, execution time) +- Timing information for state execution phases (entry, run, exit) + + +## Examples + +### Adding a New Bootstate Callback + +To register a function to be called when entering a specific state using +the static registration method: + +```c +// Function to be called +static void my_init_function(void *arg) +{ + // Initialization code + printk(BIOS_DEBUG, "My initialization running...\n"); +} + +// Register the callback at compile time +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, my_init_function, NULL); +``` + + +### Runtime Callback Registration + +For dynamic callback registration during runtime (e.g., within another +callback or state function): + +```c +static void runtime_init(void *arg) +{ + // Do something +} + +void register_my_callbacks(void) +{ + // Allocate or define a static callback structure + static struct boot_state_callback bscb = { + .callback = runtime_init, + .arg = NULL, + // .location is automatically handled if DEBUG_BOOT_STATE=y + }; + + // Schedule it + boot_state_sched_on_entry(&bscb, BS_DEV_ENABLE); +} +``` + + +### Blocking State Transition + +To temporarily block a state from progressing until a condition is met, +often used with timers: + +```c +#include <timer.h> // Required for timer functions + +static void wait_for_device(void *arg) +{ + if (!device_is_ready()) { + // Block the transition *after* BS_DEV_INIT exits + boot_state_block(BS_DEV_INIT, BS_ON_EXIT); + + // Schedule a function to check again later (e.g., after 100us) + // Assume schedule_timer exists and works appropriately + schedule_timer(check_device_ready, NULL, 100); + } +} + +static void check_device_ready(void *arg) +{ + if (device_is_ready()) { + // Device is ready, unblock the transition + boot_state_unblock(BS_DEV_INIT, BS_ON_EXIT); + } else { + // Still not ready, check again later + schedule_timer(check_device_ready, NULL, 100); + } +} + +// Register the initial check to run when entering BS_DEV_INIT +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, wait_for_device, NULL); +``` + + +## Best Practices + +### When Working with Bootstates + +1. **Choose the appropriate state**: Register callbacks at the earliest + state where all dependencies are guaranteed to be initialized, but no + earlier. Check the state descriptions and the functions called by + each state function (`bs_*`) in `hardwaremain.c`. + +2. **Keep callbacks focused**: Each callback should perform a specific, + related task and avoid complex operations that might significantly + delay the boot process. + +3. **Consider dependencies carefully**: Ensure any hardware, data + structures, or other resources your callback needs are available and + initialized at the chosen state and sequence (`BS_ON_ENTRY` vs. + `BS_ON_EXIT`). + +4. **Do not rely on callback order**: Remember that the execution order + of callbacks within the same state and sequence is not guaranteed. + Callbacks should be self-contained and not depend on side effects from + other callbacks that might run before or after them in the same phase. + +5. **Use blocking sparingly**: The blocking mechanism is powerful for + synchronization but can complicate the boot flow and make debugging + harder if overused. Always ensure a corresponding `boot_state_unblock` + call will eventually run. + +6. **Leverage compile-time registration**: Prefer using + `BOOT_STATE_INIT_ENTRY` for callbacks whenever possible. It makes the + registration explicit and easier to find. Runtime registration is + necessary only when the need for the callback is determined dynamically. + +7. **Debug with timestamps and `DEBUG_BOOT_STATE`**: Use the timestamp API + (`timestamp_add_now()`) and enable `DEBUG_BOOT_STATE` to measure + callback execution time, identify bottlenecks, and understand the + flow during development. + +8. **Document state-specific behavior**: When adding callbacks, add + comments explaining why they are placed in a particular state and + sequence. + +9. **Be careful with late states**: Avoid registering non-essential + callbacks in `BS_PAYLOAD_BOOT` or `BS_OS_RESUME`. Callbacks on + `BS_ON_EXIT` for these states are disallowed by compile-time asserts, + as coreboot is about to transfer control. + + +## Related Documentation + +- [Boot Stages in coreboot](https://doc.coreboot.org/getting_started/architecture.html): + Overview of all coreboot boot stages. + + +## References + +- `src/include/bootstate.h`: Public API definitions (callbacks, enums, + scheduling/blocking functions, static registration macro). +- `src/lib/hardwaremain.c`: Internal implementation (state machine driver, + state definitions, state functions). +- `src/ec/google/wilco/chip.c`: Example of bootstate callback usage. +- `src/mainboard/prodrive/hermes/mainboard.c`: Examples of mainboard-specific + bootstate callbacks.