# 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`:

    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:

    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:

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

    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

    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`).

    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

    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.

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

    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:

    // 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):

    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:

    #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: 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.