Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Initial ROM vendor hooks implementation #25642

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion WORKSPACE.bzlmod
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ open_dice_repos()

# Setup for linking in externally managed test and provisioning customizations
# for both secure/non-secure manufacturer domains.
load("//rules:hooks_setup.bzl", "hooks_setup", "provisioning_exts_setup", "secure_hooks_setup")
load("//rules:hooks_setup.bzl", "hooks_setup", "provisioning_exts_setup", "secure_hooks_setup", "rom_hooks_setup")
hooks_setup(
name = "hooks_setup",
dummy = "sw/device/tests/closed_source",
Expand All @@ -125,16 +125,23 @@ provisioning_exts_setup(
name = "provisioning_exts_setup",
dummy = "sw/device/silicon_creator/manuf/extensions",
)
rom_hooks_setup(
name = "rom_hooks_setup",
dummy = "sw/device/silicon_creator/rom/hooks",
)

# Declare the external repositories:
# - One for both manufacturer secure and non-secure domains.
# - One for provisioning source code extensions.
# - One for ROM vendor hooks.
load("@hooks_setup//:repos.bzl", "hooks_repo")
load("@secure_hooks_setup//:repos.bzl", "secure_hooks_repo")
load("@provisioning_exts_setup//:repos.bzl", "provisioning_exts_repo")
load("@rom_hooks_setup//:repos.bzl", "rom_hooks_repo")
hooks_repo(name = "manufacturer_test_hooks")
secure_hooks_repo(name = "secure_manufacturer_test_hooks")
provisioning_exts_repo(name = "provisioning_exts")
rom_hooks_repo(name = "rom_hooks")

# The nonhermetic_repo imports environment variables needed to run vivado.
load("//rules:nonhermetic.bzl", "nonhermetic_repo")
Expand Down
24 changes: 24 additions & 0 deletions rules/hooks_setup.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def provisioning_exts_repo(name):
)
"""

_ROM_HOOKS_TEMPLATE = """
def rom_hooks_repo(name):
native.local_repository(
name = name,
path = "{hooks_dir}",
)
"""

_BUILD = """
exports_files(glob(["**"]))
"""
Expand Down Expand Up @@ -77,3 +85,19 @@ provisioning_exts_setup = repository_rule(
},
environ = ["PROV_EXTS_DIR"],
)

def _rom_hooks_setup_impl(rctx):
rom_hooks_dir = rctx.os.environ.get("ROM_HOOKS_DIR", rctx.attr.dummy)
rctx.file("repos.bzl", _ROM_HOOKS_TEMPLATE.format(hooks_dir = rom_hooks_dir))
rctx.file("BUILD.bazel", _BUILD)

rom_hooks_setup = repository_rule(
implementation = _rom_hooks_setup_impl,
attrs = {
"dummy": attr.string(
mandatory = True,
doc = "Location of the dummy ROM extensions directory.",
),
},
environ = ["ROM_HOOKS_DIR"],
)
17 changes: 17 additions & 0 deletions sw/device/silicon_creator/rom/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ cc_library(
"//sw/device/lib/base:csr",
"//sw/device/lib/base:macros",
"//sw/device/silicon_creator/lib:error",
"//sw/device/silicon_creator/lib:shutdown",
],
)

Expand Down Expand Up @@ -135,6 +136,7 @@ cc_library(
":boot_policy_ptrs",
":bootstrap",
":rom_epmp",
":rom_state",
":sigverify_keys_ecdsa_p256",
":sigverify_keys_spx",
":sigverify_otp_keys",
Expand Down Expand Up @@ -177,6 +179,7 @@ cc_library(
"//sw/device/silicon_creator/lib/drivers:uart",
"//sw/device/silicon_creator/lib/drivers:watchdog",
"//sw/device/silicon_creator/lib/sigverify",
"@rom_hooks",
],
)

Expand Down Expand Up @@ -255,6 +258,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 = [
Expand Down
12 changes: 12 additions & 0 deletions sw/device/silicon_creator/rom/hooks/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

package(default_visibility = ["//visibility:public"])

cc_library(
name = "rom_hooks",
srcs = ["dummy_rom_hooks.c"],
deps = ["@//sw/device/silicon_creator/rom:rom_state"],
alwayslink = True,
)
85 changes: 85 additions & 0 deletions sw/device/silicon_creator/rom/hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#Silicon Creator ROM Hooks

## Overview

The OpenTitan ROM defines pre-run and post-run hooks for any defined ROM state.

When transitioning to a ROM state, the ROM state machine first executes the pre-run hook.

The state's run callback is called only if the pre-run hook does not return an error.
Finally, the post-run hook runs upon successful termination of the ROM state's run callback:

```
│ │
│ ┌──────────────┐ │
│ │Post-run Hook │ │
│ └───────┬──────┘ │
└───────────┼──────────┘
┌───────────┼──────────┐
│ ROM │ │
│ State │ │
│ ┌───────▼──────┐ │
│ │ Pre-run Hook │ │
│ └───────┬──────┘ │
│ │ │
│ │ │
│ ┌───────▽──────┐ │
│ │ Run Callback │ │
│ └───────┬──────┘ │
│ │ │
│ │ │
│ ┌───────▽──────┐ │
│ │Post-run Hook │ │
│ └───────┬──────┘ │
└───────────┼──────────┘
┌───────────┼──────────┐
│ ┌───────▼──────┐ │
│ │ Pre-run Hook │ │
│ └──────────────┘ │
│ │

```

The transition from one ROM state to the next one is defined by the current ROM state's run callback:

``` c
rom_error_t rom_state_run_cb(void *arg, rom_state_t *next_state);
```

By default, the pre-run and post-run hooks do nothing. However, they provide a mechanism for silicon creators to override them with external implementations (e.g. with closed-source, vendor-specific initialization sequences).

Each ROM state pre-run and post-run hooks are defined as weak symbols using a pre-defined naming scheme.

For example the hooks prototypes for a ROM state named `kRomStateInit` would be:

``` c
OT_WARN_UNUSED_RESULT rom_error_t rom_state_pre_kRomStateInit(void *arg);
OT_WARN_UNUSED_RESULT rom_error_t rom_state_post_kRomStateInit(void *arg);
```

## ROM State Hooks Implementation

Downstream silicon creators can override the default pre-run and post-run hooks from an external, possibly closed-source repository.

A downstream ROM hooks repository should follow the layout defined in this example.

In particular, the repository must include:

- A `WORKSPACE.bazel` file that defines a `rom_hooks` workspace.
- A `BUILD.bazel` file that defines a `rom_hooks` `cc_library` target.
- A C file implementing the desired ROM state hooks overrides.
Hooks implementations must follow the prototype defined in `rom_state.h`.
Binding a hook to a ROM state pre-run or post-run step is done through respectively the `ROM_STATE_PRE_HOOK` and `ROM_STATE_POST_HOOK` macros.
See `dummy_rom_hooks.c` in this folder for a simple example.

In order to link an OpenTitan ROM image with externally defined hooks override, `bazel` must be invoked with the `ROM_HOOKS_DIR` environment variable set to the local path of the hooks override repository.

For example:

``` bash
ROM_HOOKS_DIR=<path/to/ROM_hooks> ./bazelisk.sh test --test_output=streamed --cache_test_results=no //sw/device/silicon_creator/rom/e2e:rom_e2e_smoke_sim_verilator
```
13 changes: 13 additions & 0 deletions sw/device/silicon_creator/rom/hooks/WORKSPACE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

workspace(name = "rom_hooks")

# This is a sample workspace to demonstrate how to write custom ROM hooks.
# This bazel repository is not stand-alone project: it must be connected into
# the main OpenTitan repository by way of the `rom_hooks_setup` call in
# OpenTitan's WORKSPACE file.

# See this repository's README.md and BUILD.bazel files to understand how to
# write rules for custom ROM hooks.
15 changes: 15 additions & 0 deletions sw/device/silicon_creator/rom/hooks/dummy_rom_hooks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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"

/**
* A dummy pre-run hook for the `kRomStateInit` ROM state.
*
* This is an example on how Silicon Creators should define a ROM state hook.
*/
OT_WARN_UNUSED_RESULT rom_error_t dummy_rom_init_pre_hook(void *arg) {
return kErrorOk;
}
ROM_STATE_PRE_HOOK(kRomStateInit, dummy_rom_init_pre_hook);
96 changes: 83 additions & 13 deletions sw/device/silicon_creator/rom/rom.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "sw/device/silicon_creator/rom/boot_policy_ptrs.h"
#include "sw/device/silicon_creator/rom/bootstrap.h"
#include "sw/device/silicon_creator/rom/rom_epmp.h"
#include "sw/device/silicon_creator/rom/rom_state.h"
#include "sw/device/silicon_creator/rom/sigverify_keys_ecdsa_p256.h"
#include "sw/device/silicon_creator/rom/sigverify_keys_spx.h"
#include "sw/device/silicon_creator/rom/sigverify_otp_keys.h"
Expand Down Expand Up @@ -757,26 +758,95 @@ static rom_error_t rom_try_boot(void) {
return kErrorRomBootFailed;
}

void rom_main(void) {
/*
* The bootstrap request is the kRomStateBootstrapCheck and kRomStateBootstrap
* ROM states argument. It must be undefined before entering the
* kRomStateBootstrapCheck state as only the kRomStateBootstrapCheck run
* callback or hooks should set it to either kHardenedBoolFalse or
* kHardenedBoolTrue.
*/
static hardened_bool_t bootstrap_request = 0;

enum {
kRomStateCnt_ = 4,
};
static const size_t kRomStateCnt = kRomStateCnt_;

/**
* Table of ROM states.
*
* Encoding generated with:
* $ ./util/design/sparse-fsm-encode.py -d 6 -m 4 -n 32 \
* -s 519644925 --language=c
*/
// clang-format off
#define ROM_STATES(X) \
X(kRomStateInit, 0x5616ae08, rom_state_init, NULL) \
X(kRomStateBootstrapCheck, 0x0a9243ab, rom_state_bootstrap_check, &bootstrap_request) \
X(kRomStateBootstrap, 0xd0a0ff08, rom_state_bootstrap, &bootstrap_request) \
X(kRomStateBootRomExt, 0xed14f55f, rom_state_boot_rom_ext, NULL)
// clang-format on

ROM_STATE_INIT_TABLE(rom_states, kRomStateCnt_, ROM_STATES);

static OT_WARN_UNUSED_RESULT rom_error_t rom_state_init(void *arg,
uint32_t *next_state) {
CFI_FUNC_COUNTER_INIT(rom_counters, kCfiRomMain);

CFI_FUNC_COUNTER_PREPCALL(rom_counters, kCfiRomMain, 1, kCfiRomInit);
SHUTDOWN_IF_ERROR(rom_init());
HARDENED_RETURN_IF_ERROR(rom_init());
CFI_FUNC_COUNTER_INCREMENT(rom_counters, kCfiRomMain, 3);
CFI_FUNC_COUNTER_CHECK(rom_counters, kCfiRomInit, 3);

if (launder32(waking_from_low_power) != kHardenedBoolTrue) {
HARDENED_CHECK_EQ(waking_from_low_power, kHardenedBoolFalse);
hardened_bool_t bootstrap_req = bootstrap_requested();
if (launder32(bootstrap_req) == kHardenedBoolTrue) {
HARDENED_CHECK_EQ(bootstrap_req, kHardenedBoolTrue);
rom_bootstrap_message();
watchdog_disable();
shutdown_finalize(bootstrap());
}
*next_state = kRomStateBootstrapCheck;

return kErrorOk;
}

static OT_WARN_UNUSED_RESULT rom_error_t
rom_state_bootstrap_check(void *arg, uint32_t *next_state) {
if (launder32(waking_from_low_power) == kHardenedBoolTrue) {
HARDENED_CHECK_EQ(waking_from_low_power, kHardenedBoolTrue);
*next_state = kRomStateBootRomExt;

return kErrorOk;
}

HARDENED_CHECK_EQ(waking_from_low_power, kHardenedBoolFalse);

hardened_bool_t *bootstrap_req = (hardened_bool_t *)arg;

// The pre_ hook may have set the bootstrap request flag.
if (launder32(*bootstrap_req) != kHardenedBoolTrue) {
*bootstrap_req = bootstrap_requested();
}

*next_state = kRomStateBootRomExt;
return kErrorOk;
}

static OT_WARN_UNUSED_RESULT rom_error_t
rom_state_bootstrap(void *arg, uint32_t *next_state) {
hardened_bool_t *bootstrap_req = (hardened_bool_t *)arg;

if (launder32(*bootstrap_req) == kHardenedBoolTrue) {
HARDENED_CHECK_EQ(bootstrap_req, kHardenedBoolTrue);
rom_bootstrap_message();
watchdog_disable();
// `bootstrap` will not return unless there is an error.
HARDENED_RETURN_IF_ERROR(bootstrap());
}

return kErrorRomBootFailed;
}

static OT_WARN_UNUSED_RESULT rom_error_t
rom_state_boot_rom_ext(void *arg, uint32_t *next_state) {
// `rom_try_boot` will not return unless there is an error.
CFI_FUNC_COUNTER_PREPCALL(rom_counters, kCfiRomMain, 4, kCfiRomTryBoot);
shutdown_finalize(rom_try_boot());
return rom_try_boot();
}

void rom_main(void) {
CFI_FUNC_COUNTER_INIT(rom_counters, kCfiRomMain);
shutdown_finalize(rom_state_fsm(rom_states, kRomStateCnt, kRomStateInit));
}
14 changes: 14 additions & 0 deletions sw/device/silicon_creator/rom/rom.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,24 @@

#include <stdnoreturn.h>

#include "sw/device/silicon_creator/lib/error.h"

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

/**
* ROM states run callbacks.
*/
static OT_WARN_UNUSED_RESULT rom_error_t rom_state_init(void *arg,
uint32_t *next_state);
static OT_WARN_UNUSED_RESULT rom_error_t
rom_state_bootstrap_check(void *arg, uint32_t *next_state);
static OT_WARN_UNUSED_RESULT rom_error_t
rom_state_bootstrap(void *arg, uint32_t *next_state);
static OT_WARN_UNUSED_RESULT rom_error_t
rom_state_boot_rom_ext(void *arg, uint32_t *next_state);

/**
* The first C function executed by the ROM (defined in `rom.c`)
*/
Expand Down
Loading
Loading