Skip to content
Merged
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
1 change: 1 addition & 0 deletions apt-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ brotli
build-essential
cmake
curl
dfu-util
file
g++
git
Expand Down
54 changes: 54 additions & 0 deletions sw/device/silicon_creator/rom_ext/e2e/rescue/std_utils/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

load(
"//rules/opentitan:defs.bzl",
"fpga_params",
"opentitan_test",
)

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

opentitan_test(
name = "xmodem_protocol",
exec_env = {
"//hw/top_earlgrey:fpga_cw310_rom_ext": None,
},
fpga = fpga_params(
assemble = "",
binaries = {
"//sw/device/silicon_creator/rom_ext/e2e/rescue:boot_test_slot_a": "boot_test",
},
changes_otp = True,
test_cmd = """
--clear-bitstream
--bootstrap={rom_ext}
--firmware={boot_test}
""",
test_harness = "//sw/host/tests/rescue:xmodem_protocol",
),
)

opentitan_test(
name = "usbdfu_protocol",
exec_env = {
"//hw/top_earlgrey:fpga_cw310_rom_ext": None,
},
fpga = fpga_params(
assemble = "",
binaries = {
"//sw/device/silicon_creator/rom_ext/e2e/rescue:boot_test_slot_a": "boot_test",
},
changes_otp = True,
rom_ext = "//sw/device/silicon_creator/rom_ext:rom_ext_usbdfu",
test_cmd = """
--clear-bitstream
--bootstrap={rom_ext}
--firmware={boot_test}
--trigger=strap
--value=3
""",
test_harness = "//sw/host/tests/rescue:usbdfu_protocol",
),
)
5 changes: 5 additions & 0 deletions sw/host/opentitanlib/src/io/uart/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::os::fd::BorrowedFd;
use std::task::{Context, Poll, Waker, ready};
use std::time::Duration;

Expand Down Expand Up @@ -162,4 +163,8 @@ impl<T: Uart> Uart for SoftwareFlowControl<T> {
fn set_break(&self, enable: bool) -> Result<()> {
self.inner.set_break(enable)
}

fn borrow_fd(&self) -> Result<BorrowedFd<'_>> {
self.inner.borrow_fd()
}
}
9 changes: 9 additions & 0 deletions sw/host/opentitanlib/src/io/uart/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

use std::io::{self, Read};
use std::os::fd::BorrowedFd;
use std::rc::Rc;
use std::time::Duration;

Expand Down Expand Up @@ -106,6 +107,10 @@ pub trait Uart: ConsoleDevice {
fn set_break(&self, _enable: bool) -> Result<()> {
Err(TransportError::UnsupportedOperation.into())
}

fn borrow_fd(&self) -> Result<BorrowedFd<'_>> {
Err(TransportError::UnsupportedOperation.into())
}
}

impl<T: Uart + ?Sized> Uart for &T {
Expand Down Expand Up @@ -182,6 +187,10 @@ impl<T: Uart + ?Sized> Uart for Rc<T> {
fn set_break(&self, enable: bool) -> Result<()> {
T::set_break(self, enable)
}

fn borrow_fd(&self) -> Result<BorrowedFd<'_>> {
Err(TransportError::UnsupportedOperation.into())
}
}

impl Read for &dyn Uart {
Expand Down
7 changes: 7 additions & 0 deletions sw/host/opentitanlib/src/io/uart/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ impl Uart for SerialPortUart {
}
Ok(())
}

fn borrow_fd(&self) -> Result<BorrowedFd<'_>> {
let port = self.port.borrow();
// SAFETY: `fd` is owned by `port` and is valid.
let fd = unsafe { BorrowedFd::borrow_raw(port.as_raw_fd()) };
Ok(fd)
}
}

/// Invoke Linux `flock()` on the given serial port, lock will be released when the file
Expand Down
4 changes: 2 additions & 2 deletions sw/host/opentitanlib/src/rescue/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ pub struct RescueParams {
#[arg(short, long, default_value = "")]
pub value: String,
#[command(flatten)]
spi: SpiParams,
pub spi: SpiParams,
#[command(flatten)]
uart: UartParams,
pub uart: UartParams,
#[arg(long)]
pub usb_serial: Option<String>,
}
Expand Down
5 changes: 5 additions & 0 deletions sw/host/ot_transports/hyperdebug/src/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use std::os::fd::BorrowedFd;
use std::rc::Rc;

use anyhow::{Context, Result};
Expand Down Expand Up @@ -189,4 +190,8 @@ impl Uart for HyperdebugUart {
.context("Setting break condition")?;
Ok(())
}

fn borrow_fd(&self) -> Result<BorrowedFd<'_>> {
self.serial_port.borrow_fd()
}
}
33 changes: 33 additions & 0 deletions sw/host/tests/rescue/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library")
load("//rules:ujson.bzl", "ujson_rust")

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

rust_binary(
name = "xmodem_protocol",
srcs = ["xmodem_protocol.rs"],
deps = [
"//sw/host/opentitanlib",
"@crate_index//:anyhow",
"@crate_index//:clap",
"@crate_index//:humantime",
"@crate_index//:log",
"@crate_index//:rustix",
],
)

rust_binary(
name = "usbdfu_protocol",
srcs = ["usbdfu_protocol.rs"],
deps = [
"//sw/host/opentitanlib",
"@crate_index//:anyhow",
"@crate_index//:clap",
"@crate_index//:humantime",
"@crate_index//:log",
],
)
129 changes: 129 additions & 0 deletions sw/host/tests/rescue/usbdfu_protocol.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#![allow(clippy::bool_assert_comparison)]
use anyhow::{Result, anyhow};
use clap::Parser;
use std::process::Command;
use std::time::Duration;

use opentitanlib::app::TransportWrapper;
use opentitanlib::chip::boot_log::BootLog;
use opentitanlib::execute_test;
use opentitanlib::rescue::{EntryMode, RescueParams, RescueProtocol, RescueTrigger};
use opentitanlib::test_utils::init::InitializeTest;
use opentitanlib::uart::console::UartConsole;

#[derive(Debug, Parser)]
struct Opts {
#[command(flatten)]
init: InitializeTest,

/// The rescue protocol.
#[arg(short, long, value_enum, default_value_t = RescueProtocol::UsbDfu)]
rescue_protocol: RescueProtocol,
/// The rescue trigger mechanism.
#[arg(short, long, value_enum, default_value_t = RescueTrigger::Strap)]
trigger: RescueTrigger,
/// The rescue trigger value.
#[arg(short, long, default_value = "")]
value: String,

/// Console receive timeout.
#[arg(long, value_parser = humantime::parse_duration, default_value = "10s")]
timeout: Duration,

/// Firmware image
#[arg(long)]
firmware: String,
}

/// Checks that we can use the primitive xmodem tools from the `lrzsz` package
/// to perform firmware rescue.
fn firmware_update_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
let uart = transport.uart("console")?;
let params = RescueParams {
protocol: opts.rescue_protocol,
trigger: opts.trigger,
value: opts.value.clone(),
..Default::default()
};
let rescue = params.create(transport)?;

// It would be even cooler to do this in a python script or shell script, but
// opentitanlib is just too convenient for manipulating our test infrastructure.
// We trigger rescue and then use `dfu-util` to perform the actual operation.
rescue.enter(transport, EntryMode::Reset)?;
// Note: we drop the rescue instance to release our claim on the usb-dfu
// interface. This allows `dfu-util` to claim and use the interface.
drop(rescue);

let mut child = Command::new("dfu-util")
.arg("--device=18d1:023a")
// AltSetting 0 is the firmware rescue setting.
.arg("--alt=0")
.arg("--reset")
.arg("--download")
.arg(&opts.firmware)
.spawn()?;

let status = child.wait()?;
log::info!("Got dfu-util exit code {status:?}");

let capture = UartConsole::wait_for(&*uart, r"PASS!|BFV:([0-9A-Fa-f]{8})", opts.timeout)?;
if capture[0].starts_with("BFV") {
Err(anyhow!("Error: {}", capture[0]))
} else {
Ok(())
}
}

/// Checks that we can use the primitive xmodem tools from the `lrzsz` package
/// to perform get the BootLog from the chip.
fn get_boot_log_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
let params = RescueParams {
protocol: opts.rescue_protocol,
trigger: opts.trigger,
value: opts.value.clone(),
..Default::default()
};
let rescue = params.create(transport)?;

let path = format!("boot_log-{}.bin", std::process::id());
let _ = std::fs::remove_file(&path);
log::info!("Receving boot_log into {path:?}");

// It would be even cooler to do this in a python script or shell script, but
// opentitanlib is just too convenient for manipulating our test infrastructure.
// We trigger rescue and then use `dfu-util` to perform the actual operation.
rescue.enter(transport, EntryMode::Reset)?;
// Note: we drop the rescue instance to release our claim on the usb-dfu
// interface. This allows `dfu-util` to claim and use the interface.
drop(rescue);

let mut child = Command::new("dfu-util")
.arg("--device=18d1:023a")
// AltSetting 3 is the BootLog setting.
.arg("--alt=3")
.arg("--upload")
.arg(&path)
.spawn()?;

let status = child.wait()?;
log::info!("Got dfu-util exit code {status:?}");

let data = std::fs::read(&path)?;
let blog = BootLog::try_from(data.as_slice())?;
log::info!("BootLog = {blog:?}");
Ok(())
}

fn main() -> Result<()> {
let opts = Opts::parse();
opts.init.init_logging();
let transport = opts.init.init_target()?;
execute_test!(firmware_update_test, &opts, &transport);
execute_test!(get_boot_log_test, &opts, &transport);
Ok(())
}
Loading
Loading