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
16 changes: 16 additions & 0 deletions services/pldm/ipc-api/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")

rust_library(
name = "pldm_ipc_api",
srcs = glob(["src/**/*.rs"]),
edition = "2024",
visibility = ["//visibility:public"],
)

rust_test(
name = "pldm_ipc_api_test",
crate = ":pldm_ipc_api",
)
138 changes: 138 additions & 0 deletions services/pldm/ipc-api/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! Error types for the PLDM IPC wire protocol.

use core::fmt;

/// Wire-level decode/encode error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WireError {
/// Output buffer too small for the encoded message.
BufferTooSmall,
/// Payload exceeds the maximum allowed size.
PayloadTooLarge,
/// Unrecognized operation code.
InvalidOpcode(u8),
/// Input buffer too short for a complete header or payload.
Truncated,
/// Unrecognized enum value (status discriminant, deny reason, etc).
InvalidValue(u8),
}

impl fmt::Display for WireError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BufferTooSmall => f.write_str("buffer too small"),
Self::PayloadTooLarge => f.write_str("payload too large"),
Self::InvalidOpcode(op) => write!(f, "invalid opcode 0x{op:02x}"),
Self::Truncated => f.write_str("truncated"),
Self::InvalidValue(v) => write!(f, "invalid value 0x{v:02x}"),
}
}
}

/// On-wire response code from the FD.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ResponseCode {
/// Operation completed successfully.
Success = 0,
/// Internal server error.
InternalError = 1,
/// Unrecognized opcode (InvalidOp on the wire).
InvalidOp = 2,
/// Operation not valid in the FD's current phase.
WrongPhase = 3,
}

impl ResponseCode {
pub const fn is_success(self) -> bool {
matches!(self, Self::Success)
}

pub const fn from_u8(val: u8) -> Option<Self> {
match val {
0 => Some(Self::Success),
1 => Some(Self::InternalError),
2 => Some(Self::InvalidOp),
3 => Some(Self::WrongPhase),
_ => None,
}
}
}

impl fmt::Display for ResponseCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Success => f.write_str("success"),
Self::InternalError => f.write_str("internal error"),
Self::InvalidOp => f.write_str("invalid op"),
Self::WrongPhase => f.write_str("wrong phase"),
}
}
}

/// Reason the orchestrator denied an operation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum DenyReason {
/// Component is isolated (compromise detected).
Isolated = 0,
/// Update policy violation.
PolicyViolation = 1,
/// Component identifier not recognized.
UnknownTarget = 2,
/// Another operation is in progress.
Busy = 3,
}

impl DenyReason {
pub const fn from_u8(val: u8) -> Option<Self> {
match val {
0 => Some(Self::Isolated),
1 => Some(Self::PolicyViolation),
2 => Some(Self::UnknownTarget),
3 => Some(Self::Busy),
_ => None,
}
}
}

impl fmt::Display for DenyReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Isolated => f.write_str("isolated"),
Self::PolicyViolation => f.write_str("policy violation"),
Self::UnknownTarget => f.write_str("unknown target"),
Self::Busy => f.write_str("busy"),
}
}
}

/// Error returned to the orchestrator's client layer.
///
/// Wraps the on-wire `ResponseCode`, the way `MctpError` wraps
/// `mctp_api::ResponseCode`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PldmIpcError {
pub code: ResponseCode,
}

impl PldmIpcError {
pub const fn from_code(code: ResponseCode) -> Self {
Self { code }
}
}

impl fmt::Display for PldmIpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "pldm ipc error: {}", self.code)
}
}

impl From<ResponseCode> for PldmIpcError {
fn from(code: ResponseCode) -> Self {
Self::from_code(code)
}
}
23 changes: 23 additions & 0 deletions services/pldm/ipc-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! PLDM IPC API: wire format and types.
//!
//! Defines the binary protocol the orchestrator uses to talk to the
//! PLDM Firmware Device over IPC. Host-buildable, no kernel
//! dependencies. The server and client crates depend on this for
//! shared types; neither re-invents the encoding. The transport that
//! carries these frames is `util_service`, shared with every other
//! IPC service.

#![no_std]

pub mod error;
pub mod status;
pub mod wire;

pub use error::{DenyReason, PldmIpcError, ResponseCode, WireError};
pub use status::{FdStatus, TransferMode};
pub use wire::{
PldmOp, RequestHeader, ResponseHeader, MAX_PAYLOAD_SIZE, MAX_REQUEST_SIZE, MAX_RESPONSE_SIZE,
};
Loading