pldm_fw/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2/*
3 * PLDM firmware update utility: PLDM type 5 messaging
4 *
5 * Copyright (c) 2023 Code Construct
6 */
7#![cfg_attr(not(any(feature = "std", test)), no_std)]
8#![forbid(unsafe_code)]
9// #![warn(missing_docs)]
10
11use core::fmt;
12use log::debug;
13
14use chrono::Datelike;
15use enumset::{EnumSet, EnumSetType};
16use num_derive::FromPrimitive;
17
18use nom::{
19    branch::alt,
20    bytes::complete::{tag, take},
21    character::complete::u32 as c_u32,
22    combinator::{
23        all_consuming, flat_map, map, map_opt, map_parser, map_res, rest, value,
24    },
25    number::complete::{le_u16, le_u32, le_u8},
26    sequence::tuple,
27    IResult,
28};
29
30#[cfg(feature = "alloc")]
31use nom::multi::{count, length_count};
32
33extern crate pldm;
34
35/// Firmware Device specific
36pub mod fd;
37/// PLDM firmware packaging
38#[cfg(feature = "alloc")]
39pub mod pkg;
40/// Update Agent specific
41#[cfg(feature = "std")]
42pub mod ua;
43
44use pldm::util::*;
45
46// Firmware Update PLDM Type 5
47pub const PLDM_TYPE_FW: u8 = 5;
48
49// Baseline transfer size
50pub const PLDM_FW_BASELINE_TRANSFER: usize = 32;
51
52/// PLDM firmware specification requires 255 byte length.
53///
54/// Can be reduced when strings are a known length.
55#[cfg(not(feature = "alloc"))]
56const MAX_DESC_STRING: usize = 64;
57
58/// Maximum length allowed for vendor data in no-alloc
59///
60/// PLDM firmware specification has no length limit.
61/// Can be reduced if length is known.
62#[cfg(not(feature = "alloc"))]
63const MAX_VENDORDATA: usize = 64;
64
65/// Component Identifier
66#[derive(Debug, Eq, PartialEq, Hash)]
67pub struct ComponentId(pub u16);
68
69/// PLDM firmware device state definitions
70#[derive(Debug, PartialEq, Copy, Clone)]
71#[repr(u8)]
72pub enum PldmFDState {
73    Idle = 0,
74    LearnComponents = 1,
75    ReadyXfer = 2,
76    Download = 3,
77    Verify = 4,
78    Apply = 5,
79    Activate = 6,
80}
81
82impl TryFrom<u8> for PldmFDState {
83    type Error = &'static str;
84    fn try_from(value: u8) -> core::result::Result<Self, Self::Error> {
85        match value {
86            0 => Ok(Self::Idle),
87            1 => Ok(Self::LearnComponents),
88            2 => Ok(Self::ReadyXfer),
89            3 => Ok(Self::Download),
90            4 => Ok(Self::Verify),
91            5 => Ok(Self::Apply),
92            6 => Ok(Self::Activate),
93            _ => Err("unknown state!"),
94        }
95    }
96}
97
98impl PldmFDState {
99    /// Parse from a buffer
100    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
101        map_res(le_u8, TryInto::<PldmFDState>::try_into)(buf)
102    }
103}
104
105/// Idle Reason Codes for Get Status response
106#[allow(missing_docs)]
107#[derive(FromPrimitive, Debug, PartialEq, Copy, Clone)]
108#[repr(u8)]
109pub enum PldmIdleReason {
110    Init = 0,
111    Activate = 1,
112    Cancel = 2,
113    TimeoutLearn = 3,
114    TimeoutReadyXfer = 4,
115    TimeoutDownload = 5,
116    TimeoutVerify = 6,
117    TimeoutApply = 7,
118}
119
120/// PLDM Firmware Commands
121#[allow(missing_docs)]
122#[derive(FromPrimitive, Debug, PartialEq)]
123#[repr(u8)]
124pub enum Cmd {
125    QueryDeviceIdentifiers = 0x01,
126    GetFirmwareParameters = 0x02,
127    QueryDownstreamDevices = 0x03,
128    QueryDownstreamIdentifiers = 0x04,
129    GetDownstreamFirmwareParameters = 0x05,
130    RequestUpdate = 0x10,
131    GetPackageData = 0x11,
132    GetDeviceMetaData = 0x12,
133    PassComponentTable = 0x13,
134    UpdateComponent = 0x14,
135    RequestFirmwareData = 0x15,
136    TransferComplete = 0x16,
137    VerifyComplete = 0x17,
138    ApplyComplete = 0x18,
139    GetMetaData = 0x19,
140    ActivateFirmware = 0x1A,
141    GetStatus = 0x1B,
142    CancelUpdateComponent = 0x1C,
143    CancelUpdate = 0x1D,
144    ActivatePendingComponentImageSet = 0x1E,
145    ActivatePendingComponentImage = 0x1F,
146    RequestDownstreamDeviceUpdate = 0x20,
147}
148
149impl Cmd {
150    pub const fn is_ua(&self) -> bool {
151        !self.is_fd()
152    }
153
154    pub const fn is_fd(&self) -> bool {
155        match self {
156            Self::GetPackageData
157            | Self::RequestFirmwareData
158            | Self::TransferComplete
159            | Self::VerifyComplete
160            | Self::ApplyComplete
161            | Self::GetMetaData => true,
162            _ => false,
163        }
164    }
165}
166
167/// PLDM firmware response codes
168#[allow(missing_docs)]
169#[repr(u8)]
170#[allow(non_camel_case_types)]
171#[derive(Debug, PartialEq)]
172pub enum FwCode {
173    NOT_IN_UPDATE_MODE = 0x80,
174    ALREADY_IN_UPDATE_MODE = 0x81,
175    DATA_OUT_OF_RANGE = 0x82,
176    INVALID_TRANSFER_LENGTH = 0x83,
177    INVALID_STATE_FOR_COMMAND = 0x84,
178    INCOMPLETE_UPDATE = 0x85,
179    BUSY_IN_BACKGROUND = 0x86,
180    CANCEL_PENDING = 0x87,
181    COMMAND_NOT_EXPECTED = 0x88,
182    RETRY_REQUEST_FW_DATA = 0x89,
183    UNABLE_TO_INITIATE_UPDATE = 0x8A,
184    ACTIVATION_NOT_REQUIRED = 0x8B,
185    SELF_CONTAINED_ACTIVATION_NOT_PERMITTED = 0x8C,
186    NO_DEVICE_METADATA = 0x8D,
187    RETRY_REQUEST_UPDATE = 0x8E,
188    NO_PACKAGE_DATA = 0x8F,
189    INVALID_TRANSFER_HANDLE = 0x90,
190    INVALID_TRANSFER_OPERATION = 0x91,
191    ACTIVATE_PENDING_IMAGE_NOT_PERMITTED = 0x92,
192    PACKAGE_DATA_ERROR = 0x93,
193}
194
195/// Transfer Result codes for TransferComplete
196///
197/// Not all defined Transfer Result codes are defined in this enum,
198/// arbitrary `u8` values may be expected.
199#[repr(u8)]
200#[derive(Debug, Copy, Clone, PartialEq)]
201#[non_exhaustive]
202pub enum TransferResult {
203    Success,
204    Corrupt,
205    VersionMismatch,
206    Aborted,
207    Timeout,
208    GenericError,
209    Other(u8),
210}
211
212impl From<u8> for TransferResult {
213    fn from(v: u8) -> Self {
214        match v {
215            0x00 => Self::Success,
216            0x01 => Self::Corrupt,
217            0x02 => Self::VersionMismatch,
218            0x03 => Self::Aborted,
219            0x09 => Self::Timeout,
220            0x0a => Self::GenericError,
221            v => Self::Other(v),
222        }
223    }
224}
225
226impl From<TransferResult> for u8 {
227    fn from(v: TransferResult) -> u8 {
228        match v {
229            TransferResult::Success => 0x00,
230            TransferResult::Corrupt => 0x01,
231            TransferResult::VersionMismatch => 0x02,
232            TransferResult::Aborted => 0x03,
233            TransferResult::Timeout => 0x09,
234            TransferResult::GenericError => 0x0a,
235            TransferResult::Other(v) => v,
236        }
237    }
238}
239
240/// Verify Result codes for VerifyComplete
241///
242/// Not all defined Verify Result codes are defined in this enum,
243/// arbitrary `u8` values may be expected in `Other` variant.
244///
245/// Ref "VerifyComplete command format" Table 31 of DSP0267 1.1.0
246#[allow(missing_docs)]
247#[derive(Debug, Copy, Clone, PartialEq)]
248pub enum VerifyResult {
249    Success,
250    Failure,
251    VersionMismatch,
252    SecurityChecksFailed,
253    IncompleteImage,
254    // 0x5 - 0x8 reserved
255    Timeout,
256    GenericError,
257    Other(u8),
258}
259
260impl From<u8> for VerifyResult {
261    fn from(v: u8) -> Self {
262        match v {
263            0x00 => Self::Success,
264            0x01 => Self::Failure,
265            0x02 => Self::VersionMismatch,
266            0x03 => Self::SecurityChecksFailed,
267            0x04 => Self::IncompleteImage,
268            0x09 => Self::Timeout,
269            0x0a => Self::GenericError,
270            v => Self::Other(v),
271        }
272    }
273}
274
275impl From<VerifyResult> for u8 {
276    fn from(v: VerifyResult) -> u8 {
277        match v {
278            VerifyResult::Success => 0x00,
279            VerifyResult::Failure => 0x01,
280            VerifyResult::VersionMismatch => 0x02,
281            VerifyResult::SecurityChecksFailed => 0x03,
282            VerifyResult::IncompleteImage => 0x04,
283            VerifyResult::Timeout => 0x09,
284            VerifyResult::GenericError => 0x0a,
285            VerifyResult::Other(v) => v,
286        }
287    }
288}
289
290/// Apply Result codes for ApplyComplete
291///
292/// Not all defined Verify Result codes are defined in this enum,
293/// arbitrary `u8` values may be expected in `Other` variant.
294///
295/// Ref "ApplyComplete command format" Table 32 of DSP0267 1.1.0
296#[allow(missing_docs)]
297#[derive(Debug, Copy, Clone, PartialEq)]
298pub enum ApplyResult {
299    Success,
300    SuccessModActivation,
301    FailedMemoryWrite,
302    Timeout,
303    GenericError,
304    Other(u8),
305}
306
307impl From<u8> for ApplyResult {
308    fn from(v: u8) -> Self {
309        match v {
310            0x00 => Self::Success,
311            0x01 => Self::SuccessModActivation,
312            0x02 => Self::FailedMemoryWrite,
313            0x09 => Self::Timeout,
314            0x0a => Self::GenericError,
315            v => Self::Other(v),
316        }
317    }
318}
319
320impl From<ApplyResult> for u8 {
321    fn from(v: ApplyResult) -> u8 {
322        match v {
323            ApplyResult::Success => 0x00,
324            ApplyResult::SuccessModActivation => 0x01,
325            ApplyResult::FailedMemoryWrite => 0x02,
326            ApplyResult::Timeout => 0x09,
327            ApplyResult::GenericError => 0x0a,
328            ApplyResult::Other(v) => v,
329        }
330    }
331}
332
333//type VResult<I,O> = IResult<I, O, VerboseError<I>>;
334type VResult<I, O> = IResult<I, O>;
335
336#[derive(FromPrimitive, Debug, PartialEq)]
337#[repr(u8)]
338enum TransferFlag {
339    Start = 0x01,
340    Middle = 0x02,
341    End = 0x04,
342}
343
344#[cfg(feature = "alloc")]
345#[derive(Debug)]
346pub enum DescriptorString {
347    String(String),
348    Bytes(Vec<u8>),
349}
350
351#[cfg(not(feature = "alloc"))]
352#[derive(Debug)]
353pub enum DescriptorString {
354    String(heapless::String<MAX_DESC_STRING>),
355    Bytes(heapless::Vec<u8, MAX_DESC_STRING>),
356}
357
358impl fmt::Display for DescriptorString {
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        let trim_chars = ['\0', ' '];
361        match self {
362            Self::String(s) => {
363                write!(
364                    f,
365                    "{}",
366                    s.trim_end_matches(&trim_chars).escape_default()
367                )
368            }
369            Self::Bytes(bs) => {
370                for b in bs.iter() {
371                    write!(f, "{:02x}", b)?;
372                }
373                Ok(())
374            }
375        }
376    }
377}
378
379impl DescriptorString {
380    pub fn empty() -> Self {
381        Self::Bytes(Default::default())
382    }
383
384    pub fn string_type(&self) -> u8 {
385        match self {
386            Self::Bytes(_) => 0,
387            Self::String(_) => 1,
388        }
389    }
390
391    fn as_bytes(&self) -> &[u8] {
392        match self {
393            Self::Bytes(b) => b,
394            Self::String(b) => b.as_bytes(),
395        }
396    }
397
398    fn bytes_len(&self) -> u8 {
399        self.as_bytes().len() as u8
400    }
401}
402
403#[cfg(feature = "alloc")]
404impl DescriptorString {
405    pub fn write_utf8_bytes(&self, v: &mut Vec<u8>) {
406        match self {
407            Self::String(s) => {
408                v.push(0x01);
409                v.push(s.len() as u8);
410                v.extend_from_slice(s.as_bytes());
411            }
412            Self::Bytes(b) => {
413                v.push(0x00);
414                v.push(b.len() as u8);
415                v.extend_from_slice(b);
416            }
417        }
418    }
419
420    pub fn new_utf8(v: &[u8]) -> Option<Self> {
421        if v.len() > 0xff {
422            return None;
423        }
424        let s = core::str::from_utf8(v).ok()?;
425        Some(Self::String(s.to_string()))
426    }
427
428    pub fn new_str(s: &str) -> Option<Self> {
429        if s.as_bytes().len() > 0xff {
430            return None;
431        }
432        Some(Self::String(s.to_string()))
433    }
434
435    pub fn new_bytes(v: &[u8]) -> Option<Self> {
436        if v.len() > 0xff {
437            return None;
438        }
439        Some(Self::Bytes(v.to_vec()))
440    }
441
442    // // TODO: use encoding_rs to handle BOM, LE, BE.
443    // pub fn new_utf16(v: &[u8], _strtyp: u8) -> VResult<&[u8], Self> {
444    //     let b16 = v
445    //         .iter()
446    //         .tuples()
447    //         .map(|(a, b)| ((*a as u16) << 8 | (*b as u16)))
448    //         .collect::<Vec<u16>>();
449
450    //     let s = String::from_utf16(b16)
451    //         .map_err(|_| nom::Err::Failure((v, nom::ErrorKind::Fail)))?;
452    //     Ok(&[], Self::String(s))
453    // }
454}
455
456#[cfg(not(feature = "alloc"))]
457impl DescriptorString {
458    pub fn new_utf8(v: &[u8]) -> Option<Self> {
459        let s = core::str::from_utf8(v).ok()?;
460        Self::new_str(s)
461    }
462
463    pub fn new_str(s: &str) -> Option<Self> {
464        let s = heapless::String::try_from(s).ok()?;
465        Some(Self::String(s))
466    }
467
468    pub fn new_bytes(v: &[u8]) -> Option<Self> {
469        v.try_into().ok().map(|v| Self::Bytes(v))
470    }
471
472    // pub fn new_utf16(v: &[u8]) -> VResult<&[u8], Self> {
473    //     debug!("from_utf16 unimplemented")
474    //     let s = String::from_utf16(v)
475    //         .map_err(|_| nom::Err::Failure((v, nom::ErrorKind::Fail)))?;
476    //     Ok(&[], Self::String(s))
477    // }
478}
479
480/// A device descriptor
481#[derive(Debug)]
482pub enum Descriptor {
483    /// PCI Vendor ID
484    PciVid(u16),
485    /// IANA Enterprise ID
486    Iana(u32),
487    /// UUID
488    Uuid(uuid::Uuid),
489    /// PCI Device ID
490    PciDid(u16),
491    /// PCI Subsystem Vendor ID
492    PciSubVid(u16),
493    /// PCI Subsystem Device ID
494    PciSubDid(u16),
495    /// Vendor Defined
496    Vendor {
497        title: Option<DescriptorString>,
498        #[cfg(feature = "alloc")]
499        data: Vec<u8>,
500        #[cfg(not(feature = "alloc"))]
501        data: heapless::Vec<u8, MAX_VENDORDATA>,
502    },
503}
504
505/// Parse a string with type and length
506pub fn parse_string<'a>(
507    typ: u8,
508    len: u8,
509) -> impl FnMut(&'a [u8]) -> VResult<&'a [u8], DescriptorString> {
510    map_opt(take(len), move |d: &[u8]| match typ {
511        0 => DescriptorString::new_bytes(d),
512        // ascii or utf-8
513        1 | 2 => DescriptorString::new_utf8(d),
514        _ => {
515            debug!("unimplemented string type {typ}");
516            None
517        }
518    })
519}
520
521// Where we have type, length and data all adjacent (and in that order)
522pub fn parse_string_adjacent(buf: &[u8]) -> VResult<&[u8], DescriptorString> {
523    let (r, (typ, len)) = tuple((le_u8, le_u8))(buf)?;
524    parse_string(typ, len)(r)
525}
526
527impl Descriptor {
528    pub fn parse_pcivid(buf: &[u8]) -> VResult<&[u8], Self> {
529        map(le_u16, Self::PciVid)(buf)
530    }
531
532    pub fn parse_iana(buf: &[u8]) -> VResult<&[u8], Self> {
533        map(le_u32, Self::Iana)(buf)
534    }
535
536    pub fn parse_uuid(buf: &[u8]) -> VResult<&[u8], Self> {
537        map_res(take(16usize), |b| {
538            let u = uuid::Uuid::from_slice(b)?;
539            Ok::<Descriptor, uuid::Error>(Self::Uuid(u))
540        })(buf)
541    }
542
543    pub fn parse_pcidid(buf: &[u8]) -> VResult<&[u8], Self> {
544        map(le_u16, Self::PciDid)(buf)
545    }
546
547    pub fn parse_pcisubvid(buf: &[u8]) -> VResult<&[u8], Self> {
548        map(le_u16, Self::PciSubVid)(buf)
549    }
550
551    pub fn parse_pcisubdid(buf: &[u8]) -> VResult<&[u8], Self> {
552        map(le_u16, Self::PciSubDid)(buf)
553    }
554
555    #[cfg(feature = "alloc")]
556    fn new_vendor(t: Option<DescriptorString>, d: &[u8]) -> Option<Self> {
557        Some(Self::Vendor {
558            title: t,
559            data: d.to_vec(),
560        })
561    }
562
563    #[cfg(not(feature = "alloc"))]
564    fn new_vendor(t: Option<DescriptorString>, d: &[u8]) -> Option<Self> {
565        let data = d.try_into().ok()?;
566        Some(Self::Vendor { title: t, data })
567    }
568
569    pub fn parse_vendor(buf: &[u8]) -> VResult<&[u8], Self> {
570        // Attempt to parse with a proper title string; if not present just
571        // consume everything as byte data
572        let f1 = |(t, d): (_, &[u8])| Self::new_vendor(Some(t), d);
573        let f2 = |d: &[u8]| Self::new_vendor(None, d);
574        alt((
575            map_opt(tuple((parse_string_adjacent, rest)), f1),
576            map_opt(rest, f2),
577        ))(buf)
578    }
579
580    fn parse_fail(buf: &[u8]) -> VResult<&[u8], Self> {
581        nom::combinator::fail(buf)
582    }
583
584    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
585        let f = |(typ, len)| {
586            let g = match typ {
587                0x0000 => Self::parse_pcivid,
588                0x0001 => Self::parse_iana,
589                0x0002 => Self::parse_uuid,
590                0x0100 => Self::parse_pcidid,
591                0x0101 => Self::parse_pcisubvid,
592                0x0102 => Self::parse_pcisubdid,
593                0xffff => Self::parse_vendor,
594                _ => {
595                    debug!("Unknown descriptor type 0x{typ:04x}");
596                    Self::parse_fail
597                }
598            };
599            map_parser(take(len), all_consuming(g))
600        };
601        flat_map(tuple((le_u16, le_u16)), f)(buf)
602    }
603
604    pub fn desc_type(&self) -> u16 {
605        match self {
606            Self::PciVid(_) => 0x0000,
607            Self::Iana(_) => 0x0001,
608            Self::Uuid(_) => 0x0002,
609            Self::PciDid(_) => 0x0100,
610            Self::PciSubVid(_) => 0x0101,
611            Self::PciSubDid(_) => 0x0102,
612            Self::Vendor { .. } => 0xffff,
613        }
614    }
615
616    pub fn write_buf(&self, buf: &mut [u8]) -> Option<usize> {
617        let mut b = SliceWriter::new(buf);
618        match self {
619            Self::PciVid(v) => b.push_le16(*v),
620            Self::Iana(v) => b.push_le32(*v),
621            Self::Uuid(v) => b.push(v.as_bytes()),
622            Self::PciDid(v) => b.push_le16(*v),
623            Self::PciSubVid(v) => b.push_le16(*v),
624            Self::PciSubDid(v) => b.push_le16(*v),
625            Self::Vendor { .. } => {
626                // TODO encode Vendor
627                debug!("Vendor descriptor write not implemented");
628                None
629            }
630        }
631    }
632}
633
634impl fmt::Display for Descriptor {
635    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
636        match self {
637            Self::PciVid(id) => write!(f, "pci-vendor:{:04x}", id),
638            Self::Iana(id) => write!(f, "iana:{:08x}", id),
639            Self::Uuid(id) => write!(f, "uuid:{}", id),
640            Self::PciDid(id) => write!(f, "pci-device:{:04x}", id),
641            Self::PciSubVid(id) => write!(f, "pci-subsys-vendor:{:04x}", id),
642            Self::PciSubDid(id) => write!(f, "pci-subsys-device:{:04x}", id),
643            Self::Vendor { title, data } => {
644                match title {
645                    Some(t) => write!(f, "vendor:{}", t)?,
646                    None => write!(f, "vendor:")?,
647                }
648                write!(f, "[")?;
649                for b in data {
650                    write!(f, "{:02x}", b)?;
651                }
652                write!(f, "]")?;
653                Ok(())
654            }
655        }
656    }
657}
658
659impl PartialEq for Descriptor {
660    fn eq(&self, other: &Self) -> bool {
661        match (self, other) {
662            (Self::Vendor { data: s, .. }, Self::Vendor { data: o, .. }) => {
663                s == o
664            }
665            (Self::Iana(s), Self::Iana(o)) => s == o,
666            (Self::Uuid(s), Self::Uuid(o)) => s == o,
667            (Self::PciVid(s), Self::PciVid(o)) => s == o,
668            (Self::PciDid(s), Self::PciDid(o)) => s == o,
669            (Self::PciSubVid(s), Self::PciSubVid(o)) => s == o,
670            (Self::PciSubDid(s), Self::PciSubDid(o)) => s == o,
671            _ => false,
672        }
673    }
674}
675
676#[derive(Debug)]
677pub struct DeviceIdentifiers {
678    #[cfg(feature = "alloc")]
679    pub ids: Vec<Descriptor>,
680    #[cfg(not(feature = "alloc"))]
681    pub ids: &'static [Descriptor],
682}
683
684impl PartialEq for DeviceIdentifiers {
685    fn eq(&self, other: &Self) -> bool {
686        self.ids == other.ids
687    }
688}
689
690#[cfg(feature = "alloc")]
691impl DeviceIdentifiers {
692    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
693        length_count(le_u8, Descriptor::parse)(buf)
694            .map(|(rest, ids)| (rest, Self { ids }))
695    }
696}
697
698impl DeviceIdentifiers {
699    /// Returns a response for QueryDeviceIdentifiers
700    pub fn write_buf(&self, buf: &mut [u8]) -> Option<usize> {
701        let mut b = SliceWriter::new(buf);
702
703        if self.ids.is_empty() {
704            return None;
705        }
706
707        // To be filled after the length is known
708        b.push_le32(0)?;
709        b.push_le8(self.ids.len() as u8)?;
710
711        for v in self.ids.iter() {
712            b.push_le16(v.desc_type())?;
713            b.push_prefix_le::<u16, _>(|m| v.write_buf(m))?;
714        }
715
716        let written = b.written();
717
718        // Now fill out the DeviceIdentifiersLength the the start.
719        // Doesn't include ids len.
720        let mut b = SliceWriter::new(buf);
721        b.push_le32((written - 5) as u32)?;
722
723        Some(written)
724    }
725}
726
727impl fmt::Display for DeviceIdentifiers {
728    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
729        let mut first = true;
730        for id in self.ids.iter() {
731            write!(f, "{}{}", if first { "" } else { "," }, id)?;
732            first = false;
733        }
734        Ok(())
735    }
736}
737
738pub type PldmDate = chrono::naive::NaiveDate;
739
740#[derive(Debug)]
741#[allow(dead_code)]
742pub struct ComponentVersion {
743    pub stamp: u32,
744    pub version: DescriptorString,
745    pub date: Option<PldmDate>,
746}
747
748impl ComponentVersion {
749    /// Creates a new utf-8 string `ComponentVersion`
750    ///
751    /// May fail on non-`alloc` if the string is too long
752    pub fn new_str(s: &str) -> Option<Self> {
753        Some(Self {
754            stamp: 0,
755            version: DescriptorString::new_str(s)?,
756            date: None,
757        })
758    }
759
760    /// Writes stamp, type, length, date
761    ///
762    /// As used for the ComponentParameterTable
763    pub fn write_initial(&self, b: &mut [u8]) -> Option<usize> {
764        let mut b = SliceWriter::new(b);
765        b.push_le32(self.stamp)?;
766        b.push_le8(self.version.string_type())?;
767        b.push_le8(self.version.bytes_len())?;
768        b.push_with(|m| pldm_date_write_buf(&self.date, m))?;
769        Some(b.written())
770    }
771}
772
773impl fmt::Display for ComponentVersion {
774    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
775        write!(f, "{}", self.version)?;
776        if let Some(d) = self.date {
777            write!(f, " ({:?})", d)?;
778        }
779        if self.stamp != 0 {
780            write!(f, " [{:08x}]", self.stamp)?;
781        }
782        Ok(())
783    }
784}
785
786pub fn pldm_date_parse(buf: &[u8]) -> VResult<&[u8], Option<PldmDate>> {
787    /* YYYYMMDD */
788    let (r, o) = alt((
789        value(None, tag([0u8; 8])),
790        map(
791            tuple((
792                map_parser(take(4u8), c_u32),
793                map_parser(take(2u8), c_u32),
794                map_parser(take(2u8), c_u32),
795            )),
796            Some,
797        ),
798    ))(buf)?;
799
800    let d = o.and_then(|(y, m, d)| PldmDate::from_ymd_opt(y as i32, m, d));
801
802    Ok((r, d))
803}
804
805pub fn pldm_date_write_buf(
806    date: &Option<PldmDate>,
807    b: &mut [u8],
808) -> Option<usize> {
809    let mut b = SliceWriter::new(b);
810    if let Some(date) = date {
811        let mut y = date.year();
812        if y < 0 {
813            return None;
814        }
815        let m = date.month();
816        let d = date.day();
817
818        // hand written to avoid fmt code bloat
819        let mut w = [0u8; 8];
820        for i in 0..4 {
821            w[3 - i] = (y % 10) as u8;
822            y /= 10;
823        }
824        w[4] = (m / 10) as u8;
825        w[5] = (m % 10) as u8;
826        w[6] = (d / 10) as u8;
827        w[7] = (d % 10) as u8;
828        let w = w.map(|c| b'0' + c);
829        b.push(&w)?;
830
831        // This is 3kB of code size.
832        // write!(b, "{:04}{:02}{:02}", y, date.month(), date.day()).ok()?;
833    } else {
834        b.push(&[0u8; 8])?;
835    }
836
837    Some(b.written())
838}
839
840/// Component classification
841#[allow(missing_docs)]
842#[derive(Debug, Copy, Clone, PartialEq)]
843pub enum ComponentClassification {
844    Unknown,
845    Other,
846    Firmware,
847    /// Other values
848    Value(u16),
849}
850
851impl From<u16> for ComponentClassification {
852    fn from(x: u16) -> Self {
853        match x {
854            0x0000 => Self::Unknown,
855            0x0001 => Self::Other,
856            0x000a => Self::Firmware,
857            v => Self::Value(v),
858        }
859    }
860}
861
862impl From<&ComponentClassification> for u16 {
863    fn from(c: &ComponentClassification) -> u16 {
864        match c {
865            ComponentClassification::Unknown => 0x0000,
866            ComponentClassification::Other => 0x0001,
867            ComponentClassification::Firmware => 0x000a,
868            ComponentClassification::Value(v) => *v,
869        }
870    }
871}
872
873#[derive(EnumSetType, Debug)]
874pub enum ActivationMethod {
875    PendingComponentImageSet = 7,
876    PendingImage = 6,
877    ACPowerCycle = 5,
878    DCPowerCycle = 4,
879    SystemReboot = 3,
880    MediumSpecificReset = 2,
881    SelfContained = 1,
882    Automatic = 0,
883}
884
885pub type ActivationMethods = EnumSet<ActivationMethod>;
886
887#[derive(EnumSetType, Debug)]
888pub enum DeviceCapability {
889    ComponentUpdateFailureRecovery = 0,
890    ComponentUpdateFailureRetry = 1,
891    FDHostFunctionalityDuringUpdate = 2,
892    FDPartialUpdates = 3,
893    FDUpdateModeRestrictionOSActive = 4,
894    FDDowngradeRestrictions = 8,
895    SecurityRevisionUpdateRequest = 9,
896}
897
898impl DeviceCapability {
899    #[cfg(feature = "alloc")]
900    pub fn to_desc(&self, is_set: bool) -> String {
901        match self {
902            Self::ComponentUpdateFailureRecovery => format!(
903                "Device will{} revert to previous component on failure",
904                if is_set { " not" } else { "" }
905            ),
906            Self::ComponentUpdateFailureRetry => format!(
907                "{} restarting update on failure",
908                if is_set {
909                    "Requires"
910                } else {
911                    "Does not require"
912                }
913            ),
914            Self::FDHostFunctionalityDuringUpdate => format!(
915                "Host functionality is{} reduced during update",
916                if is_set { "" } else { " not" }
917            ),
918            Self::FDPartialUpdates => format!(
919                "Device can{} accept a partial update",
920                if is_set { "" } else { "not" }
921            ),
922            Self::FDUpdateModeRestrictionOSActive => String::from(if is_set {
923                "No host OS restrictions during update"
924            } else {
925                "Device unable to update while host OS active"
926            }),
927            Self::FDDowngradeRestrictions => String::from(if is_set {
928                "No downgrade restrictions"
929            } else {
930                "Downgrades may be restricted"
931            }),
932            Self::SecurityRevisionUpdateRequest => format!(
933                "Device components {} have security revision numbers",
934                if is_set { "may" } else { "do not" }
935            ),
936        }
937    }
938}
939
940#[derive(Debug, Default)]
941pub struct DeviceCapabilities(EnumSet<DeviceCapability>);
942
943impl DeviceCapabilities {
944    pub fn from_u32(x: u32) -> Self {
945        let x = x & EnumSet::<DeviceCapability>::all().as_u32();
946        Self(EnumSet::<DeviceCapability>::from_u32(x))
947    }
948
949    pub fn as_u32(&self) -> u32 {
950        self.0.as_u32()
951    }
952
953    pub fn is_empty(&self) -> bool {
954        self.0.is_empty()
955    }
956
957    #[cfg(feature = "alloc")]
958    pub fn values(&self) -> Vec<(DeviceCapability, bool)> {
959        EnumSet::<DeviceCapability>::all()
960            .iter()
961            .map(|cap| (cap, self.0.contains(cap)))
962            .collect()
963    }
964}
965
966#[derive(EnumSetType, Debug)]
967pub enum ComponentCapability {
968    FDApplyState = 0,
969    ComponentDowngrade = 2,
970    SecurityRevisionUpdateRequest = 3,
971    SecurityRevisionNotLatest = 4,
972}
973
974pub type ComponentCapabilities = EnumSet<ComponentCapability>;
975
976/// Specific to a ComponentParameterTable entry in Get Firmware Parameters
977#[derive(Debug)]
978#[allow(dead_code)]
979pub struct Component {
980    pub classification: ComponentClassification,
981    pub identifier: u16,
982    pub classificationindex: u8,
983    pub active: ComponentVersion,
984    pub pending: ComponentVersion,
985    pub activation_methods: ActivationMethods,
986    pub caps_during_update: ComponentCapabilities,
987}
988
989impl Component {
990    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
991        let (
992            r,
993            (
994                classification,
995                identifier,
996                classificationindex,
997                c1,
998                c2,
999                activation_methods,
1000                caps_during_update,
1001            ),
1002        ) = tuple((
1003            le_u16,
1004            le_u16,
1005            le_u8,
1006            tuple((le_u32, le_u8, le_u8, pldm_date_parse)),
1007            tuple((le_u32, le_u8, le_u8, pldm_date_parse)),
1008            le_u16,
1009            le_u32,
1010        ))(buf)?;
1011
1012        let (r, c1_str) = parse_string(c1.1, c1.2)(r)?;
1013        let (r, c2_str) = parse_string(c2.1, c2.2)(r)?;
1014
1015        let c = Component {
1016            classification: classification.into(),
1017            identifier,
1018            classificationindex,
1019            active: ComponentVersion {
1020                stamp: c1.0,
1021                version: c1_str,
1022                date: c1.3,
1023            },
1024            pending: ComponentVersion {
1025                stamp: c2.0,
1026                version: c2_str,
1027                date: c2.3,
1028            },
1029            activation_methods: ActivationMethods::from_u16(activation_methods),
1030            caps_during_update: ComponentCapabilities::from_u32(
1031                caps_during_update,
1032            ),
1033        };
1034
1035        Ok((r, c))
1036    }
1037
1038    pub fn write_buf(&self, b: &mut [u8]) -> Option<usize> {
1039        let mut b = SliceWriter::new(b);
1040        b.push_le16(u16::from(&self.classification))?;
1041        b.push_le16(self.identifier)?;
1042        b.push_le8(self.classificationindex)?;
1043        b.push_with(|m| self.active.write_initial(m))?;
1044        b.push_with(|m| self.pending.write_initial(m))?;
1045        b.push_le16(self.activation_methods.as_u16())?;
1046        b.push_le32(self.caps_during_update.as_u32())?;
1047        b.push(self.active.version.as_bytes())?;
1048        b.push(self.pending.version.as_bytes())?;
1049
1050        Some(b.written())
1051    }
1052}
1053
1054/// An entry for Pass Component Table or Update Component
1055///
1056/// The same structure is used for both, with `size` and `flags`
1057/// unpopulated for Pass Component
1058#[allow(missing_docs)]
1059pub struct UpdateComponent {
1060    pub classification: ComponentClassification,
1061    pub identifier: u16,
1062    pub classificationindex: u8,
1063    pub comparisonstamp: u32,
1064    pub version: DescriptorString,
1065    /// Size, not set for Pass Component
1066    pub size: Option<u32>,
1067    /// Flags, not set for Pass Component
1068    pub flags: Option<u32>,
1069}
1070
1071impl UpdateComponent {
1072    pub fn parse_pass_component(buf: &[u8]) -> VResult<&[u8], Self> {
1073        let (
1074            r,
1075            (
1076                classification,
1077                identifier,
1078                classificationindex,
1079                comparisonstamp,
1080                version,
1081            ),
1082        ) = tuple((le_u16, le_u16, le_u8, le_u32, parse_string_adjacent))(
1083            &buf,
1084        )?;
1085
1086        let s = Self {
1087            classification: classification.into(),
1088            identifier,
1089            classificationindex,
1090            comparisonstamp,
1091            version,
1092            size: None,
1093            flags: None,
1094        };
1095        Ok((r, s))
1096    }
1097    pub fn parse_update(buf: &[u8]) -> VResult<&[u8], Self> {
1098        let (
1099            r,
1100            (
1101                classification,
1102                identifier,
1103                classificationindex,
1104                comparisonstamp,
1105                size,
1106                flags,
1107                version,
1108            ),
1109        ) = tuple((
1110            le_u16,
1111            le_u16,
1112            le_u8,
1113            le_u32,
1114            le_u32,
1115            le_u32,
1116            parse_string_adjacent,
1117        ))(&buf)?;
1118
1119        let s = Self {
1120            classification: classification.into(),
1121            identifier,
1122            classificationindex,
1123            comparisonstamp,
1124            version,
1125            size: Some(size),
1126            flags: Some(flags),
1127        };
1128        Ok((r, s))
1129    }
1130}
1131
1132#[derive(Debug)]
1133pub struct UpdateComponentResponse {
1134    /// A ComponentResponseCode
1135    pub response_code: u8,
1136    pub update_flags: u32,
1137    pub estimate_time: u16,
1138}
1139
1140/// Response Codes for Update Component and Pass Component Table
1141///
1142/// This list is not complete, refer to the specification
1143#[allow(missing_docs)]
1144#[repr(u8)]
1145#[derive(Debug, PartialEq)]
1146#[non_exhaustive]
1147pub enum ComponentResponseCode {
1148    Success = 0x00,
1149    IdenticalVersion = 0x01,
1150    DowngradeVersion = 0x02,
1151    InvalidVersion = 0x03,
1152    Conflict = 0x04,
1153    MissingPrerequisite = 0x05,
1154    NotSupported = 0x06,
1155    SecurityPreventDowngrade = 0x07,
1156}
1157
1158impl UpdateComponentResponse {
1159    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
1160        let (r, (_response, response_code, update_flags, estimate_time)) =
1161            tuple((le_u8, le_u8, le_u32, le_u16))(buf)?;
1162
1163        let s = Self {
1164            response_code,
1165            update_flags,
1166            estimate_time,
1167        };
1168        Ok((r, s))
1169    }
1170}
1171
1172#[derive(Debug)]
1173#[allow(dead_code)]
1174pub struct FirmwareParameters<'a> {
1175    pub caps: DeviceCapabilities,
1176    pub components: VecOrSlice<'a, Component>,
1177    pub active: DescriptorString,
1178    pub pending: DescriptorString,
1179}
1180
1181#[cfg(feature = "alloc")]
1182impl FirmwareParameters<'_> {
1183    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
1184        let (r, p) = tuple((le_u32, le_u16, le_u8, le_u8, le_u8, le_u8))(buf)?;
1185
1186        let (
1187            caps,
1188            ccount,
1189            active_str_type,
1190            active_str_len,
1191            pending_str_type,
1192            pending_str_len,
1193        ) = p;
1194
1195        let (r, active) = parse_string(active_str_type, active_str_len)(r)?;
1196        let (r, pending) = parse_string(pending_str_type, pending_str_len)(r)?;
1197
1198        let (r, components) = count(Component::parse, ccount as usize)(r)?;
1199
1200        let fp = FirmwareParameters {
1201            caps: DeviceCapabilities::from_u32(caps),
1202            components: components.into(),
1203            active,
1204            pending,
1205        };
1206
1207        Ok((r, fp))
1208    }
1209}
1210
1211impl FirmwareParameters<'_> {
1212    pub fn write_buf(&self, buf: &mut [u8]) -> Option<usize> {
1213        let mut w = SliceWriter::new(buf);
1214
1215        w.push_le32(self.caps.as_u32())?;
1216        w.push_le16(self.components.len() as u16)?;
1217        w.push_le8(self.active.string_type())?;
1218        w.push_le8(self.active.bytes_len())?;
1219        w.push_le8(self.pending.string_type())?;
1220        w.push_le8(self.pending.bytes_len())?;
1221        w.push(self.active.as_bytes())?;
1222        w.push(self.pending.as_bytes())?;
1223
1224        for c in self.components.as_ref() {
1225            w.push_with(|b| c.write_buf(b))?;
1226        }
1227
1228        Some(w.written())
1229    }
1230}
1231
1232#[derive(Debug)]
1233pub struct RequestUpdateResponse {
1234    pub fd_metadata_len: u16,
1235    pub fd_will_sent_gpd: u8,
1236}
1237
1238impl RequestUpdateResponse {
1239    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
1240        let (r, t) = tuple((le_u16, le_u8))(buf)?;
1241        Ok((
1242            r,
1243            RequestUpdateResponse {
1244                fd_metadata_len: t.0,
1245                fd_will_sent_gpd: t.1,
1246            },
1247        ))
1248    }
1249
1250    pub fn write_buf(&self, b: &mut [u8]) -> Option<usize> {
1251        let mut b = SliceWriter::new(b);
1252        b.push_le16(self.fd_metadata_len)?;
1253        b.push_le8(self.fd_will_sent_gpd)?;
1254        Some(b.written())
1255    }
1256}
1257
1258#[derive(Debug)]
1259pub struct RequestUpdateRequest {
1260    pub max_transfer: u32,
1261    pub num_components: u16,
1262    pub max_outstanding: u8,
1263    pub package_data_length: u16,
1264    pub component_image_set_version: DescriptorString,
1265}
1266
1267impl RequestUpdateRequest {
1268    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
1269        let (r, t) =
1270            tuple((le_u32, le_u16, le_u8, le_u16, parse_string_adjacent))(buf)?;
1271        Ok((
1272            r,
1273            RequestUpdateRequest {
1274                max_transfer: t.0,
1275                num_components: t.1,
1276                max_outstanding: t.2,
1277                package_data_length: t.3,
1278                component_image_set_version: t.4,
1279            },
1280        ))
1281    }
1282}
1283
1284#[derive(Debug)]
1285pub struct GetStatusResponse {
1286    pub current_state: PldmFDState,
1287    pub previous_state: PldmFDState,
1288    pub aux_state: u8,
1289    pub aux_state_status: u8,
1290    pub progress_percent: u8,
1291    pub reason_code: u8,
1292    pub update_option_flags_enabled: u32,
1293}
1294
1295impl GetStatusResponse {
1296    pub fn parse(buf: &[u8]) -> VResult<&[u8], Self> {
1297        let (r, t) = tuple((
1298            PldmFDState::parse,
1299            PldmFDState::parse,
1300            le_u8,
1301            le_u8,
1302            le_u8,
1303            le_u8,
1304            le_u32,
1305        ))(buf)?;
1306        Ok((
1307            r,
1308            Self {
1309                current_state: t.0,
1310                previous_state: t.1,
1311                aux_state: t.2,
1312                aux_state_status: t.3,
1313                progress_percent: t.4,
1314                reason_code: t.5,
1315                update_option_flags_enabled: t.6,
1316            },
1317        ))
1318    }
1319
1320    pub fn write_buf(&self, buf: &mut [u8]) -> Option<usize> {
1321        let mut b = SliceWriter::new(buf);
1322        b.push_le8(self.current_state as u8)?;
1323        b.push_le8(self.previous_state as u8)?;
1324        b.push_le8(self.aux_state)?;
1325        b.push_le8(self.aux_state_status)?;
1326        b.push_le8(self.progress_percent)?;
1327        b.push_le8(self.reason_code)?;
1328        b.push_le32(self.update_option_flags_enabled)?;
1329        Some(b.written())
1330    }
1331}
1332
1333impl fmt::Display for GetStatusResponse {
1334    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1335        write!(formatter, "{:?}", self.current_state)
1336    }
1337}
1338
1339pub struct UpdateTransferProgress {
1340    pub cur_xfer: Option<(u32, u32)>,
1341    pub percent: u8,
1342    pub bps: f32,
1343    pub duration: chrono::Duration,
1344    pub remaining: chrono::Duration,
1345    pub complete: bool,
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350
1351    use crate::*;
1352
1353    #[test]
1354    fn date_parse() {
1355        let x = b"20240704x";
1356        let d = pldm_date_parse(x).unwrap();
1357        let expect = PldmDate::parse_from_str("20240704", "%Y%m%d").unwrap();
1358        assert_eq!(d, ("x".as_bytes(), Some(expect)));
1359
1360        // negative date rejected
1361        let x = b"-0240704x";
1362        pldm_date_parse(x).unwrap_err();
1363
1364        // short fails
1365        let x = b"2024070";
1366        pldm_date_parse(x).unwrap_err();
1367
1368        // space rejected
1369        let x = b" 0240704x";
1370        pldm_date_parse(x).unwrap_err();
1371
1372        // bad date returns None
1373        let x = b"20240732";
1374        let (_, d) = pldm_date_parse(x).unwrap();
1375        assert_eq!(d, None);
1376    }
1377
1378    #[test]
1379    fn date_write() {
1380        let d = PldmDate::parse_from_str("20240704", "%Y%m%d").unwrap();
1381
1382        let mut b = [99u8; 9];
1383        let l = pldm_date_write_buf(&Some(d), &mut b).unwrap();
1384        assert_eq!(b[8], 99);
1385        assert_eq!(l, 8);
1386        let b = &b[..l];
1387        assert_eq!(b"20240704", b);
1388
1389        // short fails
1390        let mut b = [99u8; 7];
1391        assert!(pldm_date_write_buf(&Some(d), &mut b).is_none());
1392
1393        // None date is all 0x00 bytes
1394        let mut b = [99u8; 8];
1395        let l = pldm_date_write_buf(&None, &mut b).unwrap();
1396        assert_eq!(l, 8);
1397        assert_eq!(b, [0u8; 8]);
1398    }
1399
1400    #[test]
1401    #[rustfmt::skip]
1402    fn write_device_identifier() {
1403        let ids = vec![Descriptor::PciVid(0xccde), Descriptor::Iana(1234)];
1404        let di = DeviceIdentifiers { ids };
1405
1406        let mut sendbuf = [0u8; 50];
1407        let l = di.write_buf(&mut sendbuf).unwrap();
1408        let sendbuf = &sendbuf[..l];
1409        let expect = [
1410            // length
1411            0x0e, 0x00, 0x00, 0x00,
1412            // count
1413            0x02,
1414            // desc 1 type
1415            0x00, 0x00,
1416            // data
1417            0x02, 0x00,
1418            // length
1419            0xde, 0xcc,
1420            // desc 2 type
1421            0x01, 0x00,
1422            // length
1423            0x04, 0x00,
1424            // data
1425            0xd2, 0x04, 0x00, 0x00,
1426        ];
1427        assert_eq!(sendbuf, expect);
1428    }
1429}