diff --git a/sw/device/silicon_creator/rom/BUILD b/sw/device/silicon_creator/rom/BUILD index 0cef04a5012619..ecbffd07dc54c5 100644 --- a/sw/device/silicon_creator/rom/BUILD +++ b/sw/device/silicon_creator/rom/BUILD @@ -255,6 +255,20 @@ cc_library( ], ) +cc_library( + name = "rom_state", + srcs = ["rom_state.c"], + hdrs = ["rom_state.h"], + target_compatible_with = [OPENTITAN_CPU], + deps = [ + "//sw/device/lib/base:hardened", + "//sw/device/lib/base:macros", + "//sw/device/silicon_creator/lib:error", + "//sw/device/silicon_creator/lib:shutdown", + ], + alwayslink = True, +) + opentitan_test( name = "rom_epmp_test", srcs = [ diff --git a/sw/device/silicon_creator/rom/rom_state.c b/sw/device/silicon_creator/rom/rom_state.c new file mode 100644 index 00000000000000..27944370565dc6 --- /dev/null +++ b/sw/device/silicon_creator/rom/rom_state.c @@ -0,0 +1,51 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/silicon_creator/rom/rom_state.h" + +#include "sw/device/silicon_creator/lib/shutdown.h" + +static OT_WARN_UNUSED_RESULT rom_error_t rom_state_get_state_cb( + const rom_state_cb_t state_callbacks[], const size_t state_callbacks_cnt, + const rom_state_t state, const rom_state_cb_t **state_cb) { + // TODO Start from a random index + for (size_t idx = 0; idx < state_callbacks_cnt; idx++) { + if (launder32(state_callbacks[idx].state) != state) { + continue; + } + + HARDENED_CHECK_EQ(state_callbacks[idx].state, state); + + *state_cb = &state_callbacks[idx]; + return kErrorOk; + } + + return kErrorRomBootFailed; +} + +OT_WARN_UNUSED_RESULT rom_error_t +rom_state_fsm(const rom_state_cb_t state_callbacks[], + const size_t state_callbacks_cnt, const rom_state_t init_state) { + rom_state_t next_state = init_state; + + while (true) { + rom_state_t current_state = next_state; + const rom_state_cb_t *current_state_cb = NULL; + + HARDENED_RETURN_IF_ERROR( + rom_state_get_state_cb(state_callbacks, state_callbacks_cnt, + current_state, ¤t_state_cb)); + + // Pre run hook + HARDENED_RETURN_IF_ERROR(current_state_cb->pre_run(current_state_cb->arg)); + + // State run callback. + HARDENED_RETURN_IF_ERROR( + current_state_cb->run(current_state_cb->arg, &next_state)); + HARDENED_CHECK_NE(current_state, next_state); + + // Post run hooks + HARDENED_RETURN_IF_ERROR(current_state_cb->post_run(current_state_cb->arg)); + } +} diff --git a/sw/device/silicon_creator/rom/rom_state.h b/sw/device/silicon_creator/rom/rom_state.h new file mode 100644 index 00000000000000..ecd75604a097e3 --- /dev/null +++ b/sw/device/silicon_creator/rom/rom_state.h @@ -0,0 +1,178 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_ROM_ROM_STATE_H_ +#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_ROM_ROM_STATE_H_ + +#include +#include + +#include "sw/device/lib/base/hardened.h" +#include "sw/device/lib/base/macros.h" +#include "sw/device/silicon_creator/lib/error.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * The ROM state API allows for declaring ROM states and their associated + * callbacks. A ROM state callback defines which new state the ROM should + * transition to if the current state executes successfully. The + * `rom_state_fsm()` function walks through the ROM defines states, runs + * the current state callback and transitions to the next state. + * + * Silicon creators can customize a ROM state execution flow through pre-run + * post-run callback hooks. Pre-run hooks are unconditionally called before a + * state callback is executed and can modify the state argument. Post-run hooks + * run only after the state callback successfully completed, and can also modify + * the state associated argument. The ROM transitions to the next state defined + * by the current state callback, if and only if the post-run hook returns + * without errors. + * + * By default, the pre-run and posr-run hooks are weakly defined symbols that do + * nothing. They can be optionally be overriden by silicon creators, through an + * external repository. See the corresponding documentation and example in + * `sw/device/silicon_creator/rom/hooks/` + */ + +/** + * A ROM state. + */ +typedef uint32_t rom_state_t; + +/** + * A ROM state hook. + * ROM state hooks are called prior and after the state run callback. + * They can be overridden by silicon creator implementation, through an external + * repository. + * + * @param arg The ROM state argument. + */ +typedef OT_WARN_UNUSED_RESULT rom_error_t rom_state_hook_cb(void *arg); + +/** + * A ROM state run callback. + * This is the main callback for a ROM state. It can not be overridden by + * silicon creator hooks, unlike ROM state hooks. + * + * @param arg The ROM state callback and hooks argument. This pointer is shared + * between a state run callback and hooks, and will typically point + * to a static structure or variable. + * @param next_state The next state the ROM should transition to. + */ +typedef OT_WARN_UNUSED_RESULT rom_error_t +rom_state_run_cb(void *arg, rom_state_t *next_state); + +/** + * A ROM state callback + */ +typedef struct rom_state_cb { + rom_state_t state; + void *arg; + rom_state_hook_cb *pre_run; + rom_state_hook_cb *post_run; + rom_state_run_cb *run; +} rom_state_cb_t; + +// clang-format off +#define ROM_STATE_PRE_HOOK_WEAK_(state_) \ + OT_WEAK OT_WARN_UNUSED_RESULT \ + rom_error_t rom_state_pre_##state_(void *arg) { \ + return kErrorOk; \ + } + +#define ROM_STATE_POST_HOOK_WEAK_(state_) \ + OT_WEAK OT_WARN_UNUSED_RESULT \ + rom_error_t rom_state_post_##state_(void *arg) { \ + return kErrorOk; \ + } + +#define ROM_STATE_CALLBACKS_(state_, value_, run_, run_arg_) \ + ROM_STATE_PRE_HOOK_WEAK_(state_) \ + ROM_STATE_POST_HOOK_WEAK_(state_) + +#define ROM_STATE_VALUES_(state_, value_, run_, run_arg) state_ = value_, + +#define ROM_STATE_TABLE_ENTRIES_(state_, value_, run_, run_arg_) \ + { \ + .state = state_, \ + .pre_run = rom_state_pre_##state_, \ + .post_run = rom_state_post_##state_, \ + .run = run_, \ + .arg = run_arg_, \ + }, + +/** + * Binds a hook implementation to a ROM state, in order for it to be run + * before the state's run callback. + * + * This macro overrides the default, empty pre-run hook for a given state. + */ +#define ROM_STATE_PRE_HOOK(state_, hook_) \ + OT_WARN_UNUSED_RESULT \ + rom_error_t rom_state_pre_##state_(void *arg) { \ + return hook_(arg); \ + } + +/** + * Binds a hook implementation to a ROM state, in order for it to be run + * after the state's run callback. + * + * This macro overrides the default, empty post-run hook for a given state. + */ +#define ROM_STATE_POST_HOOK(state_, hook_) \ + OT_WARN_UNUSED_RESULT \ + rom_error_t rom_state_post_##state_(void *arg) { \ + return hook_(arg); \ + } + +/** + * The ROM states table. + * + * This macro declares and defines: + * - All ROM state values as an enum. + * - For each ROM state, its callback, its pre-run and post-run, + * weakly-defined and overridable hooks, and its argument. + * - The ROM states array. + * + * @param table_name_ The ROM states array name. This will be defined as a + * static, constant array of `rom_state_cb_t` entries. + * @param table_cnt_ The exact number of entries in the states array. + * @param TABLE_ The ROM state table definition. `TABLE_` is C macro that + * takes another macro as its argument. The macro passed to + * `TABLE_` takes 4 arguments in order to create a ROM state + * tuple: one identifier, one value, one run callback and one + * argument to pass to the callback. The created tuple is used to + * define and declare a ROM state related structure or variable. + */ +#define ROM_STATE_INIT_TABLE(table_name_, table_cnt_, TABLE_) \ + enum { TABLE_(ROM_STATE_VALUES_) }; \ + TABLE_(ROM_STATE_CALLBACKS_) \ + static const rom_state_cb_t table_name_[table_cnt_] = {TABLE_(ROM_STATE_TABLE_ENTRIES_) } +// clang-format on + +/** + * The ROM state machine walker. + * + * ROM implementations call this function with the initial ROM state, and it + * walk through the ROM states defined in @states_callback. + * When transitioning to a ROM state, this function will first call the pre-run + * hook for the state, then the state run callback and finally the post-run + * hook. If any one of these three call returns an error, `rom_state_fsm` + * returns and propagates the error. + * + * @param states_callbacks The array of all ROM states callbacks. + * @param states_callbacks_cnt The number of entries in states_callbacks. + * @param init_state The initial state of the ROM. + */ +OT_WARN_UNUSED_RESULT rom_error_t +rom_state_fsm(const rom_state_cb_t states_callbacks[], + const size_t state_callbacks_cnt, const rom_state_t init_state); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_ROM_ROM_STATE_H_