1#![cfg_attr(not(any(feature = "std", test)), no_std)]
8#![forbid(unsafe_code)]
9#![warn(missing_docs)]
10
11use core::fmt::{self, Debug};
17
18use mctp::MsgIC;
19
20pub mod util;
21use util::*;
22
23pub const PLDM_MAX_MSGSIZE: usize = 1024;
27
28#[derive(Debug)]
30pub enum PldmError {
31 Protocol(ErrStr),
33 Mctp(mctp::Error),
35 InvalidArgument,
37 NoSpace,
39}
40
41impl core::fmt::Display for PldmError {
42 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43 match self {
44 Self::Protocol(s) => write!(f, "PLDM protocol error: {s}"),
45 Self::Mctp(s) => write!(f, "MCTP error: {s}"),
46 Self::InvalidArgument => write!(f, "Invalid Argument"),
47 Self::NoSpace => write!(f, "Insufficient buffer space available"),
48 }
49 }
50}
51
52#[cfg(feature = "std")]
53impl std::error::Error for PldmError {
54 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
55 match self {
56 Self::Mctp(s) => Some(s),
57 _ => None,
58 }
59 }
60}
61
62impl From<mctp::Error> for PldmError {
63 fn from(e: mctp::Error) -> PldmError {
64 PldmError::Mctp(e)
65 }
66}
67
68#[cfg(feature = "alloc")]
69type ErrStr = String;
70#[cfg(not(feature = "alloc"))]
71type ErrStr = &'static str;
72
73#[macro_export]
87#[cfg(feature = "alloc")]
88macro_rules! proto_error {
89 ($msg: expr, $desc_str: expr) => {
90 $crate::PldmError::Protocol(format!("{}. {}", $msg, $desc_str))
91 };
92 ($msg: expr) => {
93 $crate::PldmError::Protocol(format!("{}.", $msg))
94 };
95}
96
97#[macro_export]
111#[cfg(not(feature = "alloc"))]
112macro_rules! proto_error {
113 ($msg: expr, $desc_str: expr) => {
114 $crate::PldmError::Protocol($msg)
115 };
116 ($msg: expr) => {
117 $crate::PldmError::Protocol($msg)
118 };
119}
120
121pub type Result<T> = core::result::Result<T, PldmError>;
123
124#[allow(missing_docs)]
125#[repr(u8)]
126#[allow(non_camel_case_types)]
127#[derive(Debug, PartialEq)]
128pub enum CCode {
129 SUCCESS = 0,
130 ERROR = 1,
131 ERROR_INVALID_DATA = 2,
132 ERROR_INVALID_LENGTH = 3,
133 ERROR_NOT_READY = 4,
134 ERROR_UNSUPPORTED_PLDM_CMD = 5,
135 ERROR_INVALID_PLDM_TYPE = 32,
136}
137
138pub struct PldmRequest<'a> {
140 pub iid: u8,
142 pub typ: u8,
144 pub cmd: u8,
146 pub data: VecOrSlice<'a, u8>,
148}
149
150impl<'a> Debug for PldmRequest<'a> {
151 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152 let vs = match self.data {
153 #[cfg(feature = "alloc")]
154 VecOrSlice::Owned(_) => "Owned",
155 VecOrSlice::Borrowed(_) => "Borrowed",
156 };
157 f.debug_struct("PldmRequest")
158 .field("iid", &self.iid)
159 .field("typ", &self.typ)
160 .field("cmd", &self.cmd)
161 .field("data.len()", &self.data.len())
162 .field("data..10", &&self.data[..self.data.len().min(10)])
163 .field("storage", &vs)
164 .finish()
165 }
166}
167
168#[cfg(feature = "alloc")]
169impl<'a> PldmRequest<'a> {
170 pub fn make_owned(self) -> PldmRequest<'static> {
172 let d = match self.data {
173 VecOrSlice::Borrowed(b) => b.to_vec().into(),
174 VecOrSlice::Owned(b) => VecOrSlice::Owned(b),
175 };
176 PldmRequest { data: d, ..self }
177 }
178
179 pub fn set_data(&mut self, data: Vec<u8>) {
181 self.data = data.into()
182 }
183
184 pub fn response(&self) -> PldmResponse<'_> {
189 PldmResponse {
190 iid: self.iid,
191 typ: self.typ,
192 cmd: self.cmd,
193 cc: 0,
194 data: Vec::new().into(),
195 }
196 }
197}
198
199#[cfg(feature = "alloc")]
201impl PldmRequest<'static> {
202 pub fn new(typ: u8, cmd: u8) -> Self {
205 Self::new_data(typ, cmd, Vec::new())
206 }
207
208 pub fn new_data(typ: u8, cmd: u8, data: Vec<u8>) -> Self {
210 Self {
211 iid: 0,
212 typ,
213 cmd,
214 data: data.into(),
215 }
216 }
217
218 pub fn from_buf<'f>(data: &'f [u8]) -> Result<Self> {
222 PldmRequest::from_buf_borrowed(data).map(|p| p.make_owned())
223 }
224}
225
226impl<'a> PldmRequest<'a> {
227 pub fn new_borrowed(typ: u8, cmd: u8, data: &'a [u8]) -> Self {
229 Self {
230 iid: 0,
231 typ,
232 cmd,
233 data: data.into(),
234 }
235 }
236
237 pub fn from_buf_borrowed(data: &'a [u8]) -> Result<PldmRequest<'a>> {
242 if data.len() < 3 {
243 return Err(proto_error!(
244 "Short request",
245 format!("{} bytes", data.len())
246 ));
247 }
248
249 let rq = (data[0] & 0x80) != 0;
250 let iid = data[0] & 0x1f;
251 let typ = data[1] & 0x3f;
252 let cmd = data[2];
253
254 if !rq {
255 return Err(proto_error!("PLDM response, expected request"));
256 }
257
258 Ok(PldmRequest {
259 iid,
260 typ,
261 cmd,
262 data: (&data[3..]).into(),
263 })
264 }
265
266 pub fn response_borrowed<'f>(&self, data: &'f [u8]) -> PldmResponse<'f> {
273 PldmResponse {
274 iid: self.iid,
275 typ: self.typ,
276 cmd: self.cmd,
277 cc: 0,
278 data: data.into(),
279 }
280 }
281}
282
283pub struct PldmResponse<'a> {
285 pub iid: u8,
287 pub typ: u8,
289 pub cmd: u8,
291 pub cc: u8,
293 pub data: VecOrSlice<'a, u8>,
295}
296
297impl<'a> Debug for PldmResponse<'a> {
298 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
299 let vs = match self.data {
300 #[cfg(feature = "alloc")]
301 VecOrSlice::Owned(_) => "Owned",
302 VecOrSlice::Borrowed(_) => "Borrowed",
303 };
304 f.debug_struct("PldmResponse")
305 .field("iid", &self.iid)
306 .field("typ", &self.typ)
307 .field("cmd", &self.cmd)
308 .field("cc", &self.cc)
309 .field("data.len()", &self.data.len())
310 .field("data..10", &&self.data[..self.data.len().min(10)])
311 .field("storage", &vs)
312 .finish()
313 }
314}
315
316#[cfg(feature = "alloc")]
317impl<'a> PldmResponse<'a> {
318 pub fn set_data(&mut self, data: Vec<u8>) {
320 self.data = data.into()
321 }
322
323 pub fn make_owned(self) -> PldmResponse<'static> {
325 let d = match self.data {
326 VecOrSlice::Borrowed(b) => b.to_vec().into(),
327 VecOrSlice::Owned(b) => VecOrSlice::Owned(b),
328 };
329 PldmResponse { data: d, ..self }
330 }
331}
332
333impl<'a> PldmResponse<'a> {
334 pub fn from_buf_borrowed(rx_buf: &'a [u8]) -> Result<Self> {
336 if rx_buf.len() < 4 {
337 return Err(proto_error!(
338 "Short response",
339 format!("{} bytes", rx_buf.len())
340 ));
341 }
342
343 let rq = (rx_buf[0] & 0x80) != 0;
344 let iid = rx_buf[0] & 0x1f;
345 let typ = rx_buf[1] & 0x3f;
346 let cmd = rx_buf[2];
347 let cc = rx_buf[3];
348
349 if rq {
350 return Err(proto_error!("PLDM request, expected response"));
351 }
352
353 let rsp = PldmResponse {
354 iid,
355 typ,
356 cmd,
357 cc,
358 data: (&rx_buf[4..]).into(),
359 };
360
361 Ok(rsp)
362 }
363}
364
365#[derive(Debug)]
367pub enum PldmMessage<'a> {
368 Request(PldmRequest<'a>),
370 Response(PldmResponse<'a>),
372}
373
374#[cfg(feature = "alloc")]
379pub fn pldm_xfer(
380 ep: &mut impl mctp::ReqChannel,
381 req: PldmRequest,
382) -> Result<PldmResponse<'static>> {
383 let mut rx_buf = [0u8; PLDM_MAX_MSGSIZE]; pldm_xfer_buf(ep, req, &mut rx_buf).map(|r| r.make_owned())
385}
386
387pub fn pldm_xfer_buf<'buf>(
394 ep: &mut impl mctp::ReqChannel,
395 mut req: PldmRequest,
396 rx_buf: &'buf mut [u8],
397) -> Result<PldmResponse<'buf>> {
398 pldm_tx_req(ep, &mut req)?;
399
400 let rsp = pldm_rx_resp_borrowed(ep, rx_buf)?;
401
402 if rsp.iid != req.iid {
403 return Err(proto_error!(
404 "Incorrect instance ID in reply",
405 format!("Expected 0x{:02x} got 0x{:02x}", req.iid, rsp.iid)
406 ));
407 }
408
409 if rsp.typ != req.typ {
410 return Err(proto_error!(
411 "Incorrect PLDM type in reply",
412 format!("Expected 0x{:02x} got 0x{:02x}", req.typ, rsp.typ)
413 ));
414 }
415
416 if rsp.cmd != req.cmd {
417 return Err(proto_error!(
418 "Incorrect PLDM command in reply",
419 format!("Expected 0x{:02x} got 0x{:02x}", req.cmd, rsp.cmd)
420 ));
421 }
422
423 Ok(rsp)
424}
425
426#[cfg(feature = "alloc")]
435pub fn pldm_rx_req<'lis, L>(
436 listener: &'lis mut L,
437) -> Result<(PldmRequest<'static>, L::RespChannel<'lis>)>
438where
439 L: mctp::Listener,
440{
441 let mut rx_buf = [0u8; PLDM_MAX_MSGSIZE]; let (req, ep) = pldm_rx_req_borrowed(listener, &mut rx_buf)?;
443 Ok((req.make_owned(), ep))
444}
445
446pub fn pldm_rx_req_borrowed<'lis, 'buf, L>(
455 listener: &'lis mut L,
456 rx_buf: &'buf mut [u8],
457) -> Result<(PldmRequest<'buf>, L::RespChannel<'lis>)>
458where
459 L: mctp::Listener,
460{
461 let (typ, ic, rx_buf, ep) = listener.recv(rx_buf)?;
462 if ic.0 {
463 return Err(proto_error!("IC bit set"));
464 }
465 if typ != mctp::MCTP_TYPE_PLDM {
466 return Err(PldmError::InvalidArgument);
468 }
469 let req = PldmRequest::from_buf_borrowed(rx_buf)?;
470
471 Ok((req, ep))
472}
473
474pub fn pldm_rx_resp_borrowed<'buf>(
476 ep: &mut impl mctp::ReqChannel,
477 rx_buf: &'buf mut [u8],
478) -> Result<PldmResponse<'buf>> {
479 let (_typ, ic, rx_buf) = ep.recv(rx_buf)?;
480 if ic.0 {
481 return Err(proto_error!("IC bit set"));
482 }
483 PldmResponse::from_buf_borrowed(rx_buf)
484}
485
486pub fn pldm_tx_resp(
490 ep: &mut impl mctp::RespChannel,
491 resp: &PldmResponse,
492) -> Result<()> {
493 let tx_buf = [resp.iid, resp.typ, resp.cmd, resp.cc];
494 let txs = &[&tx_buf, resp.data.as_ref()];
495 ep.send_vectored(MsgIC(false), txs)?;
496 Ok(())
497}
498
499pub fn pldm_tx_req(
504 ep: &mut impl mctp::ReqChannel,
505 req: &mut PldmRequest,
506) -> Result<()> {
507 const REQ_IID: u8 = 0;
509 req.iid = REQ_IID;
510
511 let tx_buf = [1 << 7 | req.iid, req.typ & 0x3f, req.cmd];
512
513 let txs = &[&tx_buf, req.data.as_ref()];
514 ep.send_vectored(mctp::MCTP_TYPE_PLDM, MsgIC(false), txs)?;
515 Ok(())
516}