arm_ffa/
boot_info.rs

1// SPDX-FileCopyrightText: Copyright The arm-ffa Contributors.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Implementation of the FF-A Boot information protocol.
5//!
6//! An SP or SPMC could rely on boot information for their initialization e.g. a flattened device
7//! tree with nodes to describe the devices and memory regions assigned to the SP or SPMC. FF-A
8//! specifies a protocol that can be used by a producer to pass boot information to a consumer at a
9//! Secure FF-A instance. The Framework assumes that the boot information protocol is used by a
10//! producer and consumer pair that reside at adjacent exception levels as listed below.
11//! - SPMD (producer) and an SPMC (consumer) in either S-EL1 or S-EL2.
12//! - An SPMC (producer) and SP (consumer) pair listed below.
13//!   - EL3 SPMC and a Logical S-EL1 SP.
14//!   - S-EL2 SPMC and Physical S-EL1 SP.
15//!   - EL3 SPMC and a S-EL0 SP.
16//!   - S-EL2 SPMC and a S-EL0 SP.
17//!   - S-EL1 SPMC and a S-EL0 SP.
18
19use core::ffi::CStr;
20use thiserror::Error;
21use uuid::Uuid;
22use zerocopy::{FromBytes, IntoBytes};
23
24// This module uses FF-A v1.1 types by default.
25// FF-A v1.2 didn't introduce any changes to the data stuctures used by this module.
26use crate::{
27    ffa_v1_1::{boot_info_descriptor, boot_info_header},
28    UuidHelper, Version,
29};
30
31/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
32/// with the `FFA_ERROR` interface.
33#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
34pub enum Error {
35    #[error("Invalid standard type {0}")]
36    InvalidStdType(u8),
37    #[error("Invalid type {0}")]
38    InvalidType(u8),
39    #[error("Invalid contents format {0}")]
40    InvalidContentsFormat(u16),
41    #[error("Invalid name format {0}")]
42    InvalidNameFormat(u16),
43    #[error("Invalid name")]
44    InvalidName,
45    #[error("Invalid flags {0}")]
46    InvalidFlags(u16),
47    #[error("Invalid header size or alignment")]
48    InvalidHeader,
49    #[error("Invalid buffer size")]
50    InvalidBufferSize,
51    #[error("Invalid signature")]
52    InvalidSignature,
53    #[error("Invalid version {0}")]
54    InvalidVersion(Version),
55    #[error("Malformed descriptor")]
56    MalformedDescriptor,
57}
58
59impl From<Error> for crate::FfaError {
60    fn from(_value: Error) -> Self {
61        Self::InvalidParameters
62    }
63}
64
65/// Name of boot information descriptor.
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub enum BootInfoName<'a> {
68    NullTermString(&'a CStr),
69    Uuid(Uuid),
70}
71
72/// ID for supported standard boot information types.
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74#[repr(u8)]
75pub enum BootInfoStdId {
76    Fdt = Self::FDT,
77    Hob = Self::HOB,
78}
79
80impl TryFrom<u8> for BootInfoStdId {
81    type Error = Error;
82
83    fn try_from(value: u8) -> Result<Self, Self::Error> {
84        match value {
85            Self::FDT => Ok(BootInfoStdId::Fdt),
86            Self::HOB => Ok(BootInfoStdId::Hob),
87            _ => Err(Error::InvalidStdType(value)),
88        }
89    }
90}
91
92impl BootInfoStdId {
93    const FDT: u8 = 0;
94    const HOB: u8 = 1;
95}
96
97/// ID for implementation defined boot information type.
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99pub struct BootInfoImpdefId(pub u8);
100
101impl From<u8> for BootInfoImpdefId {
102    fn from(value: u8) -> Self {
103        Self(value)
104    }
105}
106
107/// Boot information type.
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub enum BootInfoType {
110    Std(BootInfoStdId),
111    Impdef(BootInfoImpdefId),
112}
113
114impl TryFrom<u8> for BootInfoType {
115    type Error = Error;
116
117    fn try_from(value: u8) -> Result<Self, Self::Error> {
118        match (value >> Self::TYPE_SHIFT) & Self::TYPE_MASK {
119            Self::TYPE_STANDARD => Ok(BootInfoType::Std((value & Self::ID_MASK).try_into()?)),
120            Self::TYPE_IMPDEF => Ok(BootInfoType::Impdef((value & Self::ID_MASK).into())),
121            _ => Err(Error::InvalidType(value)),
122        }
123    }
124}
125
126impl From<BootInfoType> for u8 {
127    fn from(value: BootInfoType) -> Self {
128        match value {
129            BootInfoType::Std(std_type) => {
130                std_type as u8 | (BootInfoType::TYPE_STANDARD << BootInfoType::TYPE_SHIFT)
131            }
132            BootInfoType::Impdef(impdef_type) => {
133                impdef_type.0 | (BootInfoType::TYPE_IMPDEF << BootInfoType::TYPE_SHIFT)
134            }
135        }
136    }
137}
138
139impl BootInfoType {
140    // This field contains the boot info type at bit[7] and the boot info identifier in bits[6:0]
141    const TYPE_SHIFT: usize = 7;
142    const TYPE_MASK: u8 = 0b1;
143    const TYPE_STANDARD: u8 = 0b0;
144    const TYPE_IMPDEF: u8 = 0b1;
145    // Mask for boot info identifier in bits[6:0]
146    const ID_MASK: u8 = 0b0111_1111;
147}
148
149/// Boot information contents.
150#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151pub enum BootInfoContents<'a> {
152    Address { content_buf: &'a [u8] },
153    Value { val: u64, len: usize },
154}
155
156#[derive(Clone, Copy, Debug, PartialEq, Eq)]
157#[repr(u16)]
158enum BootInfoContentsFormat {
159    Address = Self::ADDRESS << Self::SHIFT,
160    Value = Self::VALUE << Self::SHIFT,
161}
162
163impl TryFrom<u16> for BootInfoContentsFormat {
164    type Error = Error;
165
166    fn try_from(value: u16) -> Result<Self, Self::Error> {
167        match (value >> Self::SHIFT) & Self::MASK {
168            Self::ADDRESS => Ok(BootInfoContentsFormat::Address),
169            Self::VALUE => Ok(BootInfoContentsFormat::Value),
170            _ => Err(Error::InvalidContentsFormat(value)),
171        }
172    }
173}
174
175impl BootInfoContentsFormat {
176    const SHIFT: usize = 2;
177    const MASK: u16 = 0b11;
178    const ADDRESS: u16 = 0b00;
179    const VALUE: u16 = 0b01;
180}
181
182#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183#[repr(u16)]
184enum BootInfoNameFormat {
185    String = Self::STRING << Self::SHIFT,
186    Uuid = Self::UUID << Self::SHIFT,
187}
188
189impl TryFrom<u16> for BootInfoNameFormat {
190    type Error = Error;
191
192    fn try_from(value: u16) -> Result<Self, Self::Error> {
193        match (value >> Self::SHIFT) & Self::MASK {
194            Self::STRING => Ok(BootInfoNameFormat::String),
195            Self::UUID => Ok(BootInfoNameFormat::Uuid),
196            _ => Err(Error::InvalidNameFormat(value)),
197        }
198    }
199}
200
201impl BootInfoNameFormat {
202    const SHIFT: usize = 0;
203    const MASK: u16 = 0b11;
204    const STRING: u16 = 0b00;
205    const UUID: u16 = 0b01;
206}
207
208#[derive(Clone, Copy, Debug, PartialEq, Eq)]
209struct BootInfoFlags {
210    contents_format: BootInfoContentsFormat,
211    name_format: BootInfoNameFormat,
212}
213
214impl TryFrom<u16> for BootInfoFlags {
215    type Error = Error;
216
217    fn try_from(value: u16) -> Result<Self, Self::Error> {
218        // bits[15:4]: Reserved (MBZ)
219        if value >> 4 == 0 {
220            Ok(Self {
221                contents_format: BootInfoContentsFormat::try_from(value)?,
222                name_format: BootInfoNameFormat::try_from(value)?,
223            })
224        } else {
225            Err(Error::InvalidFlags(value))
226        }
227    }
228}
229
230impl From<BootInfoFlags> for u16 {
231    fn from(value: BootInfoFlags) -> Self {
232        value.contents_format as u16 | value.name_format as u16
233    }
234}
235
236/// Boot information descriptor.
237#[derive(Clone, Debug, PartialEq, Eq)]
238pub struct BootInfo<'a> {
239    pub name: BootInfoName<'a>,
240    pub typ: BootInfoType,
241    pub contents: BootInfoContents<'a>,
242}
243
244impl BootInfo<'_> {
245    /// Serialize a list of boot information descriptors into a buffer. The `mapped_addr` parameter
246    /// should contain the address of the buffer in the consumers translation regime (typically a
247    /// virtual address where the buffer is mapped to). This is necessary since there are
248    /// self-references within the serialized data structure which must be described with an
249    /// absolute address according to the FF-A spec.
250    pub fn pack(
251        version: Version,
252        descriptors: &[BootInfo],
253        buf: &mut [u8],
254        mapped_addr: Option<usize>,
255    ) {
256        assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
257
258        // Offset from the base of the header to the first element in the boot info descriptor array
259        // Must be 8 byte aligned, but otherwise we're free to choose any value here.
260        // Let's just pack the array right after the header.
261        const DESC_ARRAY_OFFSET: usize = size_of::<boot_info_header>().next_multiple_of(8);
262        const DESC_SIZE: usize = size_of::<boot_info_descriptor>();
263
264        assert!(buf.len() <= u32::MAX as usize);
265
266        let desc_cnt = descriptors.len();
267
268        // Add the already known fields, later we have to add the sizes referenced by the individual
269        // descriptors
270
271        // The Size of boot information blob field specifies the size of the blob that spans one or
272        // more contiguous 4K pages used by the producer to populate it. It is calculated by adding
273        // the following values:
274        // 1. Boot information descriptor array offset
275        // 2. Product of Boot information descriptor count and Boot information descriptor size.
276        // 3. Total size of all boot information referenced by boot information descriptors.
277        //    This is determined by adding the values in the Size field of each boot information
278        //    descriptor whose Contents field contains an address.
279        // 4. Any padding between,
280        //    1. The boot information descriptor array and the boot information referenced from it.
281        //    2. Distinct instances of boot information referenced from the boot information
282        //       descriptor array.
283        let mut total_offset = 0usize;
284
285        // No. 1 from the "Size of boot information blob" list
286        total_offset = total_offset.checked_add(DESC_ARRAY_OFFSET).unwrap();
287
288        // No. 2 from the "Size of boot information blob" list
289        total_offset = total_offset
290            .checked_add(desc_cnt.checked_mul(DESC_SIZE).unwrap())
291            .unwrap();
292
293        // Fail early if the buffer is too small
294        assert!(total_offset <= buf.len());
295
296        // Fill the boot info descriptor array, all offset based from DESC_ARRAY_OFFSET
297        let mut desc_array_offset = DESC_ARRAY_OFFSET;
298
299        for desc in descriptors {
300            let mut desc_raw = boot_info_descriptor::default();
301
302            let name_format = match &desc.name {
303                BootInfoName::NullTermString(name) => {
304                    // count_bytes() doesn't include nul terminator
305                    let name_len = name.count_bytes().min(15);
306                    desc_raw.name[..name_len].copy_from_slice(&name.to_bytes()[..name_len]);
307                    // Add nul terminator and zero fill the rest
308                    desc_raw.name[name_len..].fill(0);
309
310                    BootInfoNameFormat::String
311                }
312                BootInfoName::Uuid(uuid) => {
313                    desc_raw.name.copy_from_slice(&UuidHelper::to_bytes(*uuid));
314                    BootInfoNameFormat::Uuid
315                }
316            };
317
318            let contents_format = match desc.contents {
319                BootInfoContents::Address { content_buf } => {
320                    // We have to copy the contents referenced by the boot info descriptor into the
321                    // boot info blob. At this offset we're after the boot info header and all of
322                    // the boot info descriptors. The contents referenced from the individual boot
323                    // info descriptors will get copied to this starting address. The 8 byte
324                    // alignment is not explicitly mentioned by the spec, but it's better to have it
325                    // anyway.
326                    // No. 4 from the "Size of boot information blob" list
327                    total_offset = total_offset.next_multiple_of(8);
328
329                    // The mapped_addr argument contains the address where buf is mapped to in the
330                    // consumer's translation regime. If it's None, we assume identity mapping is
331                    // used, so the buffer's address stays the same.
332                    let buf_addr = mapped_addr.unwrap_or(buf.as_ptr() as usize);
333
334                    // The content's address in the consumer's translation regime will be the
335                    // buffer's address in the consumer's translation regime plus the offset of the
336                    // content within the boot info blob.
337                    let content_addr = buf_addr.checked_add(total_offset).unwrap();
338
339                    // Check if the content fits before copying
340                    let content_len = content_buf.len();
341                    total_offset.checked_add(content_len).unwrap();
342
343                    // Do the copy and increase the total size
344                    // No. 3 from the "Size of boot information blob" list
345                    buf[total_offset..total_offset + content_len].copy_from_slice(content_buf);
346                    total_offset += content_len;
347
348                    desc_raw.contents = content_addr as u64;
349                    desc_raw.size = content_len as u32;
350
351                    BootInfoContentsFormat::Address
352                }
353                BootInfoContents::Value { val, len } => {
354                    assert!((1..=8).contains(&len));
355                    desc_raw.contents = val;
356                    desc_raw.size = len as u32;
357
358                    BootInfoContentsFormat::Value
359                }
360            };
361
362            let flags = BootInfoFlags {
363                contents_format,
364                name_format,
365            };
366
367            desc_raw.flags = flags.into();
368            desc_raw.typ = desc.typ.into();
369
370            desc_raw
371                .write_to_prefix(&mut buf[desc_array_offset..])
372                .unwrap();
373            desc_array_offset += DESC_SIZE;
374        }
375
376        let header_raw = boot_info_header {
377            signature: 0x0ffa,
378            version: version.into(),
379            boot_info_blob_size: total_offset as u32,
380            boot_info_desc_size: DESC_SIZE as u32,
381            boot_info_desc_count: desc_cnt as u32,
382            boot_info_array_offset: DESC_ARRAY_OFFSET as u32,
383            reserved: 0,
384        };
385
386        header_raw.write_to_prefix(buf).unwrap();
387    }
388
389    /// Validate and return the boot information header
390    fn get_header(version: Version, buf: &[u8]) -> Result<&boot_info_header, Error> {
391        let (header_raw, _) =
392            boot_info_header::ref_from_prefix(buf).map_err(|_| Error::InvalidHeader)?;
393
394        if header_raw.signature != 0x0ffa {
395            return Err(Error::InvalidSignature);
396        }
397
398        let header_version = header_raw
399            .version
400            .try_into()
401            .map_err(|_| Error::InvalidHeader)?;
402        if header_version != version {
403            return Err(Error::InvalidVersion(header_version));
404        }
405
406        Ok(header_raw)
407    }
408
409    /// Get the size of the boot information blob spanning contiguous memory. This enables a
410    /// consumer to map all of the boot information blob in its translation regime or copy it to
411    /// another memory location without parsing each element in the boot information descriptor
412    /// array.
413    pub fn get_blob_size(version: Version, buf: &[u8]) -> Result<usize, Error> {
414        if !(Version(1, 1)..=Version(1, 2)).contains(&version) {
415            return Err(Error::InvalidVersion(version));
416        }
417
418        let header_raw = Self::get_header(version, buf)?;
419
420        Ok(header_raw.boot_info_blob_size as usize)
421    }
422}
423
424/// Iterator of boot information descriptors.
425pub struct BootInfoIterator<'a> {
426    buf: &'a [u8],
427    offset: usize,
428    desc_count: usize,
429    desc_size: usize,
430}
431
432impl<'a> BootInfoIterator<'a> {
433    /// Create an iterator of boot information descriptors from a buffer.
434    pub fn new(version: Version, buf: &'a [u8]) -> Result<Self, Error> {
435        let header_raw = BootInfo::get_header(version, buf)?;
436
437        if buf.len() < header_raw.boot_info_blob_size as usize {
438            return Err(Error::InvalidBufferSize);
439        }
440
441        if header_raw.boot_info_desc_size as usize != size_of::<boot_info_descriptor>() {
442            return Err(Error::MalformedDescriptor);
443        }
444
445        let Some(total_desc_size) = header_raw
446            .boot_info_desc_count
447            .checked_mul(header_raw.boot_info_desc_size)
448            .and_then(|x| x.checked_add(header_raw.boot_info_array_offset))
449        else {
450            return Err(Error::InvalidBufferSize);
451        };
452
453        if buf.len() < total_desc_size as usize {
454            return Err(Error::InvalidBufferSize);
455        }
456
457        Ok(Self {
458            buf,
459            offset: header_raw.boot_info_array_offset as usize,
460            desc_count: header_raw.boot_info_desc_count as usize,
461            desc_size: header_raw.boot_info_desc_size as usize,
462        })
463    }
464}
465
466impl<'a> Iterator for BootInfoIterator<'a> {
467    type Item = Result<BootInfo<'a>, Error>;
468
469    fn next(&mut self) -> Option<Self::Item> {
470        if self.desc_count > 0 {
471            let desc_offset = self.offset;
472            self.offset += self.desc_size;
473            self.desc_count -= 1;
474
475            let Ok(desc_raw) = boot_info_descriptor::ref_from_bytes(
476                &self.buf[desc_offset..desc_offset + self.desc_size],
477            ) else {
478                return Some(Err(Error::MalformedDescriptor));
479            };
480
481            if desc_raw.reserved != 0 {
482                return Some(Err(Error::MalformedDescriptor));
483            }
484
485            let typ: BootInfoType = match desc_raw.typ.try_into() {
486                Ok(v) => v,
487                Err(e) => return Some(Err(e)),
488            };
489
490            let flags: BootInfoFlags = match desc_raw.flags.try_into() {
491                Ok(v) => v,
492                Err(e) => return Some(Err(e)),
493            };
494
495            let name = match flags.name_format {
496                BootInfoNameFormat::String => {
497                    let Ok(name_str) = CStr::from_bytes_until_nul(desc_raw.name.as_bytes()) else {
498                        return Some(Err(Error::InvalidName));
499                    };
500                    BootInfoName::NullTermString(name_str)
501                }
502                BootInfoNameFormat::Uuid => {
503                    BootInfoName::Uuid(UuidHelper::from_bytes(desc_raw.name))
504                }
505            };
506
507            let contents = match flags.contents_format {
508                BootInfoContentsFormat::Address => {
509                    let contents = desc_raw.contents as usize;
510                    let contents_size = desc_raw.size as usize;
511
512                    let Some(offset) = contents.checked_sub(self.buf.as_ptr() as usize) else {
513                        return Some(Err(Error::InvalidBufferSize));
514                    };
515
516                    let Some(offset_end) = offset.checked_add(contents_size) else {
517                        return Some(Err(Error::InvalidBufferSize));
518                    };
519
520                    if self.buf.len() < offset_end {
521                        return Some(Err(Error::InvalidBufferSize));
522                    }
523
524                    BootInfoContents::Address {
525                        content_buf: &self.buf[offset..offset_end],
526                    }
527                }
528
529                BootInfoContentsFormat::Value => {
530                    let len = desc_raw.size as usize;
531                    if (1..=8).contains(&len) {
532                        BootInfoContents::Value {
533                            val: desc_raw.contents,
534                            len,
535                        }
536                    } else {
537                        return Some(Err(Error::MalformedDescriptor));
538                    }
539                }
540            };
541
542            return Some(Ok(BootInfo {
543                name,
544                typ,
545                contents,
546            }));
547        }
548
549        None
550    }
551}
552
553#[cfg(test)]
554mod tests {
555    use super::*;
556    use uuid::uuid;
557
558    #[test]
559    fn boot_info() {
560        let desc1 = BootInfo {
561            name: BootInfoName::NullTermString(c"test1234test123"),
562            typ: BootInfoType::Impdef(BootInfoImpdefId(0x2b)),
563            contents: BootInfoContents::Value {
564                val: 0xdeadbeef,
565                len: 4,
566            },
567        };
568
569        let fdt = [0u8; 0xff];
570        let desc2 = BootInfo {
571            name: BootInfoName::Uuid(uuid!("12345678-abcd-dcba-1234-123456789abc")),
572            typ: BootInfoType::Std(BootInfoStdId::Fdt),
573            contents: BootInfoContents::Address { content_buf: &fdt },
574        };
575
576        let mut buf = [0u8; 0x1ff];
577        let buf_addr = buf.as_ptr() as usize;
578        BootInfo::pack(
579            Version(1, 1),
580            &[desc1.clone(), desc2.clone()],
581            &mut buf,
582            Some(buf_addr),
583        );
584        let mut descriptors = BootInfoIterator::new(Version(1, 1), &buf).unwrap();
585        let desc1_check = descriptors.next().unwrap().unwrap();
586        let desc2_check = descriptors.next().unwrap().unwrap();
587
588        assert_eq!(desc1, desc1_check);
589        assert_eq!(desc2, desc2_check);
590
591        assert_eq!(BootInfo::get_blob_size(Version(1, 1), &buf), Ok(351));
592
593        let fa = (buf.as_ptr() as u64 + 96).to_le_bytes();
594
595        let expected = [
596            // Header
597            0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
598            0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599            0x00, 0x00, 0x00, 0x00, // End of Header
600            // Desc1
601            0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32,
602            0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde,
603            0x00, 0x00, 0x00, 0x00, // End of Desc1
604            // Desc2
605            0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xdc, 0xba, 0x12, 0x34, 0x12, 0x34, 0x56, 0x78,
606            0x9a, 0xbc, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, fa[0], fa[1], fa[2], fa[3],
607            fa[4], fa[5], fa[6], fa[7], // End of Desc2
608        ];
609
610        assert_eq!(expected, buf[0..expected.len()]);
611    }
612
613    #[test]
614    pub fn get_blob_size_invalid_version() {
615        let buf = [0; 0x100];
616
617        assert_eq!(
618            BootInfo::get_blob_size(Version(0, 1), &buf),
619            Err(Error::InvalidVersion(Version(0, 1)))
620        );
621        assert_eq!(
622            BootInfo::get_blob_size(Version(1, 0), &buf),
623            Err(Error::InvalidVersion(Version(1, 0)))
624        );
625        assert_eq!(
626            BootInfo::get_blob_size(Version(2, 1), &buf),
627            Err(Error::InvalidVersion(Version(2, 1)))
628        );
629        assert_eq!(
630            BootInfo::get_blob_size(Version(2, 2), &buf),
631            Err(Error::InvalidVersion(Version(2, 2)))
632        );
633    }
634
635    #[test]
636    pub fn boot_info_iter_new_err1() {
637        assert!(BootInfoIterator::new(Version(1, 1), &[0; 4]).is_err());
638    }
639
640    #[test]
641    pub fn boot_info_iter_new_err2() {
642        // Empty boot info.
643        assert!(BootInfoIterator::new(
644            Version(1, 1),
645            &[
646                0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
647                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
648                0x00, 0x00, 0x00, 0x00
649            ]
650        )
651        .is_err());
652    }
653
654    #[test]
655    pub fn boot_info_iter_new_err3() {
656        // Indicates two entries but array is one.
657        assert!(BootInfoIterator::new(
658            Version(1, 1),
659            &[
660                0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
661                0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
662                0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65,
663                0x73, 0x74, 0x31, 0x32, 0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
664                0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
665            ]
666        )
667        .is_err());
668    }
669
670    #[test]
671    pub fn boot_info_iter_new_err4() {
672        // Invalid entry size.
673        assert!(BootInfoIterator::new(
674            Version(1, 1),
675            &[
676                0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x10, 0x00,
677                0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
678                0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65,
679                0x73, 0x74, 0x31, 0x32, 0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
680                0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
681            ]
682        )
683        .is_err());
684    }
685
686    #[test]
687    pub fn boot_info_iter_new_err5() {
688        // Array offset out of range.
689        assert!(BootInfoIterator::new(
690            Version(1, 1),
691            &[
692                0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
693                0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
694                0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65,
695                0x73, 0x74, 0x31, 0x32, 0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
696                0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
697            ]
698        )
699        .is_err());
700    }
701}