target/virt-ast1060-evb: add QEMU mctp-requester build target Add a virt-ast1060-evb configuration for the in-process SPDM requester, enabling integration testing under patched QEMU with Aspeed I2C emulation without requiring physical AST1060-EVB hardware. - system.json5: two-process layout (i2c_server + mctp_server_requester), 640 KB SRAM, ASPEED I2C MMIO at 0x7e7b0000/0x7e6e2000 - target.rs: semihosting shim; shutdown() exits QEMU with pass/fail code - BUILD.bazel: inline rust_app expansion (rust_app_codegen + app_linker_script + rust_binary) to avoid codegen_crate_name collision with the hw target in //services/mctp/server - services/mctp/server/BUILD.bazel: exports_files(["src/main.rs"]) so the virt BUILD can reference the shared entry point Signed-off-by: Anthony Rocha <anthony.rocha@amd.com>
diff --git a/services/mctp/server/BUILD.bazel b/services/mctp/server/BUILD.bazel index d90e598..384515f 100644 --- a/services/mctp/server/BUILD.bazel +++ b/services/mctp/server/BUILD.bazel
@@ -1,5 +1,7 @@ # Licensed under the Apache-2.0 license +exports_files(["src/main.rs"]) + load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") load("@pigweed//pw_kernel/tooling:rust_app.bzl", "rust_app") @@ -128,9 +130,12 @@ ], ) -# In-process SPDM requester variant. Same src/main.rs, different feature -# set. No separate system_image target yet — use the mctp:mctp image once -# it is re-wired, or build this target alone to verify compilation. +# In-process SPDM requester variant (AST1060-EVB hardware target). +# Same src/main.rs as mctp_server, different feature set. +# The virt-ast1060-evb variant is expanded inline in +# //target/virt-ast1060-evb/mctp-requester/BUILD.bazel to avoid a +# codegen_crate_name collision (two rust_app rules in the same Bazel package +# cannot share codegen_crate_name). rust_app( name = "mctp_server_requester", codegen_crate_name = "app_mctp_server_requester",
diff --git a/services/mctp/server/IMPLEMENTATION_PLAN.md b/services/mctp/server/IMPLEMENTATION_PLAN.md index ba935bc..2dadff8 100644 --- a/services/mctp/server/IMPLEMENTATION_PLAN.md +++ b/services/mctp/server/IMPLEMENTATION_PLAN.md
@@ -15,13 +15,16 @@ | 4 — safety net + counters | ✅ done | `AWAIT_STEP_BUDGET = 10_000`, `await_try!` macro, counters `req_send_ok` / `req_recv_ok` / `req_recv_pending`. | | 5 — Bazel target + system image | ✅ done (merged into Phase 2) | See Phase 2 note above. | | 6 — two-board integration test | ⏸ blocked | No on-target hardware available in this environment. See IMPLEMENTATION_PLAN.md §A for measurements to collect when hardware is accessible. | +| 6a — QEMU integration test (virt-ast1060-evb) | ✅ done | QEMU patched with ASPEED I2C emulation; `//target/virt-ast1060-evb/mctp-requester:mctp_requester_test` added. No two-board setup needed. | | 7 — docs + follow-up TODOs | ✅ done | [INITIALIZATION.md](INITIALIZATION.md) updated with requester glossary + setup steps; follow-up design refs annotated at the FSM in `main.rs`. | Build verification at end of Phase 7: ``` -bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp:mctp # responder — clean -bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp-requester:mctp_requester # requester — clean +bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp:mctp # responder (hw) — clean +bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp-requester:mctp_requester # requester (hw) — clean +bazel build --config=k_virt_ast1060_evb //target/virt-ast1060-evb/mctp-requester:mctp_requester # requester (QEMU) — clean +bazel test --config=k_virt_ast1060_evb //target/virt-ast1060-evb/mctp-requester:mctp_requester_test # requester QEMU test ``` Legend: @@ -44,11 +47,15 @@ **Developer commands:** ```bash -# Responder image (default) +# Responder image (default, hardware) bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp:mctp -# Requester image +# Requester image (hardware) bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp-requester:mctp_requester + +# Requester image (QEMU — requires patched QEMU with ASPEED I2C emulation) +bazel build --config=k_virt_ast1060_evb //target/virt-ast1060-evb/mctp-requester:mctp_requester +bazel test --config=k_virt_ast1060_evb //target/virt-ast1060-evb/mctp-requester:mctp_requester_test ``` Running any test or integration probe uses the same prefix @@ -437,11 +444,14 @@ --- -## Phase 6 — Integration Test +## Phase 6 — Integration Test (two-board, on-target hardware) **Goal**: a repeatable test that exercises the in-process requester against the in-process responder on the bench. +**Status**: ⏸ blocked — no on-target hardware available. See §A for +measurements to collect when hardware is accessible. + **Deliverables**: - Two-board test config: board A flashed with `mctp_server_requester` system image, board B flashed with @@ -471,6 +481,73 @@ --- +## Phase 6a — QEMU Integration Test (virt-ast1060-evb) + +**Goal**: automated QEMU test that exercises the full I2C + MCTP + SPDM +requester stack without physical hardware, using QEMU's ASPEED I2C +device emulation. + +**Prerequisite**: QEMU patched with ASPEED i2c device model (user confirmed +April 2026). + +**Deliverables**: +- `target/virt-ast1060-evb/mctp-requester/system.json5` — two-process + (i2c_server + mctp_server_requester) virt image with ASPEED I2C MMIO + mappings at the real hardware addresses (emulated by QEMU). +- `target/virt-ast1060-evb/mctp-requester/target.rs` — semihosting + kernel shim; `shutdown()` calls `exit(EXIT_SUCCESS/FAILURE)` so QEMU + terminates and the test harness can read the exit code. +- `target/virt-ast1060-evb/mctp-requester/BUILD.bazel` — `system_image`, + `system_image_test`, `target_codegen`, `target_linker_script`, + `rust_binary(name="target")` wired to `//target/virt-ast1060-evb` + platform and linker template. +- `services/mctp/server/BUILD.bazel` — `mctp_server_requester_virt` + rust_app, same feature set as `mctp_server_requester` but with + `system_config = "//target/virt-ast1060-evb/mctp-requester:system_config"` + so codegen (handle constants + linker script) is correct for QEMU. + +**Files**: +- `target/virt-ast1060-evb/mctp-requester/system.json5` (new) +- `target/virt-ast1060-evb/mctp-requester/target.rs` (new) +- `target/virt-ast1060-evb/mctp-requester/BUILD.bazel` (new) +- `services/mctp/server/BUILD.bazel` (add `mctp_server_requester_virt`) + +**Build / test commands**: + +```bash +bazel build --config=k_virt_ast1060_evb //target/virt-ast1060-evb/mctp-requester:mctp_requester +bazel test --config=k_virt_ast1060_evb //target/virt-ast1060-evb/mctp-requester:mctp_requester_test +``` + +**Exit criteria**: +- `bazel test` passes: QEMU starts, the FSM reaches `Done`, semihosting + `exit(EXIT_SUCCESS)` fires, QEMU terminates with code 0, Bazel marks + the test green. +- Responder hardware image (`//target/ast1060-evb/mctp:mctp`) and + hardware requester image (`//target/ast1060-evb/mctp-requester:mctp_requester`) + still build clean. + +**Key design notes**: +- `mctp_server_requester_virt` shares `codegen_crate_name = + "app_mctp_server_requester"` with the hardware target — the handle + layout is identical (I2C object is second in the process objects list + in both system configs), so no source changes are needed. +- Memory layout (640KB) mirrors `virt-ast1060-evb/spdm-req-resp-test`; + vector_table_size_bytes = 1280 (matches multi-process virt convention). +- ASPEED I2C MMIO mappings (`0x7e7b0000` / `0x7e6e2000`) are kept so + the i2c_server binary can run unmodified against the QEMU device model. + +**Risks / rollback**: +- *Risk*: QEMU's ASPEED I2C model doesn't emulate bus-2 slave-mode + loopback precisely enough for the polling loop. *Mitigation*: QEMU + patch is user-supplied and already tested; fall back to the two-board + (Phase 6) path if emulation fidelity is insufficient. +- *Rollback*: delete the new `target/virt-ast1060-evb/mctp-requester/` + package and remove `mctp_server_requester_virt` from services BUILD.bazel; + hardware targets are unaffected. + +--- + ## Phase 7 — Cleanup & Follow-Up Tickets **Goal**: close loose ends the design doc called out and make sure
diff --git a/target/virt-ast1060-evb/mctp-requester/BUILD.bazel b/target/virt-ast1060-evb/mctp-requester/BUILD.bazel new file mode 100644 index 0000000..c98f3a8 --- /dev/null +++ b/target/virt-ast1060-evb/mctp-requester/BUILD.bazel
@@ -0,0 +1,145 @@ +# Licensed under the Apache-2.0 license + +load("@pigweed//pw_kernel/tooling:system_image.bzl", "system_image", "system_image_test") +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:app_linker_script.bzl", "app_linker_script") +load("@pigweed//pw_kernel/tooling:rust_app.bzl", "rust_app_codegen") +load("@rules_rust//rust:defs.bzl", "rust_binary") +load("//target/virt-ast1060-evb:defs.bzl", "TARGET_COMPATIBLE_WITH") + +# --------------------------------------------------------------------------- +# System image: I2C server + MCTP server (in-process SPDM requester, QEMU) +# +# Requires QEMU patched with ASPEED I2C device emulation. Mirrors the +# hardware image at //target/ast1060-evb/mctp-requester:mctp_requester but +# targets the virt-ast1060-evb platform for automated QEMU testing. +# +# The mctp_server_requester app binary is expanded inline here (rather than +# as a rust_app in //services/mctp/server/BUILD.bazel) because two rust_app +# rules in the same Bazel package cannot share codegen_crate_name — expansion +# in the target package places the generated rules in a different namespace. +# --------------------------------------------------------------------------- + +system_image( + name = "mctp_requester", + apps = [ + "//services/i2c/server:i2c_server", + ":mctp_server_requester_app", + ], + kernel = ":target", + platform = "//target/virt-ast1060-evb", + system_config = ":system_config", + tags = ["kernel"], + visibility = ["//visibility:public"], +) + +system_image_test( + name = "mctp_requester_test", + image = ":mctp_requester", + target_compatible_with = TARGET_COMPATIBLE_WITH, +) + +filegroup( + name = "system_config", + srcs = ["system.json5"], + visibility = ["//visibility:public"], +) + +# --------------------------------------------------------------------------- +# Kernel target +# --------------------------------------------------------------------------- + +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/virt-ast1060-evb:linker_script_template", +) + +rust_binary( + name = "target", + srcs = [ + "target.rs", + ], + edition = "2024", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + deps = [ + ":codegen", + ":linker_script", + "@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", + "//target/virt-ast1060-evb:entry", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + "@rust_crates//:cortex-m-semihosting", + ], +) + +# --------------------------------------------------------------------------- +# App: MCTP Server (in-process SPDM requester) +# +# Expanded from rust_app macro inline so the codegen_crate_name +# "app_mctp_server_requester" lives in this package, not in +# //services/mctp/server, avoiding a rule-name collision. +# --------------------------------------------------------------------------- + +rust_app_codegen( + name = "app_mctp_server_requester", + app_name = "mctp_server_requester", + edition = "2024", + system_config = ":system_config", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, +) + +app_linker_script( + name = "mctp_server_requester_app.linker_script", + app_name = "mctp_server_requester", + system_config = ":system_config", + tags = ["kernel"], +) + +rust_binary( + name = "mctp_server_requester_app", + srcs = [ + "//services/mctp/server:src/main.rs", + ], + crate_features = [ + "i2c-polling", + "direct-client", + "in-process-requester", + ], + edition = "2024", + tags = ["kernel"], + target_compatible_with = TARGET_COMPATIBLE_WITH, + deps = [ + ":app_mctp_server_requester", + ":mctp_server_requester_app.linker_script", + "//services/i2c/api:i2c_api", + "//services/i2c/client:i2c_client", + "//services/mctp/api:mctp_api", + "//services/mctp/server:mctp_server_lib", + "//services/mctp/transport-i2c:mctp_transport_i2c", + "//services/spdm/transport-mctp:spdm_transport_mctp", + "//target/ast1060-evb/spdm-req-resp-test:mock_platform", + "@pigweed//pw_kernel/syscall:syscall_user", + "@pigweed//pw_kernel/userspace", + "@pigweed//pw_log/rust:pw_log", + "@pigweed//pw_status/rust:pw_status", + "@rust_crates//:mctp", + "@rust_crates//:mctp-lib", + "@oot_crates_no_std//:spdm-lib", + ], +)
diff --git a/target/virt-ast1060-evb/mctp-requester/system.json5 b/target/virt-ast1060-evb/mctp-requester/system.json5 new file mode 100644 index 0000000..f23df24 --- /dev/null +++ b/target/virt-ast1060-evb/mctp-requester/system.json5
@@ -0,0 +1,119 @@ +// Licensed under the Apache-2.0 license + +// Virtual AST1060-EVB MCTP Requester Configuration (QEMU) +// ARM Cortex-M4 with semihosting console. +// +// Requires QEMU patched with ASPEED I2C device emulation so that the i2c_server +// can drive bus 2 and the mctp_server_requester can exchange MCTP frames with a +// peer via the emulated I2C controller. +// +// Memory Layout (PMSAv7-friendly, power-of-2 aligned regions): +// 0x00000000 - 0x00000500: Vector table + kernel annotations (1280 bytes) +// 0x00000500 - 0x00020000: Kernel flash (~127KB) +// 0x00020000 - 0x00040000: i2c_server flash (128KB) +// 0x00040000 - 0x00060000: mctp_server_requester flash (128KB) +// 0x00060000 - 0x00080000: Kernel RAM (128KB) +// 0x00080000 - 0x00090000: i2c_server RAM (64KB) +// 0x00090000 - 0x000A0000: mctp_server_requester RAM (64KB) +// +// Total: 640KB (fits within the virtual AST1060's 640KB SRAM window). +{ + arch: { + type: "armv7m", + vector_table_start_address: 0x00000000, + vector_table_size_bytes: 1280, + }, + kernel: { + flash_start_address: 0x00000500, + flash_size_bytes: 129792, // ~127KB, ends at 0x20000 + ram_start_address: 0x00060000, + ram_size_bytes: 131072, // 128KB + }, + apps: [ + // ──── I2C Server ──── + // Drives the QEMU-emulated ASPEED I2C controller on bus 2. + // Object layout intentionally mirrors //target/ast1060-evb/i2c:system_config + // so the //services/i2c/server:i2c_server rust_app's handle constants are + // consistent across both system images. + { + name: "i2c_server", + flash_size_bytes: 131072, // 128KB + ram_size_bytes: 65536, // 64KB + processes: [ + { + name: "i2c server process", + objects: [ + { + name: "I2C", + type: "channel_handler", + }, + { + name: "WG", + type: "wait_group", + }, + ], + threads: [ + { + name: "i2c server thread", + stack_size_bytes: 4096, + }, + ], + memory_mappings: [ + { + // ASPEED I2C peripheral block emulated by patched QEMU. + // Covers I2Cglobal through I2cFilter (all 14 controllers + buffers). + name: "i2c_regs", + type: "device", + start_address: 0x7e7b0000, + size_bytes: 0x4000, + }, + { + // SCU block — scu310 at +0x310 read by ClockConfig::from_hardware() + // to derive APB clock frequency. + name: "scu", + type: "device", + start_address: 0x7e6e2000, + size_bytes: 0x1000, + }, + ], + }, + ], + }, + // ──── MCTP Server (in-process SPDM requester) ──── + // Object layout mirrors //target/ast1060-evb/mctp-requester:system_config + // so the //services/mctp/server:mctp_server_requester_virt rust_app's + // handle constants are consistent with the hardware image. + { + name: "mctp_server_requester", + flash_size_bytes: 131072, // 128KB + ram_size_bytes: 65536, // 64KB (Router state + SPDM context + buffers) + processes: [ + { + name: "mctp server process", + objects: [ + { + name: "MCTP", + type: "channel_handler", + }, + { + name: "I2C", + type: "channel_initiator", + handler_process: "i2c server process", + handler_object_name: "I2C", + }, + { + name: "WG", + type: "wait_group", + }, + ], + threads: [ + { + name: "mctp server thread", + stack_size_bytes: 32768, + }, + ], + }, + ], + }, + ], +}
diff --git a/target/virt-ast1060-evb/mctp-requester/target.rs b/target/virt-ast1060-evb/mctp-requester/target.rs new file mode 100644 index 0000000..5261816 --- /dev/null +++ b/target/virt-ast1060-evb/mctp-requester/target.rs
@@ -0,0 +1,38 @@ +// Licensed under the Apache-2.0 license + +//! Virtual AST1060-EVB MCTP Requester Target (QEMU) +//! +//! Kernel entry shim for the virt-ast1060-evb mctp-requester system image. +//! Uses cortex-m-semihosting to signal test pass/fail back to QEMU. + +#![no_std] +#![no_main] + +use cortex_m_semihosting::debug::{EXIT_FAILURE, EXIT_SUCCESS, exit}; +use target_common::{TargetInterface, declare_target}; +use {console_backend as _, entry as _}; + +pub struct Target {} + +impl TargetInterface for Target { + const NAME: &'static str = "virt-AST1060-EVB MCTP Requester"; + + fn main() -> ! { + codegen::start(); + #[expect(clippy::empty_loop)] + loop {} + } + + fn shutdown(code: u32) -> ! { + pw_log::info!("Shutting down with code {}", code as u32); + let status = match code { + 0 => EXIT_SUCCESS, + _ => EXIT_FAILURE, + }; + exit(status); + #[expect(clippy::empty_loop)] + loop {} + } +} + +declare_target!(Target);