Skip to content

Commit 2d37e7b

Browse files
[rom_ext,e2e] add ROM_EXT E2E test to check variation interoperability
There are two flavors of ROM_EXT: 1. one that uses X.509 DICE cert encodings, and 2. one that uses CWT DICE cert encodings. If you run one first, you should be able to run the other later and the ROM_EXT should seemlessly update the CDI certs. This adds a test to check this behavior works as intended. Signed-off-by: Tim Trippel <[email protected]>
1 parent d2c3b24 commit 2d37e7b

File tree

4 files changed

+219
-1
lines changed

4 files changed

+219
-1
lines changed

sw/device/silicon_creator/rom_ext/e2e/dice_chain/BUILD

+100
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,27 @@
22
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
33
# SPDX-License-Identifier: Apache-2.0
44

5+
load("//rules:const.bzl", "CONST", "hex")
6+
load("//rules:manifest.bzl", "manifest")
7+
load("//rules/opentitan:cc.bzl", "opentitan_binary_assemble")
58
load(
69
"//rules/opentitan:defs.bzl",
710
"fpga_params",
11+
"opentitan_binary",
812
"opentitan_test",
913
)
14+
load(
15+
"//sw/device/silicon_creator/rom/e2e:defs.bzl",
16+
"SLOTS",
17+
)
1018
load(
1119
"//sw/device/silicon_creator/rom_ext:defs.bzl",
1220
"ROM_EXT_VARIATIONS",
1321
)
22+
load(
23+
"//sw/device/silicon_creator/rom_ext/e2e:defs.bzl",
24+
"OWNER_SLOTS",
25+
)
1426

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

@@ -38,3 +50,91 @@ package(default_visibility = ["//visibility:public"])
3850
)
3951
for variation in ROM_EXT_VARIATIONS.keys()
4052
]
53+
54+
manifest({
55+
"name": "owner_manifest",
56+
"identifier": hex(CONST.OWNER),
57+
"visibility": ["//visibility:private"],
58+
})
59+
60+
opentitan_binary(
61+
name = "print_certs_for_assemble",
62+
testonly = True,
63+
srcs = ["//sw/device/silicon_creator/rom_ext/e2e/attestation:print_certs.c"],
64+
exec_env = {
65+
"//hw/top_earlgrey:fpga_hyper310_rom_ext": None,
66+
"//hw/top_earlgrey:fpga_cw340_rom_ext": None,
67+
},
68+
linker_script = "//sw/device/lib/testing/test_framework:ottf_ld_silicon_owner_slot_a",
69+
manifest = ":owner_manifest",
70+
deps = [
71+
"//sw/device/lib/base:status",
72+
"//sw/device/lib/runtime:log",
73+
"//sw/device/lib/runtime:print",
74+
"//sw/device/lib/testing/test_framework:ottf_main",
75+
"//sw/device/silicon_creator/lib/drivers:flash_ctrl",
76+
"//sw/device/silicon_creator/manuf/base:perso_tlv_data",
77+
],
78+
)
79+
80+
[
81+
opentitan_binary_assemble(
82+
name = "{}_rom_ext_owner_bundle".format(variation),
83+
testonly = True,
84+
bins = {
85+
"//sw/device/silicon_creator/rom_ext:rom_ext_{}_slot_a".format(variation): SLOTS["a"],
86+
":print_certs_for_assemble": OWNER_SLOTS["a"],
87+
},
88+
exec_env = [
89+
"//hw/top_earlgrey:fpga_hyper310_rom_with_fake_keys",
90+
"//hw/top_earlgrey:fpga_cw340_rom_with_fake_keys",
91+
],
92+
)
93+
for variation in ROM_EXT_VARIATIONS.keys()
94+
]
95+
96+
_VARIATION_INTEROP_TEST_CASES = [
97+
{
98+
"first": "x509",
99+
"second": "cwt",
100+
},
101+
{
102+
"first": "cwt",
103+
"second": "x509",
104+
},
105+
]
106+
107+
[
108+
opentitan_test(
109+
name = "variation_interop_{}_first_test".format(tc["first"]),
110+
exec_env = {
111+
"//hw/top_earlgrey:fpga_hyper310_rom_with_fake_keys": None,
112+
"//hw/top_earlgrey:fpga_cw340_rom_with_fake_keys": None,
113+
},
114+
fpga = fpga_params(
115+
binaries = {
116+
":dice_{}_rom_ext_owner_bundle".format(tc["second"]): "second",
117+
":dice_{}_rom_ext_owner_bundle".format(tc["first"]): "firmware",
118+
},
119+
otp = "//sw/device/silicon_creator/rom_ext/e2e:otp_img_secret2_locked_rma",
120+
test_cmd = """
121+
--clear-bitstream
122+
--bootstrap={firmware}
123+
--second-bootstrap={second}
124+
""",
125+
test_harness = "//sw/host/tests/rom_ext/e2e_variation_interop",
126+
),
127+
)
128+
for tc in _VARIATION_INTEROP_TEST_CASES
129+
]
130+
131+
test_suite(
132+
name = "variation_interop_tests",
133+
tags = ["manual"],
134+
tests = [
135+
"variation_interop_{}_first_test".format(
136+
tc["first"],
137+
)
138+
for tc in _VARIATION_INTEROP_TEST_CASES
139+
],
140+
)

sw/device/silicon_creator/rom_ext/rom_ext.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ rom_error_t dice_chain_attestation_owner(
875875
HARDENED_RETURN_IF_ERROR(dice_chain_load_cert_obj("CDI_1", /*name_size=*/6));
876876
if (launder32(dice_chain.cert_valid) == kHardenedBoolFalse) {
877877
HARDENED_CHECK_EQ(dice_chain.cert_valid, kHardenedBoolFalse);
878-
dbg_printf("warning: CDI_1 certificate not valid. updating\r\n");
878+
dbg_printf("warning: CDI_1 certificate not valid; updating\r\n");
879879
// Update the cert page buffer.
880880
size_t updated_cert_size = kScratchCertSizeBytes;
881881
HARDENED_RETURN_IF_ERROR(dice_cdi_1_cert_build(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright lowRISC contributors (OpenTitan project).
2+
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
load("@rules_rust//rust:defs.bzl", "rust_binary")
6+
7+
package(default_visibility = ["//visibility:public"])
8+
9+
rust_binary(
10+
name = "e2e_variation_interop",
11+
srcs = [
12+
"src/main.rs",
13+
],
14+
deps = [
15+
"//sw/host/opentitanlib",
16+
"//third_party/rust/crates:anyhow",
17+
"//third_party/rust/crates:clap",
18+
"//third_party/rust/crates:humantime",
19+
"//third_party/rust/crates:log",
20+
"//third_party/rust/crates:num_enum",
21+
"//third_party/rust/crates:regex",
22+
],
23+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright lowRISC contributors (OpenTitan project).
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
//! ROM_EXT E2E Variation Interoperability test.
6+
//!
7+
//! This test harness checks that an CWT ROM_EXT can be booted on a chip before and after an X.509
8+
//! ROM_EXT can be booted, and vice versa. In both cases, CDI_* should be regenerated by the
9+
//! ROM_EXT seamlessly.
10+
11+
use std::path::PathBuf;
12+
use std::time::Duration;
13+
14+
use anyhow::{anyhow, Context};
15+
use clap::Parser;
16+
use regex::Regex;
17+
18+
use opentitanlib::test_utils::init::InitializeTest;
19+
use opentitanlib::uart::console::{ExitStatus, UartConsole};
20+
21+
/// CLI args for this test.
22+
#[derive(Debug, Parser)]
23+
struct Opts {
24+
#[command(flatten)]
25+
init: InitializeTest,
26+
27+
/// Second image (ROM_EXT + Owner FW bundle) to bootstrap.
28+
#[arg(long)]
29+
second_bootstrap: PathBuf,
30+
31+
/// Console receive timeout.
32+
#[arg(long, value_parser = humantime::parse_duration, default_value = "30s")]
33+
timeout: Duration,
34+
}
35+
36+
fn main() -> anyhow::Result<()> {
37+
let opts = Opts::parse();
38+
opts.init.init_logging();
39+
40+
// Bootstrap first ROM_EXT + Owner FW.
41+
let transport = opts.init.init_target()?;
42+
let uart = transport.uart("console").context("failed to get UART")?;
43+
44+
// Wait for CDI_* update messags.
45+
for i in 0..2 {
46+
let _ = UartConsole::wait_for(
47+
&*uart,
48+
format!(r"warning: CDI_{:?} certificate not valid; updating", i).as_str(),
49+
opts.timeout,
50+
)?;
51+
}
52+
53+
// Wait for pass message from first owner firmware stage.
54+
let _ = UartConsole::wait_for(&*uart, r"PASS!", opts.timeout)?;
55+
56+
// Bootstrap second ROM_EXT + Owner FW.
57+
opts.init
58+
.bootstrap
59+
.load(&transport, &opts.second_bootstrap)?;
60+
61+
// Wait for pass message from owner firmware stage.
62+
let mut console = UartConsole {
63+
timeout: Some(opts.timeout),
64+
exit_success: Some(Regex::new(r"PASS.*\n")?),
65+
exit_failure: Some(Regex::new(r"BFV.*\n")?),
66+
newline: true,
67+
..Default::default()
68+
};
69+
let mut stdout = std::io::stdout();
70+
let result = console.interact(&*uart, None, Some(&mut stdout))?;
71+
match result {
72+
ExitStatus::None | ExitStatus::CtrlC => Ok(()),
73+
ExitStatus::Timeout => {
74+
if console.exit_success.is_some() {
75+
Err(anyhow!("Console timeout exceeded"))
76+
} else {
77+
Ok(())
78+
}
79+
}
80+
ExitStatus::ExitSuccess => {
81+
log::info!(
82+
"ExitSuccess({:?})",
83+
console.captures(result).unwrap().get(0).unwrap().as_str()
84+
);
85+
Ok(())
86+
}
87+
ExitStatus::ExitFailure => {
88+
log::info!(
89+
"ExitFailure({:?})",
90+
console.captures(result).unwrap().get(0).unwrap().as_str()
91+
);
92+
Err(anyhow!("Matched exit_failure expression"))
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)