1use std::fmt;
2
3use 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 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 }
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}