rust_ipmi/parser/ipmi/payload/
ipmi_payload_response.rs

1use std::fmt;
2
3// use bitvec::prelude::*;
4use bitvec::{field::BitField, order::Msb0, slice::BitSlice};
5
6use crate::{
7    commands::Command,
8    err::{IpmiPayloadError, IpmiPayloadRequestError},
9    helpers::utils::join_two_bits_to_byte,
10};
11
12use super::ipmi_payload::{AddrType, Lun, NetFn, SlaveAddress, SoftwareType};
13
14#[derive(Clone)]
15pub struct IpmiPayloadResponse {
16    pub rq_addr: Address,
17    pub net_fn: NetFn,
18    pub rq_lun: Lun,
19    // checksum 1
20    pub rs_addr: Address,
21    pub rq_sequence: u8,
22    pub rs_lun: Lun,
23    pub command: Command,
24    pub completion_code: CompletionCode,
25    pub data: Option<Vec<u8>>,
26    // checksum 2
27}
28
29impl fmt::Display for IpmiPayloadResponse {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        let data: String = match self.data.clone() {
32            Some(x) => format!("{:x?}", x),
33            None => "None".to_string(),
34        };
35        write!(
36            f,
37            "IPMI Response:\n\tRequester Address: {}\n\tNetFn: {}\n\tRequester LUN: {}\n\tResponder Address: {}\n\tRequester Sequence Number: {}\n\tResponder LUN: {}\n\tCommand: {}\n\tCompletion Code: {}\n\tDate: {}",
38            self.rq_addr,
39            self.net_fn,
40            self.rq_lun,
41            self.rs_addr,
42            self.rq_sequence,
43            self.rs_lun,
44            self.command,
45            self.completion_code,
46            data
47        )
48    }
49}
50
51impl TryFrom<&[u8]> for IpmiPayloadResponse {
52    type Error = IpmiPayloadError;
53
54    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
55        if value.len() < 8 {
56            Err(IpmiPayloadRequestError::WrongLength)?
57        }
58        let netfn_rqlun = BitSlice::<u8, Msb0>::from_element(&value[1]);
59        let rqseq_rslun = BitSlice::<u8, Msb0>::from_element(&value[4]);
60        let net_fn: NetFn = netfn_rqlun[0..6].load::<u8>().into();
61
62        Ok(IpmiPayloadResponse {
63            rq_addr: value[0].into(),
64            net_fn: net_fn.clone(),
65            rq_lun: netfn_rqlun[7..8].load::<u8>().try_into()?,
66            rs_addr: value[3].into(),
67            rq_sequence: rqseq_rslun[0..6].load::<u8>(),
68            rs_lun: rqseq_rslun[7..8].load::<u8>().try_into()?,
69            command: (value[5], net_fn.into()).try_into()?,
70            completion_code: value[6].into(),
71            data: {
72                let len = value.len() - 1;
73                if len == 7 {
74                    None
75                } else {
76                    Some(value[7..len].into())
77                }
78            },
79        })
80    }
81}
82
83impl IpmiPayloadResponse {
84    pub fn payload_length(&self) -> usize {
85        match &self.data {
86            Some(d) => d.len() + 8,
87            None => 8,
88        }
89    }
90}
91
92#[derive(Clone, Debug)]
93pub enum Address {
94    Slave(SlaveAddress),
95    Software(SoftwareType),
96}
97
98impl fmt::Display for Address {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        match self {
101            Address::Slave(x) => write!(f, "{}", x),
102            Address::Software(x) => write!(f, "{}", x),
103        }
104    }
105}
106
107impl From<u8> for Address {
108    fn from(value: u8) -> Self {
109        let bitslice = BitSlice::<u8, Msb0>::from_element(&value);
110        let rs_addr_type: AddrType = bitslice[0].into();
111        match rs_addr_type {
112            AddrType::SlaveAddress => Self::Slave(bitslice[1..].load::<u8>().into()),
113            AddrType::SoftwareId => Self::Software(bitslice[1..].load::<u8>().into()),
114        }
115    }
116}
117
118impl Into<u8> for Address {
119    fn into(self) -> u8 {
120        match self {
121            Self::Slave(s) => join_two_bits_to_byte(AddrType::SlaveAddress.into(), s.into(), 1),
122            Self::Software(s) => join_two_bits_to_byte(AddrType::SoftwareId.into(), s.into(), 1),
123        }
124    }
125}
126
127#[derive(Clone, Debug, PartialEq, Eq)]
128pub enum CompletionCode {
129    CompletedNormally,
130    NodeBusy,
131    InvalidCommand,
132    InvalidCommandForLun,
133    Timeout,
134    OutOfSpace,
135    ReservationCancelled,
136    RequestDataTruncated,
137    RequestDataLengthInvalid,
138    RequestDataFieldLengthLimitExceeded,
139    ParameterOutOfRange,
140    CannotReturnNumberOfRqDataBytes,
141    RqSensorDataRecordNotPresent,
142    InvalidDataFieldInRequest,
143    CommandIllegalForSensor,
144    CommandResponseNotProvided,
145    CantExecuteDuplicateRq,
146    FailedSDRUpdateMode,
147    FailedDevFirmwareMode,
148    FailedInitInProgress,
149    DestinationUnavailable,
150    CannotExecuteCommandInsuffientPrivileges,
151    CommandSubFunctionUnavailable,
152    CannotExecuteCommandIllegalParam,
153    UnspecifiedError,
154    OEM(u8),
155    CommandCode(u8),
156    Reserved(u8),
157}
158
159impl fmt::Display for CompletionCode {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        match self {
162            CompletionCode::CompletedNormally => write!(f, "Command Completed Normally"),
163            CompletionCode::NodeBusy => write!(f, "Node Busy. Command could not be processed because command processing resources are temporarily unavailable"),
164            CompletionCode::InvalidCommand => write!(f, "Invalid Command. Used to indicate an unrecognized or unsupported command"),
165            CompletionCode::InvalidCommandForLun => write!(f, "Command invalid for given LUN"),
166            CompletionCode::Timeout => write!(f, "Timeout while processing command. Response unavailable"),
167            CompletionCode::OutOfSpace => write!(f, "Out of space. Command could not be completed because of a lack of storage space required to execute the given command operation"),
168            CompletionCode::ReservationCancelled => write!(f, "Reservation Canceled or Invalid Reservation ID"),
169            CompletionCode::RequestDataTruncated => write!(f, "Request data truncated"),
170            CompletionCode::RequestDataLengthInvalid => write!(f, "Request data length invalid"),
171            CompletionCode::RequestDataFieldLengthLimitExceeded => write!(f, "Request data field length limit exceeded"),
172            CompletionCode::ParameterOutOfRange => write!(f, "Parameter out of range. One or more parameters in the data field of the Request are out of range. This is different from ‘Invalid data field’ (CCh) code in that it indicates that the erroneous field(s) has a contiguous range of possible values"),
173            CompletionCode::CannotReturnNumberOfRqDataBytes => write!(f, "Cannot return number of requested data bytes"),
174            CompletionCode::RqSensorDataRecordNotPresent => write!(f, "Requested Sensor, data, or record not present"),
175            CompletionCode::InvalidDataFieldInRequest => write!(f, "Invalid data field in Request"),
176            CompletionCode::CommandIllegalForSensor => write!(f, "Command illegal for specified sensor or record type"),
177            CompletionCode::CommandResponseNotProvided => write!(f, "Command response could not be provided"),
178            CompletionCode::CantExecuteDuplicateRq => write!(f, "Cannot execute duplicated request"),
179            CompletionCode::FailedSDRUpdateMode => write!(f, "Command response could not be provided. SDR Repository in update mode"),
180            CompletionCode::FailedDevFirmwareMode => write!(f, "Command response could not be provided. Device in firmware update mode"),
181            CompletionCode::FailedInitInProgress => write!(f, "Command response could not be provided. BMC initialization or initialization agent in progress"),
182            CompletionCode::DestinationUnavailable => write!(f, "Destination unavailable"),
183            CompletionCode::CannotExecuteCommandInsuffientPrivileges => write!(f, "Cannot execute command due to insufficient privilege level or other securitybased restriction (e.g. disabled for ‘firmware firewall’)."),
184            CompletionCode::CommandSubFunctionUnavailable => write!(f, "Cannot execute command. Command, or request parameter(s), not supported in present state"),
185            CompletionCode::CannotExecuteCommandIllegalParam => write!(f, "Cannot execute command. Parameter is illegal because command sub-function has been disabled or is unavailable (e.g. disabled for ‘firmware firewall’)"),
186            CompletionCode::UnspecifiedError => write!(f, "Unspecified error"),
187            CompletionCode::OEM(x) => write!(f, "Device specific (OEM) completion code: {}", x),
188            CompletionCode::CommandCode(x) => write!(f, "Command specific code: {}", x),
189            CompletionCode::Reserved(x) => write!(f, "Reserved code: {}", x),
190        }
191    }
192}
193
194impl From<u8> for CompletionCode {
195    fn from(value: u8) -> Self {
196        match value {
197            0x0 => CompletionCode::CompletedNormally,
198            0xc0 => CompletionCode::NodeBusy,
199            0xc1 => CompletionCode::InvalidCommand,
200            0xc2 => CompletionCode::InvalidCommandForLun,
201            0xc3 => CompletionCode::Timeout,
202            0xc4 => CompletionCode::OutOfSpace,
203            0xc5 => CompletionCode::ReservationCancelled,
204            0xc6 => CompletionCode::RequestDataTruncated,
205            0xc7 => CompletionCode::RequestDataLengthInvalid,
206            0xc8 => CompletionCode::RequestDataFieldLengthLimitExceeded,
207            0xc9 => CompletionCode::ParameterOutOfRange,
208            0xca => CompletionCode::CannotReturnNumberOfRqDataBytes,
209            0xcb => CompletionCode::RqSensorDataRecordNotPresent,
210            0xcc => CompletionCode::InvalidDataFieldInRequest,
211            0xcd => CompletionCode::CommandIllegalForSensor,
212            0xce => CompletionCode::CommandResponseNotProvided,
213            0xcf => CompletionCode::CantExecuteDuplicateRq,
214            0xd0 => CompletionCode::FailedSDRUpdateMode,
215            0xd1 => CompletionCode::FailedDevFirmwareMode,
216            0xd2 => CompletionCode::FailedInitInProgress,
217            0xd3 => CompletionCode::DestinationUnavailable,
218            0xd4 => CompletionCode::CannotExecuteCommandInsuffientPrivileges,
219            0xd5 => CompletionCode::CommandSubFunctionUnavailable,
220            0xd6 => CompletionCode::CannotExecuteCommandIllegalParam,
221            0xff => CompletionCode::UnspecifiedError,
222            0x01..=0x7e => CompletionCode::OEM(value),
223            0x80..=0xbe => CompletionCode::CommandCode(value),
224            _ => CompletionCode::Reserved(value),
225        }
226    }
227}
228
229impl CompletionCode {
230    pub fn from_u8(code: u8) -> CompletionCode {
231        match code {
232            0x0 => CompletionCode::CompletedNormally,
233            0xc0 => CompletionCode::NodeBusy,
234            0xc1 => CompletionCode::InvalidCommand,
235            0xc2 => CompletionCode::InvalidCommandForLun,
236            0xc3 => CompletionCode::Timeout,
237            0xc4 => CompletionCode::OutOfSpace,
238            0xc5 => CompletionCode::ReservationCancelled,
239            0xc6 => CompletionCode::RequestDataTruncated,
240            0xc7 => CompletionCode::RequestDataLengthInvalid,
241            0xc8 => CompletionCode::RequestDataFieldLengthLimitExceeded,
242            0xc9 => CompletionCode::ParameterOutOfRange,
243            0xca => CompletionCode::CannotReturnNumberOfRqDataBytes,
244            0xcb => CompletionCode::RqSensorDataRecordNotPresent,
245            0xcc => CompletionCode::InvalidDataFieldInRequest,
246            0xcd => CompletionCode::CommandIllegalForSensor,
247            0xce => CompletionCode::CommandResponseNotProvided,
248            0xcf => CompletionCode::CantExecuteDuplicateRq,
249            0xd0 => CompletionCode::FailedSDRUpdateMode,
250            0xd1 => CompletionCode::FailedDevFirmwareMode,
251            0xd2 => CompletionCode::FailedInitInProgress,
252            0xd3 => CompletionCode::DestinationUnavailable,
253            0xd4 => CompletionCode::CannotExecuteCommandInsuffientPrivileges,
254            0xd5 => CompletionCode::CommandSubFunctionUnavailable,
255            0xd6 => CompletionCode::CannotExecuteCommandIllegalParam,
256            0xff => CompletionCode::UnspecifiedError,
257            0x01..=0x7e => CompletionCode::OEM(code),
258            0x80..=0xbe => CompletionCode::CommandCode(code),
259            _ => CompletionCode::Reserved(code),
260        }
261    }
262
263    pub fn to_u8(&self) -> u8 {
264        match self {
265            CompletionCode::CompletedNormally => 0x00,
266            CompletionCode::NodeBusy => 0xc0,
267            CompletionCode::InvalidCommand => 0xc1,
268            CompletionCode::InvalidCommandForLun => 0xc2,
269            CompletionCode::Timeout => 0xc3,
270            CompletionCode::OutOfSpace => 0xc4,
271            CompletionCode::ReservationCancelled => 0xc5,
272            CompletionCode::RequestDataTruncated => 0xc6,
273            CompletionCode::RequestDataLengthInvalid => 0xc7,
274            CompletionCode::RequestDataFieldLengthLimitExceeded => 0xc8,
275            CompletionCode::ParameterOutOfRange => 0xc9,
276            CompletionCode::CannotReturnNumberOfRqDataBytes => 0xca,
277            CompletionCode::RqSensorDataRecordNotPresent => 0xcb,
278            CompletionCode::InvalidDataFieldInRequest => 0xcc,
279            CompletionCode::CommandIllegalForSensor => 0xcd,
280            CompletionCode::CommandResponseNotProvided => 0xce,
281            CompletionCode::CantExecuteDuplicateRq => 0xcf,
282            CompletionCode::FailedSDRUpdateMode => 0xd0,
283            CompletionCode::FailedDevFirmwareMode => 0xd1,
284            CompletionCode::FailedInitInProgress => 0xd2,
285            CompletionCode::DestinationUnavailable => 0xd3,
286            CompletionCode::CannotExecuteCommandInsuffientPrivileges => 0xd4,
287            CompletionCode::CommandSubFunctionUnavailable => 0xd5,
288            CompletionCode::CannotExecuteCommandIllegalParam => 0xd6,
289            CompletionCode::UnspecifiedError => 0xff,
290            CompletionCode::OEM(code) => *code,
291            CompletionCode::CommandCode(code) => *code,
292            CompletionCode::Reserved(code) => *code,
293        }
294    }
295}