Skip to main content

warp_devices/
cms.rs

1use crate::xdma::{Error as XdmaError, XdmaOps};
2use enum_iterator::Sequence;
3use log::debug;
4use num_enum::TryFromPrimitive;
5use num_enum::TryFromPrimitiveError;
6use std::collections::BTreeSet;
7use std::convert::TryFrom;
8use std::iter;
9use std::thread;
10use std::time::Duration;
11
12pub type Result<T> = std::result::Result<T, Error>;
13
14#[derive(Debug)]
15pub enum Error {
16    XdmaFailed(XdmaError),
17    HostStatusNotReady,
18    MailboxNotAvailable,
19    CmsRegMaskNotAsExpected,
20    HostMsgError(u32),
21    CardInfoParseError(CardInfoParseError),
22}
23
24impl From<CardInfoParseError> for Error {
25    fn from(e: CardInfoParseError) -> Self {
26        Self::CardInfoParseError(e)
27    }
28}
29
30/// CMS register offsets
31#[derive(Copy, Clone, Debug, Sequence, PartialEq)]
32#[repr(u64)]
33pub enum CmsReg {
34    /// Microblaze reset register. Active-Low. Default 0, reset active.
35    MicroblazeResetN = 0x2_0000,
36    FwVersion = 0x2_8004,
37    CmsStatus = 0x2_8008,
38    Error = 0x2_800c,
39    ProfileName = 0x2_8014,
40    Control = 0x2_8018,
41    /// Voltage in mV
42    Pex12VMax = 0x2_8020,
43    Pex12VAvg = 0x2_8024,
44    Pex12VInst = 0x2_8028,
45    Pex3V3Max = 0x2_802c,
46    Pex3V3Avg = 0x2_8030,
47    Pex3V3Inst = 0x2_8034,
48    // Aux3V3Max = 0x2_8038,
49    // Aux3V3Avg = 0x2_803c,
50    // Aux3V3Inst = 0x2_8040,
51    Aux12VMax = 0x2_8044,
52    Aux12VAvg = 0x2_8048,
53    Aux12VInst = 0x2_804c,
54    Sys5V5Max = 0x2_805c,
55    Sys5V5Avg = 0x2_8060,
56    Sys5V5Inst = 0x2_8064,
57    // Vcc1V2TopMax = 0x2_8068,
58    // Vcc1V2TopAvg = 0x2_806c,
59    // Vcc1V2TopInst = 0x2_8070,
60    Vcc1V8Max = 0x2_8074,
61    Vcc1V8Avg = 0x2_8078,
62    Vcc1V8Inst = 0x2_807c,
63    // Vcc0V85Max = 0x2_8080,
64    // Vcc0V85Avg = 0x2_8084,
65    // Vcc0V85Inst = 0x2_8088,
66    Mgt0V9AVccMax = 0x2_8098,
67    Mgt0V9AVccAvg = 0x2_809c,
68    Mgt0V9AVccInst = 0x2_80A0,
69    // Sw12VMax = 0x2_80a4,
70    // Sw12VAvg = 0x2_80a8,
71    // Sw12VInst = 0x2_80ac,
72    MgtAVttMax = 0x2_80b0,
73    MgtAVttAvg = 0x2_80b4,
74    MgtAVttInst = 0x2_80b8,
75    // Vcc1V2BottomMax = 0x2_80bc,
76    // Vcc1V2BottomTopAvg = 0x2_80c0,
77    // Vcc1V2BottomInst = 0x2_80c4,
78    /// Current in mA
79    Pex12VCurrentInMax = 0x2_80c8,
80    Pex12VCurrentInAvg = 0x2_80cc,
81    Pex12VCurrentInInst = 0x2_80d0,
82    Aux12VCurrentInMax = 0x2_80d4,
83    Aux12VCurrentInAvg = 0x2_80d8,
84    Aux12VCurrentInInst = 0x2_80dc,
85    VccIntMax = 0x2_80e0,
86    VccIntAvg = 0x2_80e4,
87    VccIntInst = 0x2_80e8,
88    VccCurrentMax = 0x2_80ec,
89    VccCurrentAvg = 0x2_80f0,
90    VccCurrentInst = 0x2_80f4,
91    /// Temperature in C
92    FpgaTempMax = 0x2_80f8,
93    FpgaTempAvg = 0x2_80fc,
94    FpgaTempInst = 0x2_8100,
95    // CageTemp0Max = 0x2_8170,
96    // CageTemp0Avg = 0x2_8174,
97    // CageTemp0Inst = 0x2_8178,
98    // CageTemp1Max = 0x2_817c,
99    // CageTemp1Avg = 0x2_8180,
100    // CageTemp1Inst = 0x2_8184,
101    // CageTemp2Max = 0x2_8188,
102    // CageTemp2Avg = 0x2_818c,
103    // CageTemp2Inst = 0x2_8190,
104    // CageTemp3Max = 0x2_8194,
105    // CageTemp3Avg = 0x2_8198,
106    // CageTemp3Inst = 0x2_819c,
107    Hbm0TempMax = 0x2_8260,
108    Hbm0TempAvg = 0x2_8264,
109    Hbm0TempInst = 0x2_8268,
110    Vcc3V3Max = 0x2_826c,
111    Vcc3V3Avg = 0x2_8270,
112    Vcc3V3Inst = 0x2_8274,
113    Pex3V3CurrentInMax = 0x2_8278,
114    Pex3V3CurrentInAvg = 0x2_827c,
115    Pex3V3CurrentInInst = 0x2_8280,
116    VccIntCurrentMax = 0x2_8284,
117    VccIntCurrentAvg = 0x2_8288,
118    VccIntCurrentInst = 0x2_828c,
119    Hbm1TempMax = 0x2_82b4,
120    Hbm1TempAvg = 0x2_82b8,
121    Hbm1TempInst = 0x2_82bc,
122    // Aux1_12VMax = 0x2_82c0,
123    // Aux1_12VAvg = 0x2_82c4,
124    // Aux1_12VInst = 0x2_82c8,
125    VccIntTempMax = 0x2_82cc,
126    VccIntTempAvg = 0x2_82d0,
127    VccIntTempInst = 0x2_82d4,
128    // /// Power in mW
129    // Pex12VPowerMax = 0x2_82d8,
130    // Pex12VPowerAvg = 0x2_82dc,
131    // Pex12VPowerInst = 0x2_82e0,
132    // Pex3V3PowerMax = 0x2_82e4,
133    // Pex3V3PowerAvg = 0x2_82e8,
134    // Pex3V3PowerInst = 0x2_82ec,
135    /// Mailbox offset from the base address `0x2_8000`. Valid range `0x1000..0x1ffc`. Default `0x1000`.
136    HostMsgOffset = 0x2_8300,
137    HostMsgError = 0x2_8304,
138    /// Bit 0 is set if mailbox and sensor readings are ready.
139    HostStatus = 0x2_830c,
140}
141
142#[repr(u32)]
143pub enum ControlRegBit {
144    MaxAgvValuesReset = 1,
145    ErrorRegReset = 1 << 1,
146    MailboxStatus = 1 << 5,
147    MicroblazeReset = 1 << 6,
148    QsfpGpioEnable = 1 << 26,
149    HbmTempMonitorEnable = 1 << 27,
150}
151
152#[repr(u32)]
153pub enum MailboxMsgOpcode {
154    // Only applicable after flashing new SC firmware; TODO. This is called CMC_OP_MSP432_JUMP in
155    // "CMS SC Upgrade" UG. Calling it by itself is a bad idea.
156    //
157    // ScFwReboot = 3,
158    CardInfo = 4,
159}
160
161#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
162#[repr(u8)]
163pub enum CardInfoKey {
164    SerialNumber = 0x21,
165    MacAddress0 = 0x22,
166    MacAddress1 = 0x23,
167    MacAddress2 = 0x24,
168    MacAddress3 = 0x25,
169    CardRev = 0x26,
170    CardName = 0x27,
171    SatelliteVersion = 0x28,
172    TotalPowerAvail = 0x29,
173    FanPresence = 0x2a,
174    ConfigMode = 0x2b,
175    NewMacScheme = 0x4b,
176    CageType0 = 0x50,
177    CageType1 = 0x51,
178    CageType2 = 0x52,
179    CageType3 = 0x53,
180}
181
182pub type OldMacAddress = [u8; 16];
183pub type MacAddress = [u8; 6];
184
185#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
186#[repr(u8)]
187pub enum TotalPowerAvail {
188    Power75W,
189    Power150W,
190    Power225W,
191    Power300W,
192}
193
194#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
195#[repr(u8)]
196pub enum ConfigMode {
197    SlaveSerialX1,
198    SlaveSelectMapX8,
199    SlaveMapX16,
200    SlaveSelectMapX32,
201    JtagBoudaryScanX1,
202    MasterSpiX1,
203    MasterSpiX2,
204    MasterSpiX4,
205    MasterSpiX8,
206    MasterSpiX16,
207    MasterSerialX1,
208    MasterSelectMapX8,
209    MasterSelectMapX16,
210}
211
212#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
213#[repr(u8)]
214pub enum CageType {
215    Qsfp,
216    Dsfp,
217    Sfp,
218}
219
220#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
221pub enum CardInfoItem {
222    SerialNumber(Vec<u8>),
223    MacAddress0(OldMacAddress),
224    MacAddress1(OldMacAddress),
225    MacAddress2(OldMacAddress),
226    MacAddress3(OldMacAddress),
227    CardRev(Vec<u8>),
228    CardName(Vec<u8>),
229    SatelliteVersion(Vec<u8>),
230    TotalPowerAvail(TotalPowerAvail),
231    FanPresence(u8),
232    ConfigMode(ConfigMode),
233    NewMacScheme(u8, MacAddress),
234    CageType0(CageType),
235    CageType1(CageType),
236    CageType2(CageType),
237    CageType3(CageType),
238}
239
240#[derive(Copy, Clone, Debug, PartialEq)]
241pub enum CardInfoItemParseError {
242    IncompleteInput,
243    IncorrectLength,
244    NonNullTerminator,
245    CardInfoKey(TryFromPrimitiveError<CardInfoKey>),
246    TotalPowerAvail(TryFromPrimitiveError<TotalPowerAvail>),
247    ConfigMode(TryFromPrimitiveError<ConfigMode>),
248    CageType(TryFromPrimitiveError<CageType>),
249}
250
251impl From<TryFromPrimitiveError<CardInfoKey>> for CardInfoItemParseError {
252    fn from(e: TryFromPrimitiveError<CardInfoKey>) -> Self {
253        Self::CardInfoKey(e)
254    }
255}
256
257impl From<TryFromPrimitiveError<TotalPowerAvail>> for CardInfoItemParseError {
258    fn from(e: TryFromPrimitiveError<TotalPowerAvail>) -> Self {
259        Self::TotalPowerAvail(e)
260    }
261}
262
263impl From<TryFromPrimitiveError<ConfigMode>> for CardInfoItemParseError {
264    fn from(e: TryFromPrimitiveError<ConfigMode>) -> Self {
265        Self::ConfigMode(e)
266    }
267}
268
269impl From<TryFromPrimitiveError<CageType>> for CardInfoItemParseError {
270    fn from(e: TryFromPrimitiveError<CageType>) -> Self {
271        Self::CageType(e)
272    }
273}
274
275impl TryFrom<&[u8]> for CardInfoItem {
276    type Error = CardInfoItemParseError;
277
278    fn try_from(input: &[u8]) -> std::result::Result<Self, Self::Error> {
279        let mut iter = input.iter();
280        let mut next = || iter.next().ok_or(CardInfoItemParseError::IncompleteInput);
281        let key = CardInfoKey::try_from(*next()?)?;
282        let len = *next()? as usize;
283        match key {
284            CardInfoKey::SerialNumber => {
285                if len == 0 {
286                    return Err(CardInfoItemParseError::IncorrectLength);
287                }
288                let mut v = vec![];
289                for _ in 0..len - 1 {
290                    v.push(*next()?);
291                }
292                if *next()? != 0 {
293                    return Err(CardInfoItemParseError::NonNullTerminator);
294                }
295                Ok(Self::SerialNumber(v))
296            }
297            CardInfoKey::MacAddress0 => {
298                if len != 18 {
299                    return Err(CardInfoItemParseError::IncorrectLength);
300                }
301                let mut v = vec![];
302                for _ in 0..len - 1 {
303                    v.push(*next()?);
304                }
305                if *next()? != 0 {
306                    return Err(CardInfoItemParseError::NonNullTerminator);
307                }
308                let old_mac = v
309                    .try_into()
310                    .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
311                Ok(Self::MacAddress0(old_mac))
312            }
313            CardInfoKey::MacAddress1 => {
314                if len != 18 {
315                    return Err(CardInfoItemParseError::IncorrectLength);
316                }
317                let mut v = vec![];
318                for _ in 0..len - 1 {
319                    v.push(*next()?);
320                }
321                if *next()? != 0 {
322                    return Err(CardInfoItemParseError::NonNullTerminator);
323                }
324                let old_mac = v
325                    .try_into()
326                    .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
327                Ok(Self::MacAddress1(old_mac))
328            }
329            CardInfoKey::MacAddress2 => {
330                if len != 18 {
331                    return Err(CardInfoItemParseError::IncorrectLength);
332                }
333                let mut v = vec![];
334                for _ in 0..len - 1 {
335                    v.push(*next()?);
336                }
337                if *next()? != 0 {
338                    return Err(CardInfoItemParseError::NonNullTerminator);
339                }
340                let old_mac = v
341                    .try_into()
342                    .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
343                Ok(Self::MacAddress2(old_mac))
344            }
345            CardInfoKey::MacAddress3 => {
346                if len != 18 {
347                    return Err(CardInfoItemParseError::IncorrectLength);
348                }
349                let mut v = vec![];
350                for _ in 0..len - 1 {
351                    v.push(*next()?);
352                }
353                if *next()? != 0 {
354                    return Err(CardInfoItemParseError::NonNullTerminator);
355                }
356                let old_mac = v
357                    .try_into()
358                    .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
359                Ok(Self::MacAddress3(old_mac))
360            }
361            CardInfoKey::CardRev => {
362                if len == 0 {
363                    return Err(CardInfoItemParseError::IncorrectLength);
364                }
365                let mut v = vec![];
366                for _ in 0..len - 1 {
367                    v.push(*next()?);
368                }
369                if *next()? != 0 {
370                    return Err(CardInfoItemParseError::NonNullTerminator);
371                }
372                Ok(Self::CardRev(v))
373            }
374            CardInfoKey::CardName => {
375                if len == 0 {
376                    return Err(CardInfoItemParseError::IncorrectLength);
377                }
378                let mut v = vec![];
379                for _ in 0..len - 1 {
380                    v.push(*next()?);
381                }
382                if *next()? != 0 {
383                    return Err(CardInfoItemParseError::NonNullTerminator);
384                }
385                Ok(Self::CardName(v))
386            }
387            CardInfoKey::SatelliteVersion => {
388                if len == 0 {
389                    return Err(CardInfoItemParseError::IncorrectLength);
390                }
391                let mut v = vec![];
392                for _ in 0..len - 1 {
393                    v.push(*next()?);
394                }
395                if *next()? != 0 {
396                    return Err(CardInfoItemParseError::NonNullTerminator);
397                }
398                Ok(Self::SatelliteVersion(v))
399            }
400            CardInfoKey::TotalPowerAvail => {
401                if len != 1 {
402                    return Err(CardInfoItemParseError::IncorrectLength);
403                }
404                Ok(Self::TotalPowerAvail(TotalPowerAvail::try_from_primitive(
405                    *next()?,
406                )?))
407            }
408            CardInfoKey::FanPresence => {
409                if len != 1 {
410                    return Err(CardInfoItemParseError::IncorrectLength);
411                }
412                Ok(Self::FanPresence(*next()?))
413            }
414            CardInfoKey::ConfigMode => {
415                if len != 1 {
416                    return Err(CardInfoItemParseError::IncorrectLength);
417                }
418                Ok(Self::ConfigMode(ConfigMode::try_from_primitive(*next()?)?))
419            }
420            CardInfoKey::NewMacScheme => {
421                if len != 8 {
422                    return Err(CardInfoItemParseError::IncorrectLength);
423                }
424                let num_addresses = *next()?;
425                // Skip the reserved byte.
426                next()?;
427                let mut v = vec![];
428                for _ in 0..6 {
429                    v.push(*next()?);
430                }
431                let mac = v
432                    .try_into()
433                    .map_err(|_| CardInfoItemParseError::IncorrectLength)?;
434                Ok(Self::NewMacScheme(num_addresses, mac))
435            }
436            CardInfoKey::CageType0 => {
437                if len != 1 {
438                    return Err(CardInfoItemParseError::IncorrectLength);
439                }
440                Ok(Self::CageType0(CageType::try_from_primitive(*next()?)?))
441            }
442            CardInfoKey::CageType1 => {
443                if len != 1 {
444                    return Err(CardInfoItemParseError::IncorrectLength);
445                }
446                Ok(Self::CageType1(CageType::try_from_primitive(*next()?)?))
447            }
448            CardInfoKey::CageType2 => {
449                if len != 1 {
450                    return Err(CardInfoItemParseError::IncorrectLength);
451                }
452                Ok(Self::CageType2(CageType::try_from_primitive(*next()?)?))
453            }
454            CardInfoKey::CageType3 => {
455                if len != 1 {
456                    return Err(CardInfoItemParseError::IncorrectLength);
457                }
458                Ok(Self::CageType3(CageType::try_from_primitive(*next()?)?))
459            }
460        }
461    }
462}
463
464#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
465pub struct CardInfo(pub BTreeSet<CardInfoItem>);
466
467#[derive(Copy, Clone, Debug, PartialEq)]
468pub enum CardInfoParseError {
469    CardInfoItem(CardInfoItemParseError),
470    ItemLengthOutOfBounds,
471}
472
473impl From<CardInfoItemParseError> for CardInfoParseError {
474    fn from(e: CardInfoItemParseError) -> Self {
475        Self::CardInfoItem(e)
476    }
477}
478
479impl TryFrom<&[u8]> for CardInfo {
480    type Error = CardInfoParseError;
481
482    fn try_from(input: &[u8]) -> std::result::Result<Self, Self::Error> {
483        let mut set = BTreeSet::new();
484        let mut parsed_len = 0;
485        while parsed_len + 2 < input.len() {
486            let item_len = input[parsed_len + 1];
487            let to_parse_len = item_len as usize + 2;
488            let next_item_pos = parsed_len + to_parse_len;
489            debug!(
490                "parsed_len {}, item_len {}, next_item_pos {}",
491                parsed_len, item_len, next_item_pos
492            );
493            if next_item_pos - 1 > input.len() {
494                return Err(CardInfoParseError::ItemLengthOutOfBounds);
495            }
496            let item = CardInfoItem::try_from(&input[parsed_len..next_item_pos])?;
497            set.insert(item);
498            parsed_len += to_parse_len;
499        }
500        Ok(Self(set))
501    }
502}
503
504/// Card Management Solution subsystem
505pub trait CardMgmtSys {
506    /// Reads the value at a raw CMS address
507    fn get_cms_addr(&self, addr: u64) -> Result<u32>;
508
509    /// Writes the value at a raw CMS address
510    fn set_cms_addr(&self, addr: u64, value: u32) -> Result<()>;
511
512    /// Reads the value in a given CMS register
513    fn get_cms_reg(&self, reg: CmsReg) -> Result<u32> {
514        self.get_cms_addr(reg as u64)
515    }
516
517    /// Writes the value in a given CMS register
518    fn set_cms_reg(&self, reg: CmsReg, value: u32) -> Result<()> {
519        self.set_cms_addr(reg as u64, value)
520    }
521
522    /// Reads the value in the CMS control register
523    fn get_cms_control_reg(&self) -> Result<u32> {
524        self.get_cms_reg(CmsReg::Control)
525    }
526
527    /// Writes the value in the CMS control register
528    fn set_cms_control_reg(&self, value: u32) -> Result<()> {
529        self.set_cms_reg(CmsReg::Control, value)
530    }
531
532    /// Polls a given `mask` in a given CMS register `reg` continuously at least `n` times until the
533    /// mask is equal to the expected value.
534    fn poll_cms_reg_mask(&self, reg: CmsReg, mask: u32, expected: u32, n: usize) -> Result<()> {
535        iter::repeat_with(|| self.get_cms_reg(reg).ok())
536            .take(n)
537            .position(|ready| ready.map(|ready| ready & mask) == Some(expected))
538            .map(|_| ())
539            .ok_or(Error::CmsRegMaskNotAsExpected)
540    }
541
542    /// Polls a given `mask` in a given CMS register `reg` continuously at least `n` times until the
543    /// mask is equal to the expected value, sleeping for `duration` in between tries. Returns the
544    /// number of elapsed tries.
545    fn poll_cms_reg_mask_sleep(
546        &self,
547        reg: CmsReg,
548        mask: u32,
549        expected: u32,
550        n: usize,
551        duration: Duration,
552    ) -> Result<usize> {
553        iter::repeat_with(|| {
554            thread::sleep(duration);
555            self.get_cms_reg(reg).ok()
556        })
557        .take(n)
558        .position(|ready| ready.map(|ready| ready & mask) == Some(expected))
559        .map(|pos| pos + 1)
560        .ok_or(Error::CmsRegMaskNotAsExpected)
561    }
562
563    /// Polls a given `mask` in a given CMS register `reg` continuously at least `n` times until the
564    /// mask clears.
565    fn poll_cms_reg_clear(&self, reg: CmsReg, mask: u32, n: usize) -> Result<()> {
566        self.poll_cms_reg_mask(reg, mask, 0, n)
567    }
568
569    /// Polls a given `mask` in a given CMS register `reg` continuously at least `n` times until the
570    /// mask is set.
571    fn poll_cms_reg_set(&self, reg: CmsReg, mask: u32, n: usize) -> Result<()> {
572        self.poll_cms_reg_mask(reg, mask, mask, n)
573    }
574}
575
576/// CMS parameters
577pub trait CardMgmtSysParam {
578    const BASE_ADDR: u64;
579}
580
581pub trait CardMgmtOps {
582    /// Initialises the Card Management System
583    fn init_cms(&self) -> Result<()>;
584
585    // Waits roughly `ms` milliseconds to allow readings to be populated while polling the status
586    // register every 1ms. Returns the elapsed milliseconds.
587    fn expect_ready_host_status(&self, ms: usize) -> Result<usize>;
588
589    /// Enables HBM temperature monitoring
590    fn enable_hbm_temp_monitoring(&self) -> Result<()>;
591
592    /// Gets the mailbox offset from the base address
593    fn get_mailbox_offset(&self) -> Result<u64>;
594
595    // /// Issues a reboot of the satellite controller
596    // fn sc_fw_reboot(&mut self) -> Result<()>;
597
598    /// Gets the card information
599    fn get_card_info(&self) -> Result<CardInfo>;
600}
601
602impl<T> CardMgmtOps for T
603where
604    T: XdmaOps + CardMgmtSysParam,
605{
606    fn init_cms(&self) -> Result<()> {
607        self.set_cms_reg(CmsReg::MicroblazeResetN, 1)
608    }
609
610    fn expect_ready_host_status(&self, ms: usize) -> Result<usize> {
611        self.poll_cms_reg_mask_sleep(CmsReg::HostStatus, 1, 1, ms, Duration::from_millis(1))
612            .map_err(|_| Error::HostStatusNotReady)
613    }
614
615    fn enable_hbm_temp_monitoring(&self) -> Result<()> {
616        let v = self.get_cms_control_reg()?;
617        self.set_cms_control_reg(v | ControlRegBit::HbmTempMonitorEnable as u32)
618    }
619
620    fn get_mailbox_offset(&self) -> Result<u64> {
621        let control = self.get_cms_control_reg()?;
622        if 0 != control & ControlRegBit::MailboxStatus as u32 {
623            return Err(Error::MailboxNotAvailable);
624        }
625        let v = self.get_cms_reg(CmsReg::HostMsgOffset)?;
626        Ok(0x2_8000u64 + v as u64)
627    }
628
629    // fn sc_fw_reboot(&mut self) -> Result<()> {
630    //     let mbox_offset = self.get_mailbox_offset()?;
631    //     self.set_cms_addr(mbox_offset, (MailboxMsgOpcode::ScFwReboot as u32) << 24)?;
632    //     self.set_cms_addr(mbox_offset + 4, 0x00000201)?;
633    //     let control = self.get_cms_control_reg()?;
634    //     self.set_cms_control_reg(control | ControlRegBit::MailboxStatus as u32)?;
635    //     let error = self.get_cms_reg(CmsReg::HostMsgError)?;
636    //     if error != 0 {
637    //         Err(Error::HostMsgError(error))
638    //     } else {
639    //         Ok(())
640    //     }
641    // }
642
643    fn get_card_info(&self) -> Result<CardInfo> {
644        let mbox_offset = self.get_mailbox_offset()?;
645        debug!("mbox_offset 0x{:x}", mbox_offset);
646        self.set_cms_addr(mbox_offset, (MailboxMsgOpcode::CardInfo as u32) << 24)?;
647        let control = self.get_cms_control_reg()?;
648        self.set_cms_control_reg(control | ControlRegBit::MailboxStatus as u32)?;
649        // Wait for at most 1s.
650        self.poll_cms_reg_mask_sleep(
651            CmsReg::Control,
652            ControlRegBit::MailboxStatus as u32,
653            0,
654            100,
655            Duration::from_millis(10),
656        )?;
657
658        let error = self.get_cms_reg(CmsReg::HostMsgError)?;
659        if error != 0 {
660            return Err(Error::HostMsgError(error));
661        }
662
663        let len = self.get_cms_addr(mbox_offset)? & 0xfff;
664        let mut info_bytes = Vec::with_capacity(len as usize);
665        let mut data_offset = 4;
666        let mut remaining = len as isize;
667        while remaining > 0 {
668            let w = self.get_cms_addr(mbox_offset + data_offset as u64)?;
669            let bytes = w.to_le_bytes();
670            let num_bytes = remaining.min(4) as usize;
671            info_bytes.extend_from_slice(&bytes[..num_bytes]);
672            data_offset += 4;
673            remaining -= 4;
674        }
675        Ok(CardInfo::try_from(info_bytes.as_slice())?)
676    }
677}
678
679impl<T> CardMgmtSys for T
680where
681    T: XdmaOps + CardMgmtSysParam,
682{
683    fn get_cms_addr(&self, addr: u64) -> Result<u32> {
684        let mut data = [0u8; 4];
685        self.shell_read(&mut data, T::BASE_ADDR + addr)
686            .map_err(Error::XdmaFailed)?;
687        Ok(u32::from_le_bytes(data))
688    }
689
690    fn set_cms_addr(&self, addr: u64, value: u32) -> Result<()> {
691        let data = value.to_le_bytes();
692        self.shell_write(&data, T::BASE_ADDR + addr)
693            .map_err(Error::XdmaFailed)
694    }
695}
696
697#[cfg(test)]
698mod test {
699    use super::*;
700    use test_log::test;
701
702    #[test]
703    fn parse_pg348_example_card_info() {
704        let card_info_bytes: Vec<u8> = vec![
705            0x27, 0x0d, 0x41, 0x4c, 0x56, 0x45, 0x4f, 0x20, 0x55, 0x35, 0x30, 0x20, 0x50, 0x51,
706            0x00, 0x26, 0x02, 0x31, 0x00, 0x21, 0x0d, 0x35, 0x30, 0x31, 0x32, 0x31, 0x31, 0x31,
707            0x39, 0x43, 0x53, 0x50, 0x4d, 0x00, 0x4b, 0x08, 0x04, 0x00, 0x00, 0x0a, 0x35, 0x05,
708            0x0f, 0xd8, 0x2a, 0x01, 0x50, 0x2b, 0x01, 0x07, 0x29, 0x01, 0x00, 0x28, 0x04, 0x35,
709            0x2e, 0x30, 0x00,
710        ];
711        let expected_card_info = CardInfo(
712            vec![
713                CardInfoItem::SerialNumber(b"50121119CSPM".to_vec()),
714                CardInfoItem::CardRev(b"1".to_vec()),
715                CardInfoItem::CardName(b"ALVEO U50 PQ".to_vec()),
716                CardInfoItem::SatelliteVersion(b"5.0".to_vec()),
717                CardInfoItem::TotalPowerAvail(TotalPowerAvail::Power75W),
718                CardInfoItem::FanPresence(b'P'),
719                CardInfoItem::ConfigMode(ConfigMode::MasterSpiX4),
720                CardInfoItem::NewMacScheme(4, [0x00, 0x0a, 0x35, 0x05, 0x0f, 0xd8]),
721            ]
722            .into_iter()
723            .collect(),
724        );
725        assert_eq!(
726            CardInfo::try_from(card_info_bytes.as_slice()),
727            Ok(expected_card_info)
728        );
729    }
730
731    #[test]
732    fn card_info_sernum_zero_length() {
733        let card_info_bytes: Vec<u8> = vec![0x21, 0x00, 0x31, 0x32, 0x33, 0x00];
734        assert_eq!(
735            CardInfo::try_from(card_info_bytes.as_slice()),
736            Err(CardInfoParseError::CardInfoItem(
737                CardInfoItemParseError::IncorrectLength
738            ))
739        );
740    }
741
742    #[test]
743    fn card_info_sernum_length_out_of_bounds() {
744        let card_info_bytes: Vec<u8> = vec![0x21, 0xff, 0x31, 0x32, 0x33, 0x00];
745        assert_eq!(
746            CardInfo::try_from(card_info_bytes.as_slice()),
747            Err(CardInfoParseError::ItemLengthOutOfBounds)
748        );
749    }
750
751    #[test]
752    fn card_info_sernum_non_null_terminator() {
753        let card_info_bytes: Vec<u8> = vec![0x21, 0x04, 0x31, 0x32, 0x33, 0xff];
754        assert_eq!(
755            CardInfo::try_from(card_info_bytes.as_slice()),
756            Err(CardInfoParseError::CardInfoItem(
757                CardInfoItemParseError::NonNullTerminator
758            ))
759        );
760    }
761}