opentitanlib/spiflash/
sfdp.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use byteorder::{LittleEndian, ReadBytesExt};
6use serde::Serialize;
7use serde_annotate::Annotate;
8use std::convert::TryFrom;
9use std::time::Duration;
10use thiserror::Error;
11
12use crate::util::bitfield::BitField;
13
14#[derive(Debug, Error)]
15pub enum Error {
16    #[error(transparent)]
17    Io(#[from] std::io::Error),
18    #[error(transparent)]
19    TryFromIntError(#[from] std::num::TryFromIntError),
20    #[error("the range {0}..{1} is out of bounds")]
21    SliceRange(usize, usize),
22    #[error("SFDP header contains incorrect signature: {0:#010x}")]
23    WrongHeaderSignature(u32),
24
25    // This is only needed to meet the error conversion requirements for the most
26    // general case in the field! macro below.
27    #[error(transparent)]
28    Infallible(#[from] std::convert::Infallible),
29}
30
31/// The SFDP header identifies a valid SFDP, its version and the number of parameter headers.
32#[derive(Debug, Serialize, Annotate)]
33pub struct SfdpHeader {
34    #[annotate(format=hex, comment=comment_signature())]
35    pub signature: u32,
36    #[annotate(comment=comment_version())]
37    pub minor: u8,
38    pub major: u8,
39    #[annotate(comment = "Number of parameter headers minus one")]
40    pub nph: u8,
41    #[annotate(format=bin, comment="The reserved field should be all ones")]
42    pub reserved: u8,
43}
44
45impl SfdpHeader {
46    fn comment_signature(&self) -> Option<String> {
47        let signature = self.signature.to_le_bytes().map(|b| {
48            match b {
49                // The ASCII printable range is [0x20, 0x7E].
50                0x20..=0x7E => b as char,
51                _ => '.',
52            }
53        });
54        Some(format!(
55            "Signature value='{}{}{}{}' (should be 'SFDP')",
56            signature[0], signature[1], signature[2], signature[3],
57        ))
58    }
59    fn comment_version(&self) -> Option<String> {
60        Some(format!("{}.{}", self.major, self.minor))
61    }
62}
63
64impl TryFrom<&[u8]> for SfdpHeader {
65    type Error = Error;
66    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
67        const SFDP_HEADER_SIGNATURE: u32 = 0x50444653;
68
69        let mut reader = std::io::Cursor::new(buf);
70        let header = SfdpHeader {
71            signature: reader.read_u32::<LittleEndian>()?,
72            minor: reader.read_u8()?,
73            major: reader.read_u8()?,
74            nph: reader.read_u8()?,
75            reserved: reader.read_u8()?,
76        };
77        match header.signature {
78            SFDP_HEADER_SIGNATURE => Ok(header),
79            v => Err(Error::WrongHeaderSignature(v)),
80        }
81    }
82}
83
84/// The SFDP parameter header identifies additional parameter tables within the SFDP.
85/// All devices are required to have a JEDEC parameter header and corresponding table.
86#[derive(Debug, Serialize)]
87pub struct SfdpPhdr {
88    pub id: u8,
89    pub minor: u8,
90    pub major: u8,
91    pub dwords: u8,
92    pub offset: u32,
93}
94
95impl TryFrom<&[u8]> for SfdpPhdr {
96    type Error = Error;
97    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
98        let mut reader = std::io::Cursor::new(buf);
99        Ok(SfdpPhdr {
100            id: reader.read_u8()?,
101            minor: reader.read_u8()?,
102            major: reader.read_u8()?,
103            dwords: reader.read_u8()?,
104            offset: reader.read_u32::<LittleEndian>()? & 0x00FFFFFFu32,
105        })
106    }
107}
108
109// The internal JEDEC parameter struct is used as an intermediate representation
110// in creating the user-visible JEDEC parameter struct.
111struct InternalJedecParams {
112    pub data: Vec<u32>,
113}
114
115impl TryFrom<&[u8]> for InternalJedecParams {
116    type Error = Error;
117    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
118        let mut reader = std::io::Cursor::new(buf);
119        let mut data = vec![0u32; buf.len() / 4];
120        reader.read_u32_into::<LittleEndian>(&mut data)?;
121        Ok(InternalJedecParams { data })
122    }
123}
124
125// The JEDEC parameter table is documented in JESD216 "Serial Flash Discoverable Parameters".
126// The format of the parameter table is expressed in that document as a set of bitfields
127// in the various "dwords" which make up the table.
128//
129// We use the `field` macro to express how to extract and convert the various bitfields
130// into useful values.  The macro generates a function named `ident` which extracts
131// `bitsize` bits starting at `bitoffset` in the `field`th "dword" of the data.
132//
133// Since the documentation for dword offsets is 1-based, the marco parameter `field`
134// is also 1-based.
135macro_rules! field {
136    // Extract to bool.
137    ($name:ident -> bool, $field:expr, $bitoffset:expr, $bitsize:expr) => {
138        pub fn $name(&self) -> Result<bool, Error> {
139            Ok(BitField::new($bitoffset, $bitsize).extract(self.data[$field - 1]) != 0)
140        }
141    };
142    // Extract an entire u32 word.
143    ($name:ident -> u32, $field:expr, 0, 32) => {
144        pub fn $name(&self) -> Result<u32, Error> {
145            Ok(self.data[$field - 1])
146        }
147    };
148    // Extract a bitfield as u32.
149    ($name:ident -> u32, $field:expr, $bitoffset:expr, $bitsize:expr) => {
150        pub fn $name(&self) -> Result<u32, Error> {
151            Ok(BitField::new($bitoffset, $bitsize).extract(self.data[$field - 1]))
152        }
153    };
154    // Extract a bitfield as some other type.
155    ($name:ident -> $type:ty, $field:expr, $bitoffset:expr, $bitsize:expr) => {
156        pub fn $name(&self) -> Result<$type, Error> {
157            Ok(<$type>::try_from(
158                BitField::new($bitoffset, $bitsize).extract(self.data[$field - 1]),
159            )?)
160        }
161    };
162}
163
164impl InternalJedecParams {
165    field!(block_erase_size -> BlockEraseSize, 1, 0, 2);
166    field!(write_granularity -> WriteGranularity, 1, 2, 1);
167    field!(write_en_required -> bool, 1, 3, 1);
168    field!(write_en_opcode -> bool, 1, 4, 1);
169    field!(erase_opcode_4kib -> u8, 1, 8, 8);
170    field!(support_fast_read_112 -> bool, 1, 16, 1);
171    field!(address_modes -> SupportedAddressModes, 1, 17, 2);
172    field!(support_double_rate_clocking -> bool, 1, 19, 1);
173    field!(support_fast_read_122 -> bool, 1, 20, 1);
174    field!(support_fast_read_144 -> bool, 1, 21, 1);
175    field!(support_fast_read_114 -> bool, 1, 22, 1);
176
177    field!(density -> u32, 2, 0, 31);
178    field!(density_pow2 -> bool, 2, 31, 1);
179
180    field!(wait_states_144 -> u8, 3, 0, 5);
181    field!(mode_bits_144 -> u8, 3, 5, 3);
182    field!(read_opcode_144 -> u8, 3, 8, 8);
183    field!(wait_states_114 -> u8, 3, 16, 5);
184    field!(mode_bits_114 -> u8, 3, 21, 3);
185    field!(read_opcode_114 -> u8, 3, 24, 8);
186
187    field!(wait_states_112 -> u8, 4, 0, 5);
188    field!(mode_bits_112 -> u8, 4, 5, 3);
189    field!(read_opcode_112 -> u8, 4, 8, 8);
190    field!(wait_states_122 -> u8, 4, 16, 5);
191    field!(mode_bits_122 -> u8, 4, 21, 3);
192    field!(read_opcode_122 -> u8, 4, 24, 8);
193
194    field!(support_fast_read_222 -> bool, 5, 0, 1);
195    field!(support_fast_read_444 -> bool, 5, 4, 1);
196
197    field!(wait_states_222 -> u8, 6, 16, 5);
198    field!(mode_bits_222 -> u8, 6, 21, 3);
199    field!(read_opcode_222 -> u8, 6, 24, 8);
200
201    field!(wait_states_444 -> u8, 7, 16, 5);
202    field!(mode_bits_444 -> u8, 7, 21, 3);
203    field!(read_opcode_444 -> u8, 7, 24, 8);
204
205    field!(sector_type1_size -> u8, 8, 0, 8);
206    field!(sector_type1_opcode -> u8, 8, 8, 8);
207    field!(sector_type2_size -> u8, 8, 16, 8);
208    field!(sector_type2_opcode -> u8, 8, 24, 8);
209
210    field!(sector_type3_size -> u8, 9, 0, 8);
211    field!(sector_type3_opcode -> u8, 9, 8, 8);
212    field!(sector_type4_size -> u8, 9, 16, 8);
213    field!(sector_type4_opcode -> u8, 9, 24, 8);
214
215    // JESD216B pp. 20-21.
216    field!(erase_time_multiplier -> u32, 10, 0, 4);
217    field!(erase_type1_time -> u32, 10, 4, 5);
218    field!(erase_type1_unit -> usize, 10, 9, 2);
219    field!(erase_type2_time -> u32, 10, 11, 5);
220    field!(erase_type2_unit -> usize, 10, 16, 2);
221    field!(erase_type3_time -> u32, 10, 18, 5);
222    field!(erase_type3_unit -> usize, 10, 23, 2);
223    field!(erase_type4_time -> u32, 10, 25, 5);
224    field!(erase_type4_unit -> usize, 10, 30, 2);
225
226    // JESD216B pg. 22.
227    field!(pgm_time_multiplier -> u32, 11, 0, 4);
228    field!(page_size -> u32, 11, 4, 4);
229    field!(page_pgm_time -> u32, 11, 8, 5);
230    field!(page_pgm_unit -> usize, 11, 13, 1);
231    field!(byte_pgm_time -> u32, 11, 14, 4);
232    field!(byte_pgm_unit -> usize, 11, 18, 1);
233    field!(additional_byte_pgm_time -> u32, 11, 19, 4);
234    field!(additional_byte_pgm_unit -> usize, 11, 23, 1);
235    field!(chip_erase_time -> u32, 11, 24, 5);
236    field!(chip_erase_unit -> usize, 11, 29, 2);
237
238    // JESD216B pp. 23-24.
239    field!(prohibited_pgm_suspend -> u8, 12, 0, 4);
240    field!(prohibited_erase_suspend -> u8, 12, 4, 4);
241    field!(pgm_resume_to_suspend -> u32, 12, 9, 4);
242    field!(suspend_pgm_latency_time -> u32, 12, 13, 5);
243    field!(suspend_pgm_latency_unit -> usize, 12, 18, 2);
244    field!(erase_resume_to_suspend -> u32, 12, 20, 4);
245    field!(suspend_erase_latency_time -> u32, 12, 24, 5);
246    field!(suspend_erase_latency_unit -> usize, 12, 29, 2);
247    field!(suspend_resume_supported -> bool, 12, 31, 1);
248
249    // JESD216B pg. 25.
250    field!(pgm_resume_instruction -> u8, 13, 0, 8);
251    field!(pgm_suspend_instruction -> u8, 13, 8, 8);
252    field!(resume_instruction -> u8, 13, 16, 8);
253    field!(suspend_instruction -> u8, 13, 24, 8);
254
255    // JESD216B pg. 25.
256    field!(status_busy_polling -> u8, 14, 2, 6);
257    field!(exit_deep_powerdown_time -> u32, 14, 8, 5);
258    field!(exit_deep_powerdown_unit -> usize, 14, 13, 2);
259    field!(exit_deep_powerdown_instruction -> u8, 14, 15, 8);
260    field!(enter_deep_powerdown_instruction -> u8, 14, 23, 8);
261    field!(deep_powerdown_supported -> bool, 14, 31, 1);
262
263    // JESD216B pp. 26-27.
264    field!(mode_444_disable -> u8, 15, 0, 4);
265    field!(mode_444_enable -> u8, 15, 4, 5);
266    field!(mode_444_supported -> bool, 15, 9, 1);
267    field!(mode_444_exit -> u8, 15, 10, 6);
268    field!(mode_444_entry -> u8, 15, 16, 4);
269    field!(quad_enable_requirements -> u8, 15, 20, 3);
270    field!(hold_or_reset_disable -> bool, 15, 23, 1);
271
272    // JESD216B pp. 28-29.
273    field!(status_reg1_write_enable -> u8, 16, 0, 6);
274    field!(soft_reset_support -> u8, 16, 8, 6);
275    field!(exit_4b_addressing -> u16, 16, 14, 10);
276    field!(enter_4b_addressing -> u8, 16, 24, 8);
277
278    // JESD216D.01 pg. 44.
279    field!(wait_states_188 -> u8, 17, 0, 5);
280    field!(mode_bits_188 -> u8, 17, 5, 3);
281    field!(read_opcode_188 -> u8, 17, 8, 8);
282    field!(wait_states_118 -> u8, 17, 16, 5);
283    field!(mode_bits_118 -> u8, 17, 21, 3);
284    field!(read_opcode_118 -> u8, 17, 24, 8);
285
286    // JESD216D.01 pg. 45.
287    field!(output_driver_strength -> u8, 18, 18, 5);
288    field!(spi_protocol_reset -> bool, 18, 23, 1);
289    field!(data_strobe_str -> u8, 18, 24, 2);
290    field!(data_qpi_str -> bool, 18, 26, 1);
291    field!(data_qpi_dtr -> bool, 18, 27, 1);
292    field!(octal_dtr_command -> u8, 18, 29, 2);
293    field!(octal_byte_order -> u8, 18, 31, 1);
294
295    // JESD216D.01 pg. 48.
296    field!(mode_8s8s8s_disable-> u8, 19, 0, 4);
297    field!(mode_8s8s8s_enable-> u8, 19, 4, 5);
298    field!(mode_088_supported -> bool, 19, 9, 1);
299    field!(mode_088_exit -> u8, 19, 10, 6);
300    field!(mode_088_enter -> u8, 19, 16, 4);
301    field!(octal_enable_requirements -> u8, 19, 20, 3);
302
303    // JESD216D.01 pp. 49-51.
304    field!(max_speed_4s4s4s_no_strobe -> MaxSpeed, 20, 0, 4);
305    field!(max_speed_4s4s4s_strobe    -> MaxSpeed, 20, 4, 4);
306    field!(max_speed_4d4d4d_no_strobe -> MaxSpeed, 20, 8, 4);
307    field!(max_speed_4d4d4d_strobe    -> MaxSpeed, 20, 12, 4);
308    field!(max_speed_8s8s8s_no_strobe -> MaxSpeed, 20, 16, 4);
309    field!(max_speed_8s8s8s_strobe    -> MaxSpeed, 20, 20, 4);
310    field!(max_speed_8d8d8d_no_strobe -> MaxSpeed, 20, 24, 4);
311    field!(max_speed_8d8d8d_strobe    -> MaxSpeed, 20, 28, 4);
312
313    // JESD216F.01 pg. 52.
314    field!(support_fast_read_1s1d1d -> bool, 21, 0, 1);
315    field!(support_fast_read_1s2d2d -> bool, 21, 1, 1);
316    field!(support_fast_read_1s4d4d -> bool, 21, 2, 1);
317    field!(support_fast_read_4s4d4d -> bool, 21, 3, 1);
318
319    // JESD216F.01 pg. 53.
320    field!(wait_states_1s1d1d -> u8, 22, 0, 5);
321    field!(mode_bits_1s1d1d   -> u8, 22, 5, 3);
322    field!(read_opcode_1s1d1d -> u8, 22, 8, 8);
323    field!(wait_states_1s2d2d -> u8, 22, 16, 5);
324    field!(mode_bits_1s2d2d   -> u8, 22, 21, 3);
325    field!(read_opcode_1s2d2d -> u8, 22, 24, 8);
326
327    // JESD216F.01 pg. 54.
328    field!(wait_states_1s4d4d -> u8, 23, 0, 5);
329    field!(mode_bits_1s4d4d   -> u8, 23, 5, 3);
330    field!(read_opcode_1s4d4d -> u8, 23, 8, 8);
331    field!(wait_states_4s4d4d -> u8, 23, 16, 5);
332    field!(mode_bits_4s4d4d   -> u8, 23, 21, 3);
333    field!(read_opcode_4s4d4d -> u8, 23, 24, 8);
334}
335
336/// `BlockEraseSize` represents whether or not the device can perform
337/// a 4KiB erase.
338#[derive(Default, Debug, Eq, PartialEq, strum::FromRepr, Clone, Copy, Serialize)]
339#[repr(u32)]
340pub enum BlockEraseSize {
341    Reserved0 = 0,
342    Block4KiB = 1,
343    Reserved2 = 2,
344    BlockNot4KiB = 3,
345    #[default]
346    Invalid,
347}
348
349impl From<u32> for BlockEraseSize {
350    fn from(val: u32) -> Self {
351        Self::from_repr(val).unwrap_or(Self::Invalid)
352    }
353}
354
355/// `WriteGranularity` represents whether or not the device has an internal
356/// buffer for program operations.
357#[derive(Default, Debug, Eq, PartialEq, strum::FromRepr, Clone, Copy, Serialize)]
358#[repr(u32)]
359pub enum WriteGranularity {
360    Granularity1Byte = 0,
361    Granularity64Bytes = 1,
362    #[default]
363    Invalid,
364}
365
366impl From<u32> for WriteGranularity {
367    fn from(val: u32) -> Self {
368        Self::from_repr(val).unwrap_or(Self::Invalid)
369    }
370}
371
372/// `SupportedAddressModes` represents which addressing modes are valid for
373/// the device.
374#[derive(Default, Debug, Eq, PartialEq, strum::FromRepr, Clone, Copy, Serialize)]
375#[repr(u32)]
376pub enum SupportedAddressModes {
377    Mode3b = 0,
378    Mode3b4b = 1,
379    Mode4b = 2,
380    Reserved = 3,
381    #[default]
382    Invalid,
383}
384
385impl From<u32> for SupportedAddressModes {
386    fn from(val: u32) -> Self {
387        Self::from_repr(val).unwrap_or(Self::Invalid)
388    }
389}
390
391#[derive(Default, Debug, Eq, PartialEq, strum::FromRepr, Clone, Copy, Serialize)]
392#[repr(u32)]
393pub enum MaxSpeed {
394    Reserved0 = 0,
395    Speed33MHz,
396    Speed50MHz,
397    Speed66MHz,
398    Speed80MHz,
399    Speed100MHz,
400    Speed133MHz,
401    Speed166MHz,
402    Speed200MHz,
403    Speed250MHz,
404    Speed266MHz,
405    Speed333MHz,
406    Speed400MHz,
407    Reserved10,
408    NotCharacterized,
409    #[default]
410    NotSupported,
411}
412
413impl From<u32> for MaxSpeed {
414    fn from(val: u32) -> Self {
415        Self::from_repr(val).unwrap_or(Self::NotSupported)
416    }
417}
418
419/// `FastReadParam` represents the parameters for the different styles of fast read.
420#[derive(Clone, Default, Debug, Serialize, Annotate)]
421pub struct FastReadParam {
422    pub wait_states: u8,
423    pub mode_bits: u8,
424    #[annotate(format=hex)]
425    pub opcode: u8,
426}
427
428/// `SectorErase` represents the supported erase sector sizes of the device.
429#[derive(Clone, Default, Debug, Serialize, Annotate)]
430pub struct SectorErase {
431    pub size: u32,
432    #[annotate(format=hex)]
433    pub opcode: u8,
434    pub time: Option<TimeBound>,
435}
436
437#[derive(Clone, Default, Debug, Serialize)]
438pub struct TimeBound {
439    #[serde(with = "humantime_serde")]
440    pub typical: Duration,
441    #[serde(with = "humantime_serde")]
442    pub maximum: Duration,
443}
444
445/// The JEDEC parameter table is documented in JESD216 "Serial Flash Discoverable Parameters".
446/// This structure represents the parameter table after decoding it from the packed bitfield
447/// representation documented by JEDEC.
448///
449/// I have access to the following specs:
450/// - JESD216 dated April 2011 (Flash parameters version 1.0).
451/// - JESD216B dated May 2014 (Flash parameters version 1.6).
452/// - JESD216D dated August 2019 (Flash parameters version 1.7, unchanged since rev C).
453/// - JESD216F dated February 2022 (Flash parameters version 1.7, unchanged since rev D).
454///
455/// - Version 1.0 defines a table of 9 "dwords". I'm considering this to be the
456///   minimum required parameter table.
457/// - Rev B, version 1.6 extends the table to 16 "dwords", including information about
458///   erase timing, powerdown and alternate mode requirements.
459/// - Rev D, version 1.7 extends the table to 20 "dwords", including information about
460///   eight-lane SPI modes.
461/// - Rev F, version 1.7 extends the table to 23 "dwords", including information about
462///   dual data rate operations.
463#[derive(Default, Debug, Serialize, Annotate)]
464pub struct JedecParams {
465    /// Erase granularity.
466    pub block_erase_size: BlockEraseSize,
467    /// Write granularity / buffer size.
468    pub write_granularity: WriteGranularity,
469    /// WREN instruction required for writing to volatile status register.
470    pub write_en_required: bool,
471    /// Write enable opocde (this is a single bit in the jedec table; expanded
472    /// to the actual opcode here).
473    #[annotate(format=hex)]
474    pub write_en_opcode: u8,
475    /// Erase opcode for 4KiB sector erase.
476    #[annotate(format=hex)]
477    pub erase_opcode_4kib: u8,
478    /// Support Fast Read 1-1-2.
479    pub support_fast_read_112: bool,
480    /// The address modes supported by the device.
481    pub address_modes: SupportedAddressModes,
482    /// Device supports double rate clocking.
483    pub support_double_rate_clocking: bool,
484    /// Other styles of Fast Read.  The numerical designator refers to
485    /// the instruction transfer mode, the address transfer mode and data
486    /// transfer mode.
487    /// ie: 1-2-2 means single bit mode for the opcode, dual mode for the address
488    /// and dual mode for the data phase.
489    pub support_fast_read_122: bool,
490    pub support_fast_read_144: bool,
491    pub support_fast_read_114: bool,
492    pub support_fast_read_222: bool,
493    pub support_fast_read_444: bool,
494
495    /// The density of the part. In the jedec table, this is expressed in bits,
496    /// but it is converted to bytes here.
497    pub density: u32,
498    /// Parameters for the various supported FastRead modes.
499    pub param_112: FastReadParam,
500    pub param_122: FastReadParam,
501    pub param_114: FastReadParam,
502    pub param_144: FastReadParam,
503    pub param_222: FastReadParam,
504    pub param_444: FastReadParam,
505    /// Erase sector sizes.  It is common for devices to support a 4KiB erase
506    /// size, a 32KiB erase size and a 64KiB erase size.
507    pub erase: [SectorErase; 4],
508
509    // TODO: Get ahold of Revs A, C & E and restructure the revision extensions.
510    pub rev_b: Option<JedecParamsRevB>,
511    pub rev_d: Option<JedecParamsRevD>,
512    pub rev_f: Option<JedecParamsRevF>,
513}
514
515/// The Rev B extensions to the JEDEC parameters table.
516#[derive(Default, Debug, Serialize, Annotate)]
517pub struct JedecParamsRevB {
518    pub page_size: u32,
519    pub page_program_time: TimeBound,
520    pub byte_program_time: TimeBound,
521    pub additional_byte_program_time: TimeBound,
522    pub chip_erase_time: TimeBound,
523
524    pub suspend_resume_supported: bool,
525    pub prohibited_ops_program_suspend: u8,
526    pub prohibited_ops_erase_suspend: u8,
527    #[serde(with = "humantime_serde")]
528    pub program_resume_to_suspend: Duration,
529    #[serde(with = "humantime_serde")]
530    pub suspend_in_progress_program_latency: Duration,
531    #[serde(with = "humantime_serde")]
532    pub erase_resume_to_suspend: Duration,
533    #[serde(with = "humantime_serde")]
534    pub suspend_in_progress_erase_latency: Duration,
535    #[annotate(format=hex)]
536    pub program_resume_instruction: u8,
537    #[annotate(format=hex)]
538    pub program_suspend_instruction: u8,
539    #[annotate(format=hex)]
540    pub resume_instruction: u8,
541    #[annotate(format=hex)]
542    pub suspend_instruction: u8,
543
544    pub deep_powerdown_supported: bool,
545    #[annotate(format=hex)]
546    pub enter_deep_powerdown_instruction: u8,
547    #[annotate(format=hex)]
548    pub exit_deep_powerdown_instruction: u8,
549    #[serde(with = "humantime_serde")]
550    pub exit_deep_powerdown_delay: Duration,
551    pub status_register_polling: u8,
552
553    pub hold_or_reset_disable: bool,
554    pub quad_enable_requirements: u8,
555    pub mode_444_entry: u8,
556    pub mode_444_exit: u8,
557    pub mode_444_supported: bool,
558    pub mode_444_disable: u8,
559    pub mode_444_enable: u8,
560
561    pub enter_4b_addressing: u8,
562    pub exit_4b_addressing: u16,
563    pub soft_reset_support: u8,
564    pub status_reg1_write_enable: u8,
565}
566
567/// The Rev D extensions to the JEDEC parameters table.
568#[derive(Default, Debug, Serialize)]
569pub struct JedecParamsRevD {
570    pub param_118: FastReadParam,
571    pub param_188: FastReadParam,
572
573    pub output_driver_strength: u8,
574    pub jedec_spi_protocol_reset: bool,
575    pub data_strobe_waveform_str: u8,
576    pub data_strobe_support_4s4s4s: bool,
577    pub data_strobe_support_4d4d4d: bool,
578    pub octal_dtr_command: u8,
579    pub octal_byte_order: u8,
580
581    pub mode_8s8s8s_disable: u8,
582    pub mode_8s8s8s_enable: u8,
583    pub mode_088_supported: bool,
584    pub mode_088_exit: u8,
585    pub mode_088_enter: u8,
586    pub octal_enable_requirements: u8,
587
588    pub max_speed_4s4s4s_no_strobe: MaxSpeed,
589    pub max_speed_4s4s4s_strobe: MaxSpeed,
590    pub max_speed_4d4d4d_no_strobe: MaxSpeed,
591    pub max_speed_4d4d4d_strobe: MaxSpeed,
592    pub max_speed_8s8s8s_no_strobe: MaxSpeed,
593    pub max_speed_8s8s8s_strobe: MaxSpeed,
594    pub max_speed_8d8d8d_no_strobe: MaxSpeed,
595    pub max_speed_8d8d8d_strobe: MaxSpeed,
596}
597
598/// The Rev F extensions to the JEDEC parameters table.
599#[derive(Default, Debug, Serialize)]
600pub struct JedecParamsRevF {
601    pub param_1s1d1d: Option<FastReadParam>,
602    pub param_1s2d2d: Option<FastReadParam>,
603    pub param_1s4d4d: Option<FastReadParam>,
604    pub param_4s4d4d: Option<FastReadParam>,
605}
606
607impl JedecParams {
608    // JESD216B section 6.4.13 (pg. 20)
609    const ERASE_TIME_UNITS: [Duration; 4] = [
610        Duration::from_millis(1),
611        Duration::from_millis(16),
612        Duration::from_millis(128),
613        Duration::from_secs(1),
614    ];
615    // JESD216B section 6.4.14 (pg. 22)
616    const CHIP_ERASE_UNITS: [Duration; 4] = [
617        Duration::from_millis(16),
618        Duration::from_millis(256),
619        Duration::from_secs(4),
620        Duration::from_secs(64),
621    ];
622    // JESD216B section 6.4.14 (pg. 22)
623    const PAGE_PGM_UNITS: [Duration; 2] = [Duration::from_micros(8), Duration::from_micros(64)];
624    // JESD216B section 6.4.14 (pg. 22)
625    const BYTE_PGM_UNITS: [Duration; 2] = [Duration::from_micros(1), Duration::from_micros(8)];
626    // JESD216B section 6.4.15 (pg. 23)
627    const SUSPEND_RESUME_UNITS: [Duration; 4] = [
628        Duration::from_nanos(128),
629        Duration::from_micros(1),
630        Duration::from_micros(8),
631        Duration::from_micros(64),
632    ];
633
634    fn pow2_size(shift: u8) -> u32 {
635        if shift == 0 { 0 } else { 1u32 << shift }
636    }
637}
638
639impl TryFrom<&[u8]> for JedecParams {
640    type Error = Error;
641    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
642        let p = InternalJedecParams::try_from(buf)?;
643        let size_bytes = if p.density_pow2()? {
644            1u32 << ((p.density()? & 0x7FFF_FFFF) - 8)
645        } else {
646            (p.density()? + 1) / 8
647        };
648        let mut jedec = JedecParams {
649            block_erase_size: p.block_erase_size()?,
650            write_granularity: p.write_granularity()?,
651            write_en_required: p.write_en_required()?,
652            write_en_opcode: if p.write_en_opcode()? { 0x50 } else { 0x06 },
653            erase_opcode_4kib: p.erase_opcode_4kib()?,
654            support_fast_read_112: p.support_fast_read_112()?,
655            address_modes: p.address_modes()?,
656            support_double_rate_clocking: p.support_double_rate_clocking()?,
657            support_fast_read_122: p.support_fast_read_122()?,
658            support_fast_read_144: p.support_fast_read_144()?,
659            support_fast_read_114: p.support_fast_read_114()?,
660            support_fast_read_222: p.support_fast_read_222()?,
661            support_fast_read_444: p.support_fast_read_444()?,
662            density: size_bytes,
663            erase: [
664                SectorErase {
665                    size: Self::pow2_size(p.sector_type1_size()?),
666                    opcode: p.sector_type1_opcode()?,
667                    time: None,
668                },
669                SectorErase {
670                    size: Self::pow2_size(p.sector_type2_size()?),
671                    opcode: p.sector_type2_opcode()?,
672                    time: None,
673                },
674                SectorErase {
675                    size: Self::pow2_size(p.sector_type3_size()?),
676                    opcode: p.sector_type3_opcode()?,
677                    time: None,
678                },
679                SectorErase {
680                    size: Self::pow2_size(p.sector_type4_size()?),
681                    opcode: p.sector_type4_opcode()?,
682                    time: None,
683                },
684            ],
685            ..Default::default()
686        };
687        if jedec.support_fast_read_112 {
688            jedec.param_112 = FastReadParam {
689                wait_states: p.wait_states_112()?,
690                mode_bits: p.mode_bits_112()?,
691                opcode: p.read_opcode_112()?,
692            }
693        }
694        if jedec.support_fast_read_122 {
695            jedec.param_122 = FastReadParam {
696                wait_states: p.wait_states_122()?,
697                mode_bits: p.mode_bits_122()?,
698                opcode: p.read_opcode_122()?,
699            }
700        }
701        if jedec.support_fast_read_144 {
702            jedec.param_144 = FastReadParam {
703                wait_states: p.wait_states_144()?,
704                mode_bits: p.mode_bits_144()?,
705                opcode: p.read_opcode_144()?,
706            }
707        }
708        if jedec.support_fast_read_114 {
709            jedec.param_114 = FastReadParam {
710                wait_states: p.wait_states_114()?,
711                mode_bits: p.mode_bits_114()?,
712                opcode: p.read_opcode_114()?,
713            }
714        }
715        if jedec.support_fast_read_222 {
716            jedec.param_222 = FastReadParam {
717                wait_states: p.wait_states_222()?,
718                mode_bits: p.mode_bits_222()?,
719                opcode: p.read_opcode_222()?,
720            }
721        }
722        if jedec.support_fast_read_444 {
723            jedec.param_444 = FastReadParam {
724                wait_states: p.wait_states_444()?,
725                mode_bits: p.mode_bits_444()?,
726                opcode: p.read_opcode_444()?,
727            }
728        }
729        if p.data.len() >= 16 {
730            let mult = 2 * (p.erase_time_multiplier()? + 1);
731            let type1 = Self::ERASE_TIME_UNITS[p.erase_type1_unit()?] * (p.erase_type1_time()? + 1);
732            let type2 = Self::ERASE_TIME_UNITS[p.erase_type2_unit()?] * (p.erase_type2_time()? + 1);
733            let type3 = Self::ERASE_TIME_UNITS[p.erase_type3_unit()?] * (p.erase_type3_time()? + 1);
734            let type4 = Self::ERASE_TIME_UNITS[p.erase_type4_unit()?] * (p.erase_type4_time()? + 1);
735            jedec.erase[0].time = Some(TimeBound {
736                typical: type1,
737                maximum: type1 * mult,
738            });
739            jedec.erase[1].time = Some(TimeBound {
740                typical: type2,
741                maximum: type2 * mult,
742            });
743            jedec.erase[2].time = Some(TimeBound {
744                typical: type3,
745                maximum: type3 * mult,
746            });
747            jedec.erase[3].time = Some(TimeBound {
748                typical: type4,
749                maximum: type4 * mult,
750            });
751
752            let mult = 2 * (p.pgm_time_multiplier()? + 1);
753            let chip_erase_unit = Self::CHIP_ERASE_UNITS[p.chip_erase_unit()?];
754            let page_pgm_unit = Self::PAGE_PGM_UNITS[p.page_pgm_unit()?];
755            let byte_pgm_unit = Self::BYTE_PGM_UNITS[p.byte_pgm_unit()?];
756            let additional_byte_pgm_unit = Self::BYTE_PGM_UNITS[p.additional_byte_pgm_unit()?];
757
758            jedec.rev_b = Some(JedecParamsRevB {
759                page_size: 1u32 << p.page_size()?,
760                page_program_time: TimeBound {
761                    typical: page_pgm_unit * (1 + p.page_pgm_time()?),
762                    maximum: page_pgm_unit * (1 + p.page_pgm_time()?) * mult,
763                },
764                byte_program_time: TimeBound {
765                    typical: byte_pgm_unit * (1 + p.byte_pgm_time()?),
766                    maximum: byte_pgm_unit * (1 + p.byte_pgm_time()?) * mult,
767                },
768                additional_byte_program_time: TimeBound {
769                    typical: additional_byte_pgm_unit * (1 + p.additional_byte_pgm_time()?),
770                    maximum: additional_byte_pgm_unit * (1 + p.additional_byte_pgm_time()?) * mult,
771                },
772                chip_erase_time: TimeBound {
773                    typical: chip_erase_unit * (1 + p.chip_erase_time()?),
774                    maximum: chip_erase_unit * (1 + p.chip_erase_time()?) * mult,
775                },
776
777                suspend_resume_supported: !p.suspend_resume_supported()?,
778                prohibited_ops_program_suspend: p.prohibited_pgm_suspend()?,
779                prohibited_ops_erase_suspend: p.prohibited_erase_suspend()?,
780                program_resume_to_suspend: Duration::from_millis(64)
781                    * (1 + p.pgm_resume_to_suspend()?),
782                suspend_in_progress_program_latency: Self::SUSPEND_RESUME_UNITS
783                    [p.suspend_pgm_latency_unit()?]
784                    * (1 + p.suspend_pgm_latency_time()?),
785                erase_resume_to_suspend: Duration::from_millis(64)
786                    * (1 + p.erase_resume_to_suspend()?),
787                suspend_in_progress_erase_latency: Self::SUSPEND_RESUME_UNITS
788                    [p.suspend_erase_latency_unit()?]
789                    * (1 + p.suspend_erase_latency_time()?),
790                program_resume_instruction: p.pgm_resume_instruction()?,
791                program_suspend_instruction: p.pgm_suspend_instruction()?,
792                resume_instruction: p.suspend_instruction()?,
793                suspend_instruction: p.resume_instruction()?,
794
795                deep_powerdown_supported: !p.deep_powerdown_supported()?,
796                enter_deep_powerdown_instruction: p.enter_deep_powerdown_instruction()?,
797                exit_deep_powerdown_instruction: p.exit_deep_powerdown_instruction()?,
798                exit_deep_powerdown_delay: Self::SUSPEND_RESUME_UNITS
799                    [p.exit_deep_powerdown_unit()?]
800                    * (1 + p.exit_deep_powerdown_time()?),
801                status_register_polling: p.status_busy_polling()?,
802
803                hold_or_reset_disable: p.hold_or_reset_disable()?,
804                quad_enable_requirements: p.quad_enable_requirements()?,
805                mode_444_entry: p.mode_444_entry()?,
806                mode_444_exit: p.mode_444_exit()?,
807                mode_444_supported: p.mode_444_supported()?,
808                mode_444_disable: p.mode_444_disable()?,
809                mode_444_enable: p.mode_444_enable()?,
810
811                enter_4b_addressing: p.enter_4b_addressing()?,
812                exit_4b_addressing: p.exit_4b_addressing()?,
813                soft_reset_support: p.soft_reset_support()?,
814                status_reg1_write_enable: p.status_reg1_write_enable()?,
815            });
816        }
817        if p.data.len() >= 20 {
818            jedec.rev_d = Some(JedecParamsRevD {
819                param_118: FastReadParam {
820                    wait_states: p.wait_states_118()?,
821                    mode_bits: p.mode_bits_118()?,
822                    opcode: p.read_opcode_118()?,
823                },
824                param_188: FastReadParam {
825                    wait_states: p.wait_states_188()?,
826                    mode_bits: p.mode_bits_188()?,
827                    opcode: p.read_opcode_188()?,
828                },
829
830                output_driver_strength: p.output_driver_strength()?,
831                jedec_spi_protocol_reset: p.spi_protocol_reset()?,
832                data_strobe_waveform_str: p.data_strobe_str()?,
833                data_strobe_support_4s4s4s: p.data_qpi_str()?,
834                data_strobe_support_4d4d4d: p.data_qpi_dtr()?,
835                octal_dtr_command: p.octal_dtr_command()?,
836                octal_byte_order: p.octal_byte_order()?,
837
838                mode_8s8s8s_disable: p.mode_8s8s8s_disable()?,
839                mode_8s8s8s_enable: p.mode_8s8s8s_enable()?,
840                mode_088_supported: p.mode_088_supported()?,
841                mode_088_exit: p.mode_088_exit()?,
842                mode_088_enter: p.mode_088_enter()?,
843                octal_enable_requirements: p.octal_enable_requirements()?,
844
845                max_speed_4s4s4s_no_strobe: p.max_speed_4s4s4s_no_strobe()?,
846                max_speed_4s4s4s_strobe: p.max_speed_4s4s4s_strobe()?,
847                max_speed_4d4d4d_no_strobe: p.max_speed_4d4d4d_no_strobe()?,
848                max_speed_4d4d4d_strobe: p.max_speed_4d4d4d_strobe()?,
849                max_speed_8s8s8s_no_strobe: p.max_speed_8s8s8s_no_strobe()?,
850                max_speed_8s8s8s_strobe: p.max_speed_8s8s8s_strobe()?,
851                max_speed_8d8d8d_no_strobe: p.max_speed_8d8d8d_no_strobe()?,
852                max_speed_8d8d8d_strobe: p.max_speed_8d8d8d_strobe()?,
853            });
854        }
855        if p.data.len() >= 23 {
856            let mut rev_f = JedecParamsRevF::default();
857            if p.support_fast_read_1s1d1d()? {
858                rev_f.param_1s1d1d = Some(FastReadParam {
859                    wait_states: p.wait_states_1s1d1d()?,
860                    mode_bits: p.mode_bits_1s1d1d()?,
861                    opcode: p.read_opcode_1s1d1d()?,
862                });
863            }
864            if p.support_fast_read_1s2d2d()? {
865                rev_f.param_1s2d2d = Some(FastReadParam {
866                    wait_states: p.wait_states_1s2d2d()?,
867                    mode_bits: p.mode_bits_1s2d2d()?,
868                    opcode: p.read_opcode_1s2d2d()?,
869                });
870            }
871            if p.support_fast_read_1s4d4d()? {
872                rev_f.param_1s4d4d = Some(FastReadParam {
873                    wait_states: p.wait_states_1s4d4d()?,
874                    mode_bits: p.mode_bits_1s4d4d()?,
875                    opcode: p.read_opcode_1s4d4d()?,
876                });
877            }
878            if p.support_fast_read_4s4d4d()? {
879                rev_f.param_4s4d4d = Some(FastReadParam {
880                    wait_states: p.wait_states_4s4d4d()?,
881                    mode_bits: p.mode_bits_4s4d4d()?,
882                    opcode: p.read_opcode_4s4d4d()?,
883                });
884            }
885            jedec.rev_f = Some(rev_f);
886        }
887        Ok(jedec)
888    }
889}
890
891/// An `UnknownParams` structure represents SFDP parameter tables for which
892/// we don't have a specialized parser.
893#[derive(Debug, Serialize)]
894pub struct UnknownParams {
895    pub data: Vec<u32>,
896}
897
898impl TryFrom<&[u8]> for UnknownParams {
899    type Error = Error;
900    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
901        let mut reader = std::io::Cursor::new(buf);
902        let mut data = vec![0u32; buf.len() / 4];
903        reader.read_u32_into::<LittleEndian>(&mut data)?;
904        Ok(UnknownParams { data })
905    }
906}
907
908/// The `Sfdp` structure represents the decoded SFDP table.
909#[derive(Debug, Serialize)]
910pub struct Sfdp {
911    pub header: SfdpHeader,
912    pub phdr: Vec<SfdpPhdr>,
913    pub jedec: JedecParams,
914    pub params: Vec<UnknownParams>,
915}
916
917impl Sfdp {
918    /// Given an initial SFDP buffer calculate the number of bytes needed for
919    /// the entire SFDP.
920    pub fn length_required(buf: &[u8]) -> Result<usize, Error> {
921        if buf.len() < 256 {
922            // Technically, we could read out the first 8-bytes, then
923            // figure out how many phdrs there are, read out that number
924            // times 8-byte chunks, then find the max extent of the pointed-to
925            // headers and perform the calculation in the else-arm below.
926            //
927            // We punt and read out the first 256 bytes, which is often more
928            // than enough.  In the event it isn't, we assume the first 256
929            // bytes will contain all of the phdrs (it will) and perform
930            // the max-extent calculation below.
931            Ok(256)
932        } else {
933            let header = SfdpHeader::try_from(&buf[0..8])?;
934            let mut len = 0;
935            for i in 0..=header.nph {
936                let start = (8 + i * 8) as usize;
937                let end = start + 8;
938                let phdr = SfdpPhdr::try_from(&buf[start..end])?;
939                len = std::cmp::max(len, phdr.offset + (phdr.dwords * 4) as u32);
940                log::debug!("computed sfdp len = {}", len);
941            }
942            Ok(len as usize)
943        }
944    }
945}
946
947/// Convert a byte buffer into an SFDP structure.
948impl TryFrom<&[u8]> for Sfdp {
949    type Error = anyhow::Error;
950    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
951        let header = SfdpHeader::try_from(buf.get(0..8).ok_or(Error::SliceRange(0, 8))?)?;
952        let mut phdr = Vec::new();
953        for i in 0..=header.nph {
954            let start = (8 + i * 8) as usize;
955            let end = start + 8;
956            phdr.push(SfdpPhdr::try_from(
957                buf.get(start..end).ok_or(Error::SliceRange(start, end))?,
958            )?);
959        }
960
961        let start = phdr[0].offset as usize;
962        let end = start + phdr[0].dwords as usize * 4;
963        let jedec =
964            JedecParams::try_from(buf.get(start..end).ok_or(Error::SliceRange(start, end))?)?;
965
966        let mut params = Vec::new();
967        for ph in phdr.iter().take((header.nph as usize) + 1).skip(1) {
968            let start = ph.offset as usize;
969            let end = start + ph.dwords as usize * 4;
970            params.push(UnknownParams::try_from(
971                buf.get(start..end).ok_or(Error::SliceRange(start, end))?,
972            )?);
973        }
974
975        Ok(Sfdp {
976            header,
977            phdr,
978            jedec,
979            params,
980        })
981    }
982}
983
984#[cfg(test)]
985mod test {
986    use super::*;
987    use anyhow::Result;
988    use humantime::parse_duration;
989
990    #[rustfmt::skip]
991    const SFDP_MX66L1G: &[u8; 512] = include_bytes!("SFDP_MX66L1G.bin");
992
993    #[allow(clippy::bool_assert_comparison)]
994    #[test]
995    fn test_decode_mx66l1g() -> Result<()> {
996        let sfdp = Sfdp::try_from(&SFDP_MX66L1G[..])?;
997        // A simple spot-check of values from the SFDP table.
998        assert_eq!(sfdp.header.signature, 0x50444653);
999        assert_eq!(sfdp.header.major, 1);
1000        assert_eq!(sfdp.header.minor, 6);
1001        assert_eq!(sfdp.header.nph, 2);
1002
1003        assert_eq!(sfdp.jedec.block_erase_size, BlockEraseSize::Block4KiB);
1004        assert_eq!(sfdp.jedec.write_en_required, false);
1005        assert_eq!(sfdp.jedec.write_en_opcode, 0x06);
1006        assert_eq!(sfdp.jedec.erase_opcode_4kib, 0x20);
1007        assert_eq!(sfdp.jedec.support_fast_read_112, true);
1008        assert_eq!(sfdp.jedec.address_modes, SupportedAddressModes::Mode3b4b);
1009        assert_eq!(sfdp.jedec.density, 128 * 1024 * 1024);
1010
1011        assert_eq!(sfdp.jedec.erase[0].size, 4096);
1012        assert_eq!(sfdp.jedec.erase[0].opcode, 0x20);
1013        // The datasheet says typ=30ms, max=400ms.  The SFDP-reported
1014        // erase timings are advisory, have a limited representations
1015        // and won't match the data sheet exactly.
1016        assert_eq!(
1017            sfdp.jedec.erase[0].time.as_ref().unwrap().typical,
1018            parse_duration("30ms")?
1019        );
1020        assert_eq!(
1021            sfdp.jedec.erase[0].time.as_ref().unwrap().maximum,
1022            parse_duration("420ms")?
1023        );
1024
1025        assert_eq!(sfdp.jedec.erase[1].size, 32768);
1026        assert_eq!(sfdp.jedec.erase[1].opcode, 0x52);
1027        // The datasheet says typ=0.15s, max=1s.
1028        assert_eq!(
1029            sfdp.jedec.erase[1].time.as_ref().unwrap().typical,
1030            parse_duration("160ms")?
1031        );
1032        assert_eq!(
1033            sfdp.jedec.erase[1].time.as_ref().unwrap().maximum,
1034            parse_duration("2s 240ms")?
1035        );
1036
1037        assert_eq!(sfdp.jedec.erase[2].size, 65536);
1038        assert_eq!(sfdp.jedec.erase[2].opcode, 0xd8);
1039        // The datasheet says typ=0.28s, max=2s.
1040        assert_eq!(
1041            sfdp.jedec.erase[2].time.as_ref().unwrap().typical,
1042            parse_duration("288ms")?
1043        );
1044        assert_eq!(
1045            sfdp.jedec.erase[2].time.as_ref().unwrap().maximum,
1046            parse_duration("4s 32ms")?
1047        );
1048
1049        assert_eq!(sfdp.jedec.erase[3].size, 0);
1050        assert_eq!(sfdp.jedec.erase[3].opcode, 0xff);
1051
1052        let rev_b = sfdp.jedec.rev_b.as_ref().expect("rev_b parameters");
1053        assert_eq!(rev_b.page_size, 256);
1054        // The datasheet says typ=0.25ms, max=3ms.
1055        assert_eq!(rev_b.page_program_time.typical, parse_duration("256us")?);
1056        assert_eq!(rev_b.page_program_time.maximum, parse_duration("3ms 72us")?);
1057        // The datasheet says typ=25us, max=60us.
1058        assert_eq!(rev_b.byte_program_time.typical, parse_duration("32us")?);
1059        assert_eq!(rev_b.byte_program_time.maximum, parse_duration("384us")?);
1060        // The datasheet doesn't mention the additional byte programming time.
1061        assert_eq!(
1062            rev_b.additional_byte_program_time.typical,
1063            parse_duration("1us")?
1064        );
1065        assert_eq!(
1066            rev_b.additional_byte_program_time.maximum,
1067            parse_duration("12us")?
1068        );
1069        // The datasheet says typ=200s, max=600s. The typical time is the closest
1070        // possible representation given the range and units available in the SFDP.
1071        assert_eq!(rev_b.chip_erase_time.typical, parse_duration("4m 16s")?);
1072        assert_eq!(rev_b.chip_erase_time.maximum, parse_duration("51m 12s")?);
1073
1074        // The particular MX66L1G sampled doesn't have a RevD or RevF table.
1075        assert!(sfdp.jedec.rev_d.is_none());
1076        assert!(sfdp.jedec.rev_f.is_none());
1077        Ok(())
1078    }
1079
1080    // Regression test for https://github.com/lowRISC/opentitan/issues/13477
1081    #[test]
1082    fn test_bad_header_signature() -> Result<()> {
1083        let buf = &[255u8; 256];
1084        let sfdp = Sfdp::try_from(&buf[..]);
1085        assert!(sfdp.is_err());
1086        let err = sfdp.unwrap_err();
1087        assert_eq!(
1088            err.to_string(),
1089            "SFDP header contains incorrect signature: 0xffffffff"
1090        );
1091        Ok(())
1092    }
1093}