Skip to content
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
34 changes: 33 additions & 1 deletion ddi/nix/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use azihsm_ddi_mbor_types::MborError;
use azihsm_ddi_mbor_types::SessionControlKind;
use azihsm_ddi_tbor_types::TborOpReq;
use azihsm_ddi_tbor_types::TborResp;
use azihsm_ddi_tbor_types::TborStatus;
use bitfield_struct::bitfield;
use nix::ioctl_readwrite;
use parking_lot::RwLock;
Expand Down Expand Up @@ -698,6 +699,37 @@ impl DdiNixDev {

Ok(ioctl_status)
}

/// Same as [`Self::map_ioctl_status`] but for callers executing a
/// **TBOR** command. Driver-layer session-scoping rejections
/// (per-fd `AZIHSM_MAX_SESSIONS_PER_FD` bookkeeping — id mismatch,
/// limit reached) are surfaced as
/// `DdiError::TborStatus(TborStatus::FileHandle*)` instead of
/// `DdiError::DdiStatus(DdiStatus::FileHandle*)`, so callers see a
/// TBOR status for a TBOR command regardless of which layer of
/// the stack (driver vs FW) produced the rejection. The
/// `FileHandle*` variants exist in both `DdiStatus` and
/// `TborStatus` with identical numeric encodings, so this is a
/// pure type-level remap.
fn map_ioctl_status_tbor(&self, ioctl_status: u32) -> Result<u32, DdiError> {
match McrCpGenericIoctlErrorKind::try_from(ioctl_status) {
Ok(McrCpGenericIoctlErrorKind::SessionLimitReached) => {
return Err(DdiError::TborStatus(
TborStatus::FileHandleSessionLimitReached,
));
}
Ok(McrCpGenericIoctlErrorKind::NoExistingSession) => {
return Err(DdiError::TborStatus(TborStatus::SessionNotFound));
}
Comment thread
wenbo-yuan marked this conversation as resolved.
Ok(McrCpGenericIoctlErrorKind::SessionIdMismatch) => {
return Err(DdiError::TborStatus(
TborStatus::FileHandleSessionIdDoesNotMatch,
));
}
_ => {}
}
self.map_ioctl_status(ioctl_status)
}
}

/// Align AAD in place according to the following rules:
Expand Down Expand Up @@ -932,7 +964,7 @@ impl DdiDev for DdiNixDev {
// of the ioctl call; the buffers outlive `cmd`.
let res = unsafe { mcr_ctrl_cmd_generic_ioctl(self.file.read().as_raw_fd(), &mut cmd) };
if res.is_err() {
self.map_ioctl_status(cmd.out_data.ioctl_status)?;
self.map_ioctl_status_tbor(cmd.out_data.ioctl_status)?;
res.map_err(DdiError::NixError)?;
}
if cmd.out_data.status != 0 {
Expand Down
5 changes: 0 additions & 5 deletions ddi/tbor/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ path = "tests/azihsm_ddi_tbor_tests.rs"
emu = ["azihsm_ddi/emu"]
mock = ["azihsm_ddi/mock"]
sock = ["azihsm_ddi/sock"]
# Opt-in feature for the `hw::` smoke tests. These tests drive real
# silicon via the native OS backend and require a physical device;
# excluded from the default `cargo test` build so contributor / CI
# runs on machines without a board don't panic in `open_hw_dev`.
hw-tests = []
table-4 = ["azihsm_ddi/table-4"]
table-64 = ["azihsm_ddi/table-64"]

Expand Down
18 changes: 9 additions & 9 deletions ddi/tbor/types/tests/azihsm_ddi_tbor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
//!
//! Backend selection is feature-gated; the same tests run across every
//! transport. Run with `--features emu` (in-process firmware),
//! `--features sock` (firmware behind a socket server), or
//! `--features mock` (transport-contract probes).
//! `--features sock` (firmware behind a socket server), or with
//! **no** feature (native OS backend — `nix` on Linux / `win` on
//! Windows — for on-silicon test runs).
//!
//! With **no** feature enabled the crate falls through to the native
//! OS backend (`nix` on Linux / `win` on Windows), which drives real
//! silicon. Hardware-only smoke tests live under [`hw`].
//! `mock` disables the whole harness + `commands` tree: mock rejects
//! TBOR at the transport layer, so command-level integration tests
//! are meaningless there. Under `--features mock` this binary
//! compiles the crate only and runs zero tests.

#[cfg(any(feature = "emu", feature = "mock", feature = "sock"))]
#[cfg(not(feature = "mock"))]
pub mod harness;

#[cfg(not(feature = "mock"))]
pub mod commands;
Comment thread
wenbo-yuan marked this conversation as resolved.

#[cfg(feature = "hw-tests")]
pub mod hw;
57 changes: 18 additions & 39 deletions ddi/tbor/types/tests/commands/api_rev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,23 @@

//! Integration tests for TBOR `ApiRev`.
//!
//! `round_trip` exercises the full path host → backend (`emu` or `sock`)
//! → fw `handle_tbor_op` → response, so it is transport-agnostic.
//! `unsupported_on_mock` asserts the design contract that backends opt
//! in to TBOR.
//! `round_trip` exercises host → backend → fw `handle_tbor_op` →
//! response. `api_rev_repeated_stable` and
//! `api_rev_independent_of_session_state` guard against per-call or
//! session-scoped state leaking into the out-of-session handler.
//!
//! Pilot module for the [`TestCtx`](crate::harness::TestCtx)
//! migration — every test in this file constructs the ctx once and
//! drives every device interaction through its methods. Phase 6a
//! finished the migration of the session-state probe to the new
//! `ctx.session_open_init`/`finish`/`session_close` methods.

#![cfg(any(feature = "emu", feature = "mock", feature = "sock"))]
//! Backend is selected at compile time by
//! [`azihsm_ddi::AzihsmDdi::default`].

use azihsm_ddi_tbor_types::TborApiRevReq;

use crate::harness::TestCtx;

#[cfg(any(feature = "emu", feature = "sock"))]
const EXPECTED: azihsm_ddi_tbor_types::TborApiRevResp = azihsm_ddi_tbor_types::TborApiRevResp {
min_ver: 1,
max_ver: 1,
};

#[cfg(any(feature = "emu", feature = "sock"))]
#[test]
fn round_trip() {
let ctx = TestCtx::new();
Expand All @@ -39,14 +32,13 @@ fn round_trip() {
);
}

/// A1: `ApiRev` is stateless — repeated invocations on the same
/// device handle return byte-identical responses. Catches any
/// regression that would silently introduce per-call state (e.g. a
/// version negotiation cache, a session-dependent code path) in the
/// `ApiRev` is stateless — repeated invocations on the same device
/// handle return byte-identical responses. Catches any regression
/// that would silently introduce per-call state (e.g. a version
/// negotiation cache, a session-dependent code path) in the
/// dispatcher's only out-of-session in-band handler.
#[cfg(feature = "emu")]
#[test]
fn api_rev_repeated_stable_emu() {
fn api_rev_repeated_stable() {
let ctx = TestCtx::new();
let baseline = ctx.tbor(&TborApiRevReq::new()).expect("baseline ApiRev");
assert_eq!(baseline, EXPECTED, "baseline must match expected");
Expand All @@ -56,15 +48,14 @@ fn api_rev_repeated_stable_emu() {
}
}

/// A2: `ApiRev` is independent of session-machine state — it
/// returns the same response while a Pending (init-only) handshake
/// occupies a session slot, and continues to do so after the slot
/// transitions to Active. Together with the gate test in
/// `default_psk_gate.rs` this proves the dispatcher never lets
/// session state leak into the out-of-session handler.
#[cfg(feature = "emu")]
/// `ApiRev` is independent of session-machine state — it returns the
/// same response while a Pending (init-only) handshake occupies a
/// session slot, and continues to do so after the slot transitions
/// to Active. Together with the gate test in `default_psk_gate.rs`
/// this proves the dispatcher never lets session state leak into the
/// out-of-session handler.
#[test]
fn api_rev_independent_of_session_state_emu() {
fn api_rev_independent_of_session_state() {
use azihsm_ddi_tbor_types::SessionType;

let ctx = TestCtx::new();
Expand Down Expand Up @@ -99,15 +90,3 @@ fn api_rev_independent_of_session_state_emu() {
let post = ctx.tbor(&TborApiRevReq::new()).expect("ApiRev after close");
assert_eq!(post, EXPECTED);
}

#[cfg(all(feature = "mock", not(feature = "emu")))]
#[test]
fn unsupported_on_mock() {
use crate::harness::assertions::assert_unsupported_encoding;

let ctx = TestCtx::new();
let err = ctx
.tbor(&TborApiRevReq::new())
.expect_err("mock backend must not implement exec_op_tbor");
assert_unsupported_encoding(&err);
}
Loading
Loading