add initial test for spdm
diff --git a/target/ast10x0/tests/spdm/mctp_server/BUILD.bazel b/target/ast10x0/tests/spdm/mctp_server/BUILD.bazel new file mode 100644 index 0000000..f88470f --- /dev/null +++ b/target/ast10x0/tests/spdm/mctp_server/BUILD.bazel
@@ -0,0 +1,31 @@ +# Licensed under the Apache-2.0 license +# SPDX-License-Identifier: Apache-2.0 +# +# MCTP Server for SPDM testing +# +# STUB IMPLEMENTATION — This is a minimal MCTP server that speaks the +# openprot_mctp_api::wire protocol with deterministic responses for testing. +# +# TODO: Replace this stub with a real MCTP server implementation when +# hardware transport is integrated. The rust_app target in the test +# BUILD.bazel should only need to change its dependency from this +# package to the real implementation. + +load("@pigweed//pw_kernel/tooling:rust_app.bzl", "rust_app") +load("//target/ast10x0:defs.bzl", "TARGET_COMPATIBLE_WITH") + +rust_app( + name = "mctp_server", + srcs = ["src/main.rs"], + codegen_crate_name = "app_mctp_server", + edition = "2024", + system_config = "//target/ast10x0/tests/spdm/responder:system_config", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + visibility = ["//target/ast10x0/tests/spdm:__subpackages__"], + deps = [ + "//services/mctp/api:mctp_api", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + ], +)
diff --git a/target/ast10x0/tests/spdm/mctp_server/src/main.rs b/target/ast10x0/tests/spdm/mctp_server/src/main.rs new file mode 100644 index 0000000..e45af07 --- /dev/null +++ b/target/ast10x0/tests/spdm/mctp_server/src/main.rs
@@ -0,0 +1,130 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +//! MCTP Server stub for SPDM Responder testing. +//! +//! STUB IMPLEMENTATION — This is a minimal MCTP server that speaks the +//! `openprot_mctp_api::wire` protocol over a Pigweed IPC channel. +//! It provides deterministic responses for testing SPDM over MCTP. +//! +//! TODO: Replace with real MCTP server when hardware transport is integrated. +//! +//! ## Supported Operations +//! +//! | Op | Response | +//! |-----------|----------------------------------------------------| +//! | SetEid | Success | +//! | GetEid | Success, eid = 8 | +//! | Listener | Success, handle = 1 | +//! | Req | Success, handle = 2 | +//! | Recv | Success, returns pending SPDM request (if any) | +//! | Send | Success, buffers response for next recv | +//! | Unbind | Success | + +#![no_main] +#![no_std] + +use app_mctp_server::handle; +use openprot_mctp_api::wire::{ + self, MctpOp, MctpRequestHeader, MAX_REQUEST_SIZE, MAX_RESPONSE_SIZE, +}; +use openprot_mctp_api::ResponseCode; +use userspace::syscall::Signals; +use userspace::time::Instant; +use userspace::{entry, syscall}; + +const LISTENER_HANDLE: u32 = 1; +const REQ_HANDLE: u32 = 2; +const LOCAL_EID: u8 = 8; + +#[entry] +fn entry() { + pw_log::info!("MCTP server stub starting"); + + let mut req_buf = [0u8; MAX_REQUEST_SIZE]; + let mut resp_buf = [0u8; MAX_RESPONSE_SIZE]; + + if syscall::wait_group_add(handle::WG, handle::MCTP, Signals::READABLE, 0usize).is_err() { + pw_log::error!("Failed to add MCTP channel to wait group"); + loop {} + } + + loop { + let _ = syscall::object_wait(handle::WG, Signals::READABLE, Instant::MAX); + + let len = match syscall::channel_read(handle::MCTP, 0, &mut req_buf) { + Ok(n) => n, + Err(_) => continue, + }; + + let header = match MctpRequestHeader::from_bytes(&req_buf[..len]) { + Some(h) => h, + None => { + let rlen = + wire::encode_error_response(&mut resp_buf, ResponseCode::BadArgument) + .unwrap_or(0); + let _ = syscall::channel_respond(handle::MCTP, &resp_buf[..rlen]); + continue; + } + }; + + let resp_len = dispatch(&header, &req_buf[..len], &mut resp_buf); + let _ = syscall::channel_respond(handle::MCTP, &resp_buf[..resp_len]); + } +} + +fn dispatch(header: &MctpRequestHeader, _req: &[u8], resp: &mut [u8]) -> usize { + let op = match header.operation() { + Some(op) => op, + None => { + return wire::encode_error_response(resp, ResponseCode::BadArgument).unwrap_or(0); + } + }; + + match op { + MctpOp::SetEid => { + pw_log::debug!("SetEid: {}", header.eid as u32); + wire::encode_success_response(resp).unwrap_or(0) + } + + MctpOp::GetEid => { + pw_log::debug!("GetEid -> {}", LOCAL_EID as u32); + wire::encode_get_eid_response(resp, LOCAL_EID).unwrap_or(0) + } + + MctpOp::Listener => { + pw_log::debug!("Listener: msg_type={}", header.msg_type as u32); + wire::encode_handle_response(resp, LISTENER_HANDLE).unwrap_or(0) + } + + MctpOp::Req => { + pw_log::debug!("Req: eid={}", header.eid as u32); + wire::encode_handle_response(resp, REQ_HANDLE).unwrap_or(0) + } + + MctpOp::Recv => { + pw_log::debug!("Recv: handle={}", header.handle as u32); + // Return a minimal SPDM GET_VERSION request for testing + // SPDM message type = 0x05, SPDMVersion=0x10, RequestResponseCode=0x84 (GET_VERSION) + let spdm_get_version: [u8; 4] = [0x10, 0x84, 0x00, 0x00]; + wire::encode_recv_response(resp, 0x05, false, LOCAL_EID, 0, &spdm_get_version) + .unwrap_or(0) + } + + MctpOp::Send => { + pw_log::debug!("Send: msg_type={}", header.msg_type as u32); + wire::encode_send_response(resp, 0).unwrap_or(0) + } + + MctpOp::Unbind => { + pw_log::debug!("Unbind: handle={}", header.handle as u32); + wire::encode_success_response(resp).unwrap_or(0) + } + } +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + pw_log::error!("MCTP server panic"); + loop {} +}
diff --git a/target/ast10x0/tests/spdm/responder/BUILD.bazel b/target/ast10x0/tests/spdm/responder/BUILD.bazel new file mode 100644 index 0000000..1fe08fc --- /dev/null +++ b/target/ast10x0/tests/spdm/responder/BUILD.bazel
@@ -0,0 +1,100 @@ +# Licensed under the Apache-2.0 license +# SPDX-License-Identifier: Apache-2.0 + +load("@pigweed//pw_kernel/tooling:rust_app.bzl", "rust_app") +load("@pigweed//pw_kernel/tooling:system_image.bzl", "system_image") +load("@pigweed//pw_kernel/tooling:target_codegen.bzl", "target_codegen") +load("@pigweed//pw_kernel/tooling:target_linker_script.bzl", "target_linker_script") +load("@pigweed//pw_kernel/tooling/panic_detector:rust_binary_no_panics_test.bzl", "rust_binary_no_panics_test") +load("@rules_rust//rust:defs.bzl", "rust_binary") +load("//target/ast10x0:defs.bzl", "TARGET_COMPATIBLE_WITH") + +# ── System configuration ─────────────────────────────────────────────────────── + +filegroup( + name = "system_config", + srcs = ["system.json5"], + visibility = ["//target/ast10x0/tests/spdm:__subpackages__"], +) + +# ── Kernel image ─────────────────────────────────────────────────────────────── + +target_codegen( + name = "codegen", + arch = "@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m", + system_config = ":system_config", + target_compatible_with = TARGET_COMPATIBLE_WITH, +) + +target_linker_script( + name = "linker_script", + system_config = ":system_config", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + template = "//target/ast10x0:linker_script_template", +) + +rust_binary( + name = "target", + srcs = ["target.rs"], + edition = "2024", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + deps = [ + ":codegen", + ":linker_script", + "//target/ast10x0:entry", + "@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m", + "@pigweed//pw_kernel/kernel", + "@pigweed//pw_kernel/subsys/console:console_backend", + "@pigweed//pw_kernel/target:target_common", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + ], +) + +# ── SPDM Responder app ───────────────────────────────────────────────────────── +# SPDM responder using spdm-lib; listens for SPDM requests over MCTP. + +rust_app( + name = "spdm_responder", + srcs = ["spdm_responder_main.rs"], + codegen_crate_name = "app_spdm_responder", + edition = "2024", + system_config = ":system_config", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + deps = [ + "//services/mctp/api:mctp_api", + "//services/mctp/client-ipc:mctp_client_ipc", + "//services/spdm/responder:spdm_responder_lib", + "//services/spdm/transport-mctp:spdm_transport_mctp", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + "@pigweed//pw_status/rust:pw_status", + "@rust_crates//:spdm-lib", + ], +) + +# ── System image ─────────────────────────────────────────────────────────────── + +system_image( + name = "spdm_responder_image", + apps = [ + # TODO: Replace with real MCTP server when hardware transport is integrated + "//target/ast10x0/tests/spdm/mctp_server", + ":spdm_responder", + ], + kernel = ":target", + platform = "//target/ast10x0", + system_config = ":system_config", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + visibility = ["//visibility:public"], +) + +rust_binary_no_panics_test( + name = "no_panics_test", + binary = ":spdm_responder_image", + tags = ["kernel"], +)
diff --git a/target/ast10x0/tests/spdm/responder/spdm_responder_main.rs b/target/ast10x0/tests/spdm/responder/spdm_responder_main.rs new file mode 100644 index 0000000..0021117 --- /dev/null +++ b/target/ast10x0/tests/spdm/responder/spdm_responder_main.rs
@@ -0,0 +1,87 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +//! SPDM Responder test application. +//! +//! This application exercises the SPDM responder implementation using +//! spdm-lib over the MCTP transport layer. +//! +//! ## Test Flow +//! +//! 1. Initialize MCTP transport via IPC to mctp_server +//! 2. Create SPDM responder with platform implementations +//! 3. Process incoming SPDM messages +//! 4. Report pass/fail via debug_shutdown + +#![no_main] +#![no_std] + +use app_spdm_responder::handle; +use openprot_mctp_api::stack::Stack; +use openprot_mctp_client_ipc::IpcMctpClient; +use openprot_spdm_transport_mctp::MctpSpdmTransport; +use pw_status::Error; +use spdm_lib::platform::transport::SpdmTransport; +use userspace::{entry, syscall}; + +#[entry] +fn entry() { + match run() { + Ok(()) => { + pw_log::info!("SPDM responder test PASSED"); + let _ = syscall::debug_shutdown(Ok(())); + } + Err(e) => { + pw_log::error!("SPDM responder test FAILED: {}", e as u32); + let _ = syscall::debug_shutdown(Err(Error::Internal)); + } + } + loop {} +} + +fn run() -> Result<(), u32> { + pw_log::info!("SPDM responder test starting"); + + // Create MCTP client over IPC + let mctp_client = IpcMctpClient::new(handle::MCTP); + let stack = Stack::new(mctp_client); + + // Set local EID + stack.set_eid(8).map_err(|e| { + pw_log::error!("Stack set_eid failed: {}", e.code as u32); + 1u32 + })?; + + pw_log::info!("MCTP stack initialized with EID 8"); + + // Create SPDM transport in responder mode + let mut transport = MctpSpdmTransport::new_responder(&stack); + + // Initialize transport (registers listener for SPDM message type) + transport.init_sequence().map_err(|_| { + pw_log::error!("Transport init failed"); + 2u32 + })?; + + pw_log::info!("SPDM transport initialized"); + + // For now, just verify we can initialize the transport. + // Full responder testing requires platform implementations for: + // - CertStore (certificate management) + // - Hash (SHA-384/512) + // - RNG (random number generation) + // - Evidence (measurements) + // + // TODO: Add stub implementations and exercise full SPDM flow. + + pw_log::info!("SPDM responder initialization complete"); + + Ok(()) +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + pw_log::error!("SPDM responder panic"); + let _ = syscall::debug_shutdown(Err(Error::Internal)); + loop {} +}
diff --git a/target/ast10x0/tests/spdm/responder/system.json5 b/target/ast10x0/tests/spdm/responder/system.json5 new file mode 100644 index 0000000..3314a1c --- /dev/null +++ b/target/ast10x0/tests/spdm/responder/system.json5
@@ -0,0 +1,81 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +// AST10x0 SPDM Responder test system image. +// +// Two-process layout: +// +// mctp_server — MCTP channel handler (stub for testing) +// spdm_responder — SPDM responder using spdm-lib +// +// Memory map (AST10x0: 768 KB SRAM, no XIP): +// 0x00000000 - 0x00000500 vector table (1280 B) +// 0x00000500 - 0x00020500 kernel flash (~128 KB) +// 0x00020500 - 0x00060500 app flash (256 KB total; 128 KB per app) +// 0x00060000 - 0x00080000 kernel RAM (128 KB) +// 0x00080000 - 0x000C0000 app RAM (256 KB total) +{ + arch: { + type: "armv7m", + vector_table_start_address: 0x00000000, + vector_table_size_bytes: 1280, + }, + kernel: { + flash_start_address: 0x00000500, + flash_size_bytes: 129792, + ram_start_address: 0x00060000, + ram_size_bytes: 131072, + }, + apps: [ + { + name: "mctp_server", + flash_size_bytes: 131072, + processes: [ + { + name: "mctp_server_process", + ram_size_bytes: 32768, + objects: [ + { + name: "wg", + type: "wait_group", + }, + { + name: "mctp", + type: "channel_handler", + }, + ], + threads: [ + { + name: "mctp_server_thread", + kernel_stack_size_bytes: 4096, + }, + ], + }, + ], + }, + { + name: "spdm_responder", + flash_size_bytes: 131072, + processes: [ + { + name: "spdm_responder_process", + ram_size_bytes: 65536, + objects: [ + { + name: "mctp", + type: "channel_initiator", + handler_process: "mctp_server_process", + handler_object_name: "mctp", + }, + ], + threads: [ + { + name: "spdm_responder_thread", + kernel_stack_size_bytes: 4096, + }, + ], + }, + ], + }, + ], +}
diff --git a/target/ast10x0/tests/spdm/responder/target.rs b/target/ast10x0/tests/spdm/responder/target.rs new file mode 100644 index 0000000..b7b25c9 --- /dev/null +++ b/target/ast10x0/tests/spdm/responder/target.rs
@@ -0,0 +1,36 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +//! Kernel target for the SPDM Responder test. + +#![no_std] +#![no_main] + +use console_backend::console_backend_write_all; +use entry as _; +use target_common::{declare_target, TargetInterface}; + +pub struct Target {} + +impl TargetInterface for Target { + const NAME: &'static str = "AST10x0 SPDM Responder test"; + + fn main() -> ! { + codegen::start(); + #[expect(clippy::empty_loop)] + loop {} + } + + fn shutdown(code: u32) -> ! { + let sentinel: &[u8] = if code == 0 { + b"TEST_RESULT:PASS\n" + } else { + b"TEST_RESULT:FAIL\n" + }; + let _ = console_backend_write_all(sentinel); + #[expect(clippy::empty_loop)] + loop {} + } +} + +declare_target!(Target);