| # MCTP Server — Initialization and Variable Glossary |
| |
| Source: `services/mctp/server/src/main.rs` |
| |
| --- |
| |
| ## Build Modes |
| |
| The server compiles into one of several mutually exclusive modes, selected by |
| Cargo feature flags set in the Bazel `rust_app` target. `in-process-requester` |
| and `in-process-responder` are mutually exclusive and the binary emits a |
| `compile_error!` if both are enabled at once. |
| |
| | Feature flags | Mode | IPC served? | SPDM role in-process | |
| |---|---|---|---| |
| | _(none)_ | Notification | Yes | none | |
| | `i2c-polling` | Polling | No | none | |
| | `i2c-polling` + `direct-client` + `in-process-responder` | Polling + SPDM | No | responder | |
| | `i2c-polling` + `direct-client` + `in-process-requester` | Polling + SPDM | No | requester | |
| |
| Build targets: |
| |
| ``` |
| bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp:mctp # responder image |
| bazel build --config=k_ast1060_evb //target/ast1060-evb/mctp-requester:mctp_requester # requester image |
| ``` |
| |
| --- |
| |
| ## Initialization Sequence |
| |
| ### Shared constants (always) |
| |
| ``` |
| OWN_EID = 0x08 — MCTP Endpoint ID assigned to this device |
| OWN_I2C_ADDR = 0x10 — I2C 7-bit slave address of this device |
| ``` |
| |
| --- |
| |
| ### Polling mode (`i2c-polling`) |
| |
| Steps execute once at startup before the event loop begins. |
| |
| ``` |
| 1. IpcI2cClient::new(handle::I2C) |
| → Opens IPC channel to the I2C server (for configuration and rx). |
| Stored in `i2c`. |
| |
| 2. I2cAddress::new(OWN_I2C_ADDR) |
| → Validates the address value fits in 7 bits. |
| Stored in `addr`. |
| |
| 3. i2c.configure_target_address(BUS_2, addr) |
| → Programs the I2C hardware to respond to OWN_I2C_ADDR on bus 2. |
| FATAL if this fails (I2C server unreachable or bad address). |
| |
| 4. i2c.enable_receive(BUS_2) |
| → Arms the I2C slave receive FIFO. After this the hardware will |
| accept incoming frames into the I2C server's buffer. |
| FATAL if this fails. |
| |
| 5. IpcI2cClient::new(handle::I2C) [second instance] |
| → A separate IPC channel to the I2C server, used exclusively for |
| outbound I2C master writes. Ownership is transferred to I2cSender. |
| |
| 6. I2cSender::new(i2c_client_2, BUS_2, OWN_I2C_ADDR) |
| → Outbound MCTP-over-I2C transport. Implements mctp_lib::Sender. |
| Stored in `sender`. |
| |
| 7. MctpI2cReceiver::new(OWN_I2C_ADDR) |
| → Inbound frame decoder. Strips I2C framing to produce raw MCTP |
| packets. Stored in `receiver`. |
| |
| 8a. (direct-client only) RefCell::new(Server::new(Eid(OWN_EID), 0, sender)) |
| → MCTP router wrapped in RefCell for shared &self access. |
| Stored in `server_cell`. |
| |
| 8b. (no direct-client) Server::new(Eid(OWN_EID), 0, sender) |
| → MCTP router, exclusively owned. |
| Stored in `server_plain`. |
| |
| ── SPDM setup (in-process-responder) ────────────────────────────────────── |
| |
| 9. DirectMctpClient::new(&server_cell) |
| → In-process MctpClient. Calls server_cell methods directly; |
| no IPC channel to a separate process. |
| Consumed immediately by step 10. |
| |
| 10. MctpSpdmTransport::new_responder(client) |
| → SPDM transport layer in responder mode (listens for msg_type=0x05). |
| Stored in `transport`. |
| |
| 11. transport.init_sequence() |
| → Calls server.listener(0x05) in-process, reserving a listener handle |
| in the router for SPDM messages. |
| FATAL if the router's listener table is full (MAX_LISTENERS=8). |
| |
| 12. MockCertStore / MockHash / MockRng / MockEvidence |
| → Placeholder platform implementations. No crypto hardware access. |
| Stored in `cert_store`, `hash`, `m1_hash`, `l1_hash`, `rng`, `evidence`. |
| |
| 13. CapabilityFlags { CERT_CAP, CHAL_CAP, MEAS_CAP=2, MEAS_FRESH_CAP, CHUNK_CAP } |
| → Bitmask of SPDM capabilities advertised to the requester. |
| Stored in `flags`, then moved into `capabilities`. |
| |
| 14. DeviceCapabilities { ct_exponent=0, data_transfer_size=1024, |
| max_spdm_msg_size=4096 } |
| → SPDM device capability parameters. Stored in `capabilities`. |
| |
| 15. SUPPORTED_VERSIONS = [V12, V13] (static) |
| → SPDM protocol versions this responder accepts. |
| |
| 16. LocalDeviceAlgorithms { SHA-384, ECDSA-P384, DMTF measurements } |
| → Cryptographic algorithm selections. Stored in `algorithms`. |
| |
| 17. SpdmContext::new(...) |
| → Constructs the full SPDM state machine. Takes references to all |
| platform impls (transport, cert_store, hash, rng, evidence). |
| FATAL if any required platform impl is misconfigured. |
| Stored in `spdm_ctx`. |
| |
| 18. [0u8; MAX_PAYLOAD_SIZE] + MessageBuf::new(&mut spdm_buf) |
| → Fixed-size scratch buffer for SPDM message encoding/decoding. |
| msg_buf wraps spdm_buf for the lifetime of the loop. |
| Both declared outside the loop to avoid borrow-checker conflict. |
| ``` |
| |
| ── SPDM setup (in-process-requester) ────────────────────────────────────── |
| |
| Mirrors steps 9–18 of the responder path; only the differences are shown. |
| |
| ``` |
| 9. DirectMctpClient::new(&server_cell) [identical to responder] |
| |
| 10. MctpSpdmTransport::new_requester(client, REMOTE_RESPONDER_EID) |
| → SPDM transport layer in requester mode (targets EID 42). |
| `REMOTE_RESPONDER_EID = 42` matches `spdm_requester.rs`. |
| |
| 11. transport.init_sequence() |
| → Calls server.req(REMOTE_RESPONDER_EID) in-process, reserving a |
| request handle in the router. |
| FATAL if the router's request table is full. |
| |
| 12. MockCertStore / MockHash×3 / MockRng / MockEvidence [identical] |
| 12a. DemoPeerCertStore::default() [requester-only] |
| → Provides the peer's certificate chain to SPDM; required even if |
| only VCA is exercised. Stored in `peer_cert_store`. |
| |
| 13. CapabilityFlags { cert_cap=1, chal_cap=1, meas_cap=0, chunk_cap=1 } |
| → No `meas_fresh_cap` (requester does not publish measurements). |
| |
| 14. DeviceCapabilities { include_supported_algorithms: false } |
| → V1.3 param1 bit 2 is rejected by the responder as reserved; leave |
| it unset so algorithm negotiation goes through NEGOTIATE_ALGORITHMS. |
| |
| 15. SUPPORTED_VERSIONS = [V12, V13] [identical] |
| 16. LocalDeviceAlgorithms (SHA-384, ECDSA-P384) [identical] |
| |
| 17. SpdmContext::new(..., Some(&mut peer_cert_store), ...) |
| → Requester passes `Some(peer_cert_store)` (responder passes `None`). |
| |
| 18. spdm_buf + msg_buf [identical] |
| |
| 19. req_state = ReqState::SendVersion [requester-only] |
| → Initial FSM state. The Phase-2 step below walks through |
| SendVersion → AwaitVersion → SendCapabilities → AwaitCapabilities → |
| SendAlgorithms → AwaitAlgorithms → Done (or → Failed on error / |
| AWAIT_STEP_BUDGET exhaustion). |
| ``` |
| |
| The polling loop then runs two phases per iteration: |
| |
| ``` |
| Phase 1 (always): drain I2C → server.inbound() → router reassembly |
| Phase 2 (responder): spdm_ctx.responder_process_message() — Err on no data |
| Phase 2 (requester): FSM step — advance on Ok, stay on Err, transition to |
| Failed if await_steps hits AWAIT_STEP_BUDGET (10_000) |
| ``` |
| |
| --- |
| |
| ### Notification mode (default, no `i2c-polling`) |
| |
| ``` |
| 1. IpcI2cClient::new(handle::I2C) |
| → IPC channel to I2C server for configuration, notifications, and |
| pending-message retrieval. Stored in `i2c_notify`. |
| |
| 2-4. Same address configuration as polling mode (steps 2-4 above). |
| |
| 5. i2c_notify.register_notification(BUS_2, 0) |
| → Requests the I2C server to post a USER signal to this process |
| when a slave-mode frame arrives. FATAL if it fails. |
| |
| 6. IpcI2cClient::new(handle::I2C) [second instance] |
| → Separate channel for outbound sends, transferred to I2cSender. |
| |
| 7-8. I2cSender and MctpI2cReceiver as in polling mode. |
| |
| 9. Server::new(Eid(OWN_EID), 0, sender) |
| → MCTP router. Stored in `server`. |
| |
| 10. [0u8; MAX_REQUEST_SIZE/MAX_RESPONSE_SIZE/MAX_PAYLOAD_SIZE] |
| → IPC wire buffers. Stored in `request_buf`, `response_buf`, `recv_buf`. |
| |
| 11. syscall::wait_group_add(WG, MCTP, READABLE, user_data=0) |
| → Register the IPC channel as event source 0 in the WaitGroup. |
| |
| 12. syscall::wait_group_add(WG, I2C, USER, user_data=1) |
| → Register the I2C notification signal as event source 1. |
| ``` |
| |
| --- |
| |
| ## Variable Glossary |
| |
| ### Constants |
| |
| | Name | Value | Meaning | |
| |---|---|---| |
| | `OWN_EID` | `0x08` | MCTP Endpoint ID of this device | |
| | `OWN_I2C_ADDR` | `0x10` | I2C 7-bit slave address of this device | |
| | `REMOTE_RESPONDER_EID` | `42` | MCTP EID of the peer responder (requester-only) | |
| | `AWAIT_STEP_BUDGET` | `10_000` | Max FSM steps per Await state before Failed (requester-only) | |
| |
| ### Polling mode variables |
| |
| | Name | Type | Lifetime | Purpose | |
| |---|---|---|---| |
| | `i2c` | `IpcI2cClient` | setup + loop | I2C IPC channel for configuration, `wait_for_messages`, rx | |
| | `addr` | `I2cAddress` | setup only | Validated I2C address; consumed by `configure_target_address` | |
| | `sender` | `I2cSender` | loop (via server) | Outbound MCTP-over-I2C; owns a second `IpcI2cClient` | |
| | `receiver` | `MctpI2cReceiver` | loop | Decodes I2C slave frames into raw MCTP packets | |
| | `server_cell` | `RefCell<Server>` | loop | MCTP router; RefCell allows shared borrow with DirectMctpClient | |
| | `server_plain` | `Server` | loop | MCTP router when `direct-client` is not enabled | |
| | `transport` | `MctpSpdmTransport` | loop (via spdm_ctx) | SPDM transport; calls server_cell through DirectMctpClient | |
| | `cert_store` | `MockCertStore` | loop (via spdm_ctx) | Provides certificate chain to SPDM protocol | |
| | `hash` | `MockHash` | loop (via spdm_ctx) | Main transcript hash | |
| | `m1_hash` | `MockHash` | loop (via spdm_ctx) | M1 measurement hash (measurements with signature) | |
| | `l1_hash` | `MockHash` | loop (via spdm_ctx) | L1 challenge hash | |
| | `rng` | `MockRng` | loop (via spdm_ctx) | Random number source (nonces, challenge data) | |
| | `evidence` | `MockEvidence` | loop (via spdm_ctx) | Device measurement evidence provider | |
| | `flags` | `CapabilityFlags` | setup only | SPDM capability bitmask; consumed by `capabilities` | |
| | `capabilities` | `DeviceCapabilities` | setup only | SPDM capability parameters; consumed by `SpdmContext::new` | |
| | `SUPPORTED_VERSIONS` | `[SpdmVersion; 2]` | `'static` | SPDM versions [V1.2, V1.3]; must be static due to spdm-lib lifetime | |
| | `algorithms` | `LocalDeviceAlgorithms` | setup only | Algorithm selections; consumed by `SpdmContext::new` | |
| | `spdm_ctx` | `SpdmContext` | loop | Full SPDM state machine; processes one request per `process_message` call | |
| | `spdm_buf` | `[u8; MAX_PAYLOAD_SIZE]` | loop | Raw byte storage for SPDM message encoding | |
| | `msg_buf` | `MessageBuf` | loop | Cursor over `spdm_buf`; reset each iteration via `msg_buf.reset()` | |
| |
| ### Notification mode variables |
| |
| | Name | Type | Lifetime | Purpose | |
| |---|---|---|---| |
| | `i2c_notify` | `IpcI2cClient` | loop | I2C channel for slave config, notification registration, and `get_pending_messages` | |
| | `addr` | `I2cAddress` | setup only | Validated I2C address | |
| | `sender` | `I2cSender` | loop (via server) | Outbound MCTP-over-I2C | |
| | `receiver` | `MctpI2cReceiver` | loop | Decodes I2C slave frames | |
| | `server` | `Server` | loop | MCTP router | |
| | `request_buf` | `[u8; MAX_REQUEST_SIZE]` | loop | IPC receive buffer for inbound MCTP operation requests | |
| | `response_buf` | `[u8; MAX_RESPONSE_SIZE]` | loop | IPC transmit buffer for operation responses | |
| | `recv_buf` | `[u8; MAX_PAYLOAD_SIZE]` | loop | Scratch buffer passed to `dispatch_mctp_op` for message payload | |
| |
| ### Fault-isolation counters (polling mode, loop-scoped) |
| |
| | Name | Increments on | Logged when | |
| |---|---|---| |
| | `i2c_pkt` | Successful I2C frame decode | Every packet (at `debug` level) | |
| | `i2c_recv_err` | `wait_for_messages` returns `Err` | 1st occurrence, then every 16th | |
| | `decode_err` | `receiver.decode()` failure | 1st occurrence, then every 16th | |
| | `inbound_err` | `server.inbound()` returns `Err` | 1st occurrence, then every 16th | |
| | `spdm_ok` | `responder_process_message` returns `Ok` (responder-only) | Every success (at `info` level) | |
| | `spdm_err` | `responder_process_message` returns `Err` (responder-only) | 1st occurrence, then every 256th (at `debug` level — mostly TimedOut noise) | |
| |
| ### Requester FSM variables (`in-process-requester`, loop-scoped) |
| |
| | Name | Type | Purpose | |
| |---|---|---| |
| | `req_state` | `ReqState` | Current FSM state (SendVersion → AwaitAlgorithms → Done/Failed) | |
| | `req_send_ok` | `u32` | Successful `requester_send_request` calls (3 on happy path) | |
| | `req_recv_ok` | `u32` | Successful `requester_process_message` calls (3 on happy path) | |
| | `req_recv_pending` | `u32` | `requester_process_message` returned `Err` (mostly TimedOut) | |
| | `await_steps` | `u32` | Consecutive Phase-2 steps in the current Await state; reset on transition; triggers Failed at `AWAIT_STEP_BUDGET` | |
| | `peer_cert_store` | `DemoPeerCertStore` | Peer cert chain handed to `SpdmContext::new` (requester-only) | |
| |
| --- |
| |
| ## Handle IDs (`app_mctp_server::handle` or `app_mctp_server_requester::handle`) |
| |
| Each Bazel `rust_app` target generates its own codegen crate: |
| `app_mctp_server` for the responder image, `app_mctp_server_requester` for the |
| requester image. `src/main.rs` picks the right one with a cfg-gated `use`. |
| The numeric values are target-specific and should not be hardcoded. |
| |
| | Handle | Used by | Purpose | |
| |---|---|---| |
| | `handle::I2C` | Both modes | IPC channel to `i2c_server` process | |
| | `handle::MCTP` | Notification mode only | IPC channel handler for MCTP client connections | |
| | `handle::WG` | Notification mode only | WaitGroup multiplexing IPC + I2C notification | |