Skip to main content

tock_tbf/
types.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Types and Data Structures for TBFs.
6
7use core::fmt;
8use core::mem::size_of;
9
10/// We only support up to a fixed number of storage permissions for each of read
11/// and modify. This simplification enables us to use fixed sized buffers.
12const NUM_STORAGE_PERMISSIONS: usize = 8;
13
14/// Error when parsing just the beginning of the TBF header. This is only used
15/// when establishing the linked list structure of apps installed in flash.
16pub enum InitialTbfParseError {
17    /// We were unable to parse the beginning of the header. This either means
18    /// we ran out of flash, or the trusted values are invalid meaning this is
19    /// just empty flash after the end of the last app. This error is fine, as
20    /// it just means we must have hit the end of the linked list of apps.
21    UnableToParse,
22
23    /// Some length or value in the header is invalid. The header parsing has
24    /// failed at this point. However, the total app length value is a trusted
25    /// field, so we return that value with this error so that we can skip over
26    /// this invalid app and continue to check for additional apps.
27    InvalidHeader(u32),
28}
29
30impl From<core::array::TryFromSliceError> for InitialTbfParseError {
31    // Convert a slice to a parsed type. Since we control how long we make our
32    // slices, this conversion should never fail. If it does, then this is a bug
33    // in this library that must be fixed.
34    fn from(_error: core::array::TryFromSliceError) -> Self {
35        InitialTbfParseError::UnableToParse
36    }
37}
38
39/// Error when parsing an app's TBF header.
40pub enum TbfParseError {
41    /// Not enough bytes in the buffer to parse the expected field.
42    NotEnoughFlash,
43
44    /// Unknown version of the TBF header.
45    UnsupportedVersion(u16),
46
47    /// Checksum calculation did not match what is stored in the TBF header.
48    /// First value is the checksum provided, second value is the checksum we
49    /// calculated.
50    ChecksumMismatch(u32, u32),
51
52    /// One of the TLV entries did not parse correctly. This could happen if the
53    /// TLV.length does not match the size of a fixed-length entry. The `usize`
54    /// is the value of the "tipe" field.
55    BadTlvEntry(usize),
56
57    /// The app name in the TBF header could not be successfully parsed as a
58    /// UTF-8 string.
59    BadProcessName,
60
61    /// Internal kernel error. This is a bug inside of this library. Likely this
62    /// means that for some reason a slice was not sized properly for parsing a
63    /// certain type, which is something completely controlled by this library.
64    /// If the slice passed in is not long enough, then a `get()` call will
65    /// fail and that will trigger a different error.
66    InternalError,
67
68    /// The number of variable length entries (for example the number of
69    /// `TbfHeaderDriverPermission` entries in `TbfHeaderV2Permissions`) is
70    /// too long for Tock to parse.
71    /// This can be fixed by increasing the number in `TbfHeaderV2`.
72    TooManyEntries(usize),
73}
74
75impl From<core::array::TryFromSliceError> for TbfParseError {
76    // Convert a slice to a parsed type. Since we control how long we make our
77    // slices, this conversion should never fail. If it does, then this is a bug
78    // in this library that must be fixed.
79    fn from(_error: core::array::TryFromSliceError) -> Self {
80        TbfParseError::InternalError
81    }
82}
83
84impl fmt::Debug for TbfParseError {
85    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86        match self {
87            TbfParseError::NotEnoughFlash => write!(f, "Buffer too short to parse TBF header"),
88            TbfParseError::UnsupportedVersion(version) => {
89                write!(f, "TBF version {} unsupported", version)
90            }
91            TbfParseError::ChecksumMismatch(app, calc) => write!(
92                f,
93                "Checksum verification failed: app:{:#x}, calc:{:#x}",
94                app, calc
95            ),
96            TbfParseError::BadTlvEntry(tipe) => write!(f, "TLV entry type {} is invalid", tipe),
97            TbfParseError::BadProcessName => write!(f, "Process name not UTF-8"),
98            TbfParseError::InternalError => write!(f, "Internal kernel error. This is a bug."),
99            TbfParseError::TooManyEntries(tipe) => {
100                write!(
101                    f,
102                    "There are too many variable entries of {} for Tock to parse",
103                    tipe
104                )
105            }
106        }
107    }
108}
109
110// TBF structure
111
112/// Size in bytes of [`TbfHeaderV2Base`], the minimum valid v2 TBF header.
113pub const TBF_HEADER_V2_BASE_SIZE: usize = 16;
114
115/// TBF fields that must be present in all v2 headers.
116#[derive(Clone, Copy, Debug)]
117pub struct TbfHeaderV2Base {
118    pub(crate) version: u16,
119    pub(crate) header_size: u16,
120    pub(crate) total_size: u32,
121    pub(crate) flags: u32,
122    pub(crate) checksum: u32,
123}
124
125/// Types in TLV structures for each optional block of the header.
126#[derive(Clone, Copy, Debug)]
127pub enum TbfHeaderTypes {
128    TbfHeaderMain = 1,
129    TbfHeaderWriteableFlashRegions = 2,
130    TbfHeaderPackageName = 3,
131    TbfHeaderFixedAddresses = 5,
132    TbfHeaderPermissions = 6,
133    TbfHeaderStoragePermissions = 7,
134    TbfHeaderKernelVersion = 8,
135    TbfHeaderProgram = 9,
136    TbfHeaderShortId = 10,
137    TbfFooterCredentials = 128,
138
139    /// Some field in the header that we do not understand. Since the TLV format
140    /// specifies the length of each section, if we get a field we do not
141    /// understand we just skip it, rather than throwing an error.
142    Unknown,
143}
144
145/// The TLV header (T and L).
146#[derive(Clone, Copy, Debug)]
147pub struct TbfTlv {
148    pub(crate) tipe: TbfHeaderTypes,
149    pub(crate) length: u16,
150}
151
152/// The v2 Main Header for apps.
153///
154/// All apps must have either a Main Header or a Program Header. Without
155/// either, the TBF object is considered padding. Main and Program Headers
156/// differ in whether they specify the endpoint of the process binary; Main
157/// Headers do not, while Program Headers do. A TBF with a Main Header cannot
158/// have any Credentials Footers, while a TBF with a Program Header can.
159#[derive(Clone, Copy, Debug)]
160pub struct TbfHeaderV2Main {
161    init_fn_offset: u32,
162    protected_trailer_size: u32,
163    minimum_ram_size: u32,
164}
165
166/// The v2 Program Header for apps.
167///
168/// All apps must have either a Main Header or a Program Header. Without
169/// either, the TBF object is considered padding. Main and Program Headers
170/// differ in whether they specify the endpoint of the process binary; Main
171/// Headers do not, while Program Headers do. A Program Header includes
172/// the binary end offset so that a Verifier knows where Credentials Headers
173/// start. The region between the end of the binary and the end of the TBF
174/// is reserved for Credentials Footers.
175#[derive(Clone, Copy, Debug)]
176pub struct TbfHeaderV2Program {
177    init_fn_offset: u32,
178    protected_trailer_size: u32,
179    minimum_ram_size: u32,
180    binary_end_offset: u32,
181    version: u32,
182}
183
184/// Writeable flash regions only need an offset and size.
185///
186/// There can be multiple (or zero) flash regions defined, so this is its own
187/// struct.
188#[derive(Clone, Copy, Debug, Default)]
189pub struct TbfHeaderV2WriteableFlashRegion {
190    writeable_flash_region_offset: u32,
191    writeable_flash_region_size: u32,
192}
193
194/// Optional fixed addresses for flash and RAM for this process.
195///
196/// If a process is compiled for a specific address this header entry lets the
197/// kernel know what those addresses are.
198///
199/// If this header is omitted the kernel will assume that the process is
200/// position-independent and can be loaded at any (reasonably aligned) flash
201/// address and can be given any (reasonable aligned) memory segment.
202///
203/// If this header is included, the kernel will check these values when setting
204/// up the process. If a process wants to set one fixed address but not the other, the unused one
205/// can be set to 0xFFFFFFFF.
206#[derive(Clone, Copy, Debug, Default)]
207pub struct TbfHeaderV2FixedAddresses {
208    /// The absolute address of the start of RAM that the process expects. For
209    /// example, if the process was linked with a RAM region starting at
210    /// address `0x00023000`, then this would be set to `0x00023000`.
211    start_process_ram: u32,
212    /// The absolute address of the start of the process binary. This does _not_
213    /// include the TBF header. This is the address the process used for the
214    /// start of flash with the linker.
215    start_process_flash: u32,
216}
217
218#[derive(Clone, Copy, Debug, Default)]
219struct TbfHeaderDriverPermission {
220    driver_number: u32,
221    offset: u32,
222    allowed_commands: u64,
223}
224
225/// A list of permissions for this app
226#[derive(Clone, Copy, Debug)]
227pub struct TbfHeaderV2Permissions<const L: usize> {
228    length: u16,
229    perms: [TbfHeaderDriverPermission; L],
230}
231
232/// A list of storage (read/write/modify) permissions for this app.
233#[derive(Clone, Copy, Debug)]
234pub struct TbfHeaderV2StoragePermissions<const L: usize> {
235    write_id: Option<core::num::NonZeroU32>,
236    read_length: u16,
237    read_ids: [u32; L],
238    modify_length: u16,
239    modify_ids: [u32; L],
240}
241
242#[derive(Clone, Copy, Debug)]
243pub struct TbfHeaderV2KernelVersion {
244    major: u16,
245    minor: u16,
246}
247
248/// The v2 ShortId for apps.
249///
250/// Header to specify a fixed ShortID for an app.
251#[derive(Clone, Copy, Debug)]
252pub struct TbfHeaderV2ShortId {
253    short_id: Option<core::num::NonZeroU32>,
254}
255
256#[derive(Clone, Copy, Debug, Eq, PartialEq)]
257pub enum TbfFooterV2CredentialsType {
258    Reserved = 0,
259    Rsa3072Key = 1,
260    Rsa4096Key = 2,
261    SHA256 = 3,
262    SHA384 = 4,
263    SHA512 = 5,
264    EcdsaNistP256 = 6,
265}
266
267#[derive(Clone, Copy, Debug)]
268pub struct TbfFooterV2Credentials {
269    format: TbfFooterV2CredentialsType,
270    data: &'static [u8],
271}
272
273impl TbfFooterV2Credentials {
274    pub fn format(&self) -> TbfFooterV2CredentialsType {
275        self.format
276    }
277
278    pub fn data(&self) -> &'static [u8] {
279        self.data
280    }
281}
282
283// Conversion functions from slices to the various TBF fields.
284
285impl core::convert::TryFrom<&[u8]> for TbfHeaderV2Base {
286    type Error = TbfParseError;
287
288    fn try_from(b: &[u8]) -> Result<TbfHeaderV2Base, Self::Error> {
289        if b.len() < 16 {
290            return Err(TbfParseError::InternalError);
291        }
292        Ok(TbfHeaderV2Base {
293            version: u16::from_le_bytes(
294                b.get(0..2)
295                    .ok_or(TbfParseError::InternalError)?
296                    .try_into()?,
297            ),
298            header_size: u16::from_le_bytes(
299                b.get(2..4)
300                    .ok_or(TbfParseError::InternalError)?
301                    .try_into()?,
302            ),
303            total_size: u32::from_le_bytes(
304                b.get(4..8)
305                    .ok_or(TbfParseError::InternalError)?
306                    .try_into()?,
307            ),
308            flags: u32::from_le_bytes(
309                b.get(8..12)
310                    .ok_or(TbfParseError::InternalError)?
311                    .try_into()?,
312            ),
313            checksum: u32::from_le_bytes(
314                b.get(12..16)
315                    .ok_or(TbfParseError::InternalError)?
316                    .try_into()?,
317            ),
318        })
319    }
320}
321
322impl core::convert::TryFrom<u16> for TbfHeaderTypes {
323    type Error = TbfParseError;
324
325    fn try_from(h: u16) -> Result<TbfHeaderTypes, Self::Error> {
326        match h {
327            1 => Ok(TbfHeaderTypes::TbfHeaderMain),
328            2 => Ok(TbfHeaderTypes::TbfHeaderWriteableFlashRegions),
329            3 => Ok(TbfHeaderTypes::TbfHeaderPackageName),
330            5 => Ok(TbfHeaderTypes::TbfHeaderFixedAddresses),
331            6 => Ok(TbfHeaderTypes::TbfHeaderPermissions),
332            7 => Ok(TbfHeaderTypes::TbfHeaderStoragePermissions),
333            8 => Ok(TbfHeaderTypes::TbfHeaderKernelVersion),
334            9 => Ok(TbfHeaderTypes::TbfHeaderProgram),
335            10 => Ok(TbfHeaderTypes::TbfHeaderShortId),
336            128 => Ok(TbfHeaderTypes::TbfFooterCredentials),
337            _ => Ok(TbfHeaderTypes::Unknown),
338        }
339    }
340}
341
342impl core::convert::TryFrom<&[u8]> for TbfTlv {
343    type Error = TbfParseError;
344
345    fn try_from(b: &[u8]) -> Result<TbfTlv, Self::Error> {
346        Ok(TbfTlv {
347            tipe: u16::from_le_bytes(
348                b.get(0..2)
349                    .ok_or(TbfParseError::InternalError)?
350                    .try_into()?,
351            )
352            .try_into()?,
353            length: u16::from_le_bytes(
354                b.get(2..4)
355                    .ok_or(TbfParseError::InternalError)?
356                    .try_into()?,
357            ),
358        })
359    }
360}
361
362impl core::convert::TryFrom<&[u8]> for TbfHeaderV2Main {
363    type Error = TbfParseError;
364
365    fn try_from(b: &[u8]) -> Result<TbfHeaderV2Main, Self::Error> {
366        // For 3 or more fields, this shortcut check reduces code size
367        if b.len() < 12 {
368            return Err(TbfParseError::InternalError);
369        }
370        Ok(TbfHeaderV2Main {
371            init_fn_offset: u32::from_le_bytes(
372                b.get(0..4)
373                    .ok_or(TbfParseError::InternalError)?
374                    .try_into()?,
375            ),
376            protected_trailer_size: u32::from_le_bytes(
377                b.get(4..8)
378                    .ok_or(TbfParseError::InternalError)?
379                    .try_into()?,
380            ),
381            minimum_ram_size: u32::from_le_bytes(
382                b.get(8..12)
383                    .ok_or(TbfParseError::InternalError)?
384                    .try_into()?,
385            ),
386        })
387    }
388}
389
390impl core::convert::TryFrom<&[u8]> for TbfHeaderV2Program {
391    type Error = TbfParseError;
392    fn try_from(b: &[u8]) -> Result<TbfHeaderV2Program, Self::Error> {
393        // For 3 or more fields, this shortcut check reduces code size
394        if b.len() < 20 {
395            return Err(TbfParseError::InternalError);
396        }
397        Ok(TbfHeaderV2Program {
398            init_fn_offset: u32::from_le_bytes(
399                b.get(0..4)
400                    .ok_or(TbfParseError::InternalError)?
401                    .try_into()?,
402            ),
403            protected_trailer_size: u32::from_le_bytes(
404                b.get(4..8)
405                    .ok_or(TbfParseError::InternalError)?
406                    .try_into()?,
407            ),
408            minimum_ram_size: u32::from_le_bytes(
409                b.get(8..12)
410                    .ok_or(TbfParseError::InternalError)?
411                    .try_into()?,
412            ),
413            binary_end_offset: u32::from_le_bytes(
414                b.get(12..16)
415                    .ok_or(TbfParseError::InternalError)?
416                    .try_into()?,
417            ),
418            version: u32::from_le_bytes(
419                b.get(16..20)
420                    .ok_or(TbfParseError::InternalError)?
421                    .try_into()?,
422            ),
423        })
424    }
425}
426
427impl core::convert::TryFrom<&[u8]> for TbfHeaderV2WriteableFlashRegion {
428    type Error = TbfParseError;
429
430    fn try_from(b: &[u8]) -> Result<TbfHeaderV2WriteableFlashRegion, Self::Error> {
431        Ok(TbfHeaderV2WriteableFlashRegion {
432            writeable_flash_region_offset: u32::from_le_bytes(
433                b.get(0..4)
434                    .ok_or(TbfParseError::InternalError)?
435                    .try_into()?,
436            ),
437            writeable_flash_region_size: u32::from_le_bytes(
438                b.get(4..8)
439                    .ok_or(TbfParseError::InternalError)?
440                    .try_into()?,
441            ),
442        })
443    }
444}
445
446impl core::convert::TryFrom<&[u8]> for TbfHeaderV2FixedAddresses {
447    type Error = TbfParseError;
448
449    fn try_from(b: &[u8]) -> Result<TbfHeaderV2FixedAddresses, Self::Error> {
450        Ok(TbfHeaderV2FixedAddresses {
451            start_process_ram: u32::from_le_bytes(
452                b.get(0..4)
453                    .ok_or(TbfParseError::InternalError)?
454                    .try_into()?,
455            ),
456            start_process_flash: u32::from_le_bytes(
457                b.get(4..8)
458                    .ok_or(TbfParseError::InternalError)?
459                    .try_into()?,
460            ),
461        })
462    }
463}
464
465impl core::convert::TryFrom<&[u8]> for TbfHeaderDriverPermission {
466    type Error = TbfParseError;
467
468    fn try_from(b: &[u8]) -> Result<TbfHeaderDriverPermission, Self::Error> {
469        // For 3 or more fields, this shortcut check reduces code size
470        if b.len() < 16 {
471            return Err(TbfParseError::InternalError);
472        }
473        Ok(TbfHeaderDriverPermission {
474            driver_number: u32::from_le_bytes(
475                b.get(0..4)
476                    .ok_or(TbfParseError::InternalError)?
477                    .try_into()?,
478            ),
479            offset: u32::from_le_bytes(
480                b.get(4..8)
481                    .ok_or(TbfParseError::InternalError)?
482                    .try_into()?,
483            ),
484            allowed_commands: u64::from_le_bytes(
485                b.get(8..16)
486                    .ok_or(TbfParseError::InternalError)?
487                    .try_into()?,
488            ),
489        })
490    }
491}
492
493impl<const L: usize> core::convert::TryFrom<&[u8]> for TbfHeaderV2StoragePermissions<L> {
494    type Error = TbfParseError;
495
496    fn try_from(b: &[u8]) -> Result<TbfHeaderV2StoragePermissions<L>, Self::Error> {
497        let mut read_end = 6;
498
499        let write_id = core::num::NonZeroU32::new(u32::from_le_bytes(
500            b.get(0..4)
501                .ok_or(TbfParseError::NotEnoughFlash)?
502                .try_into()?,
503        ));
504
505        let read_length = u16::from_le_bytes(
506            b.get(4..6)
507                .ok_or(TbfParseError::NotEnoughFlash)?
508                .try_into()?,
509        );
510
511        let mut read_ids: [u32; L] = [0; L];
512        for i in 0..read_length as usize {
513            let start = 6 + (i * size_of::<u32>());
514            read_end = start + size_of::<u32>();
515            if let Some(read_id) = read_ids.get_mut(i) {
516                *read_id = u32::from_le_bytes(
517                    b.get(start..read_end)
518                        .ok_or(TbfParseError::NotEnoughFlash)?
519                        .try_into()?,
520                );
521            } else {
522                return Err(TbfParseError::BadTlvEntry(
523                    TbfHeaderTypes::TbfHeaderStoragePermissions as usize,
524                ));
525            }
526        }
527
528        let modify_length = u16::from_le_bytes(
529            b.get(read_end..(read_end + 2))
530                .ok_or(TbfParseError::NotEnoughFlash)?
531                .try_into()?,
532        );
533
534        let mut modify_ids: [u32; L] = [0; L];
535        for i in 0..modify_length as usize {
536            let start = read_end + 2 + (i * size_of::<u32>());
537            let modify_end = start + size_of::<u32>();
538            if let Some(modify_id) = modify_ids.get_mut(i) {
539                *modify_id = u32::from_le_bytes(
540                    b.get(start..modify_end)
541                        .ok_or(TbfParseError::NotEnoughFlash)?
542                        .try_into()?,
543                );
544            } else {
545                return Err(TbfParseError::BadTlvEntry(
546                    TbfHeaderTypes::TbfHeaderStoragePermissions as usize,
547                ));
548            }
549        }
550
551        Ok(TbfHeaderV2StoragePermissions {
552            write_id,
553            read_length,
554            read_ids,
555            modify_length,
556            modify_ids,
557        })
558    }
559}
560
561impl core::convert::TryFrom<&[u8]> for TbfHeaderV2KernelVersion {
562    type Error = TbfParseError;
563
564    fn try_from(b: &[u8]) -> Result<TbfHeaderV2KernelVersion, Self::Error> {
565        Ok(TbfHeaderV2KernelVersion {
566            major: u16::from_le_bytes(
567                b.get(0..2)
568                    .ok_or(TbfParseError::InternalError)?
569                    .try_into()?,
570            ),
571            minor: u16::from_le_bytes(
572                b.get(2..4)
573                    .ok_or(TbfParseError::InternalError)?
574                    .try_into()?,
575            ),
576        })
577    }
578}
579
580impl core::convert::TryFrom<&[u8]> for TbfHeaderV2ShortId {
581    type Error = TbfParseError;
582
583    fn try_from(b: &[u8]) -> Result<TbfHeaderV2ShortId, Self::Error> {
584        Ok(TbfHeaderV2ShortId {
585            short_id: core::num::NonZeroU32::new(u32::from_le_bytes(
586                b.get(0..4)
587                    .ok_or(TbfParseError::InternalError)?
588                    .try_into()?,
589            )),
590        })
591    }
592}
593
594impl core::convert::TryFrom<&'static [u8]> for TbfFooterV2Credentials {
595    type Error = TbfParseError;
596
597    fn try_from(b: &'static [u8]) -> Result<TbfFooterV2Credentials, Self::Error> {
598        let format = u32::from_le_bytes(
599            b.get(0..4)
600                .ok_or(TbfParseError::InternalError)?
601                .try_into()?,
602        );
603        let ftype = match format {
604            0 => TbfFooterV2CredentialsType::Reserved,
605            1 => TbfFooterV2CredentialsType::Rsa3072Key,
606            2 => TbfFooterV2CredentialsType::Rsa4096Key,
607            3 => TbfFooterV2CredentialsType::SHA256,
608            4 => TbfFooterV2CredentialsType::SHA384,
609            5 => TbfFooterV2CredentialsType::SHA512,
610            6 => TbfFooterV2CredentialsType::EcdsaNistP256,
611            _ => {
612                return Err(TbfParseError::BadTlvEntry(
613                    TbfHeaderTypes::TbfFooterCredentials as usize,
614                ));
615            }
616        };
617        let length = match ftype {
618            TbfFooterV2CredentialsType::Reserved => 0,
619            TbfFooterV2CredentialsType::Rsa3072Key => 768,
620            TbfFooterV2CredentialsType::Rsa4096Key => 1024,
621            TbfFooterV2CredentialsType::SHA256 => 32,
622            TbfFooterV2CredentialsType::SHA384 => 48,
623            TbfFooterV2CredentialsType::SHA512 => 64,
624            TbfFooterV2CredentialsType::EcdsaNistP256 => 64,
625        };
626        let data = &b
627            .get(4..(length + 4))
628            .ok_or(TbfParseError::NotEnoughFlash)?;
629        Ok(TbfFooterV2Credentials {
630            format: ftype,
631            data,
632        })
633    }
634}
635
636/// The command permissions specified by the TBF header.
637///
638/// Use the `get_command_permissions()` function to retrieve these.
639pub enum CommandPermissions {
640    /// The TBF header did not specify any permissions for any driver numbers.
641    NoPermsAtAll,
642    /// The TBF header did specify permissions for at least one driver number,
643    /// but not for the requested driver number.
644    NoPermsThisDriver,
645    /// The bitmask of allowed command numbers starting from the offset provided
646    /// when this enum was created.
647    Mask(u64),
648}
649
650/// Single header that can contain all parts of a v2 header.
651///
652/// Note, this struct limits the number of writeable regions an app can have to
653/// four since we need to statically know the length of the array to store in
654/// this type.
655#[derive(Clone, Copy, Debug)]
656pub struct TbfHeaderV2<'a> {
657    pub(crate) base: TbfHeaderV2Base,
658    pub(crate) main: Option<TbfHeaderV2Main>,
659    pub(crate) program: Option<TbfHeaderV2Program>,
660    pub(crate) package_name: Option<&'a str>,
661    pub(crate) writeable_regions: Option<&'a [u8]>,
662    pub(crate) fixed_addresses: Option<&'a [u8]>,
663    pub(crate) permissions: Option<&'a [u8]>,
664    pub(crate) storage_permissions: Option<&'a [u8]>,
665    pub(crate) kernel_version: Option<TbfHeaderV2KernelVersion>,
666    pub(crate) short_id: Option<TbfHeaderV2ShortId>,
667}
668
669/// Type that represents the fields of the Tock Binary Format header.
670///
671/// This specifies the locations of the different code and memory sections
672/// in the tock binary, as well as other information about the application.
673/// The kernel can also use this header to keep persistent state about
674/// the application.
675#[derive(Debug)]
676pub enum TbfHeader<'a> {
677    TbfHeaderV2(TbfHeaderV2<'a>),
678    Padding(TbfHeaderV2Base),
679}
680
681impl<'a> TbfHeader<'a> {
682    /// Return the length of the header.
683    pub fn length(&self) -> u16 {
684        match *self {
685            TbfHeader::TbfHeaderV2(hd) => hd.base.header_size,
686            TbfHeader::Padding(base) => base.header_size,
687        }
688    }
689
690    /// Return whether this is an app or just padding between apps.
691    pub fn is_app(&self) -> bool {
692        match *self {
693            TbfHeader::TbfHeaderV2(_) => true,
694            TbfHeader::Padding(_) => false,
695        }
696    }
697
698    /// Return whether the application is enabled or not.
699    /// Disabled applications are not started by the kernel.
700    pub fn enabled(&self) -> bool {
701        match *self {
702            TbfHeader::TbfHeaderV2(hd) => {
703                // Bit 1 of flags is the enable/disable bit.
704                hd.base.flags & 0x00000001 == 1
705            }
706            TbfHeader::Padding(_) => false,
707        }
708    }
709
710    /// Add up all of the relevant fields in header version 1, or just used the
711    /// app provided value in version 2 to get the total amount of RAM that is
712    /// needed for this app.
713    pub fn get_minimum_app_ram_size(&self) -> u32 {
714        match *self {
715            TbfHeader::TbfHeaderV2(hd) => {
716                if hd.program.is_some() {
717                    hd.program.map_or(0, |p| p.minimum_ram_size)
718                } else if hd.main.is_some() {
719                    hd.main.map_or(0, |m| m.minimum_ram_size)
720                } else {
721                    0
722                }
723            }
724            _ => 0,
725        }
726    }
727
728    /// Get the number of bytes from the start of the app's region in flash that
729    /// is for kernel use only. The app cannot write this region.
730    pub fn get_protected_size(&self) -> u32 {
731        match *self {
732            TbfHeader::TbfHeaderV2(hd) => {
733                if hd.program.is_some() {
734                    hd.program.map_or(0, |p| {
735                        (hd.base.header_size as u32) + p.protected_trailer_size
736                    })
737                } else if hd.main.is_some() {
738                    hd.main.map_or(0, |m| {
739                        (hd.base.header_size as u32) + m.protected_trailer_size
740                    })
741                } else {
742                    0
743                }
744            }
745            _ => 0,
746        }
747    }
748
749    /// Get the start offset of the application binary from the beginning
750    /// of the process binary (start of the TBF header). Only valid if this
751    /// is an app.
752    pub fn get_app_start_offset(&self) -> u32 {
753        // The application binary starts after the header plus any
754        // additional protected space.
755        self.get_protected_size()
756    }
757
758    /// Get the offset from the beginning of the app's flash region where the
759    /// app should start executing.
760    pub fn get_init_function_offset(&self) -> u32 {
761        match *self {
762            TbfHeader::TbfHeaderV2(hd) => {
763                if hd.program.is_some() {
764                    hd.program
765                        .map_or(0, |p| p.init_fn_offset + (hd.base.header_size as u32))
766                } else if hd.main.is_some() {
767                    hd.main
768                        .map_or(0, |m| m.init_fn_offset + (hd.base.header_size as u32))
769                } else {
770                    0
771                }
772            }
773            _ => 0,
774        }
775    }
776
777    /// Get the name of the app.
778    pub fn get_package_name(&self) -> Option<&'a str> {
779        match *self {
780            TbfHeader::TbfHeaderV2(hd) => hd.package_name,
781            _ => None,
782        }
783    }
784
785    /// Get the number of flash regions this app has specified in its header.
786    pub fn number_writeable_flash_regions(&self) -> usize {
787        match *self {
788            TbfHeader::TbfHeaderV2(hd) => hd.writeable_regions.map_or(0, |wr_slice| {
789                let wfr_len = size_of::<TbfHeaderV2WriteableFlashRegion>();
790                wr_slice.len() / wfr_len
791            }),
792            _ => 0,
793        }
794    }
795
796    /// Get the offset and size of a given flash region.
797    pub fn get_writeable_flash_region(&self, index: usize) -> (usize, usize) {
798        match *self {
799            TbfHeader::TbfHeaderV2(hd) => hd.writeable_regions.map_or((0, 0), |wr_slice| {
800                fn get_region(
801                    wr_slice: &[u8],
802                    index: usize,
803                ) -> Result<TbfHeaderV2WriteableFlashRegion, ()> {
804                    let wfr_len = size_of::<TbfHeaderV2WriteableFlashRegion>();
805
806                    let wfr = wr_slice
807                        .get(index * wfr_len..(index + 1) * wfr_len)
808                        .ok_or(())?
809                        .try_into()
810                        .or(Err(()))?;
811                    Ok(wfr)
812                }
813
814                match get_region(wr_slice, index) {
815                    Ok(wr) => (
816                        wr.writeable_flash_region_offset as usize,
817                        wr.writeable_flash_region_size as usize,
818                    ),
819                    Err(()) => (0, 0),
820                }
821            }),
822            _ => (0, 0),
823        }
824    }
825
826    /// Get the address in RAM this process was specifically compiled for. If
827    /// the process is position independent, return `None`.
828    pub fn get_fixed_address_ram(&self) -> Option<u32> {
829        let hd = match self {
830            TbfHeader::TbfHeaderV2(hd) => hd,
831            _ => return None,
832        };
833        let fixed_addresses: TbfHeaderV2FixedAddresses = hd.fixed_addresses?.try_into().ok()?;
834        match fixed_addresses.start_process_ram {
835            0xFFFFFFFF => None,
836            start => Some(start),
837        }
838    }
839
840    /// Get the address in flash this process was specifically compiled for. If
841    /// the process is position independent, return `None`.
842    pub fn get_fixed_address_flash(&self) -> Option<u32> {
843        let hd = match self {
844            TbfHeader::TbfHeaderV2(hd) => hd,
845            _ => return None,
846        };
847        let fixed_addresses: TbfHeaderV2FixedAddresses = hd.fixed_addresses?.try_into().ok()?;
848        match fixed_addresses.start_process_flash {
849            0xFFFFFFFF => None,
850            start => Some(start),
851        }
852    }
853
854    /// Get the permissions for a specified driver and offset.
855    ///
856    /// - `driver_num`: The driver to lookup.
857    /// - `offset`: The offset for the driver to find. An offset value of 1 will
858    ///   find a header with offset 1, so the `allowed_commands` will cover
859    ///   command numbers 64 to 127.
860    ///
861    /// If permissions are found for the driver number, this function will
862    /// return `CommandPermissions::Mask`. If there are permissions in the
863    /// header but not for this driver the function will return
864    /// `CommandPermissions::NoPermsThisDriver`. If the process does not have
865    /// any permissions specified, return `CommandPermissions::NoPermsAtAll`.
866    pub fn get_command_permissions(&self, driver_num: usize, offset: usize) -> CommandPermissions {
867        match self {
868            TbfHeader::TbfHeaderV2(hd) => match hd.permissions {
869                Some(permissions_tlv_slice) => {
870                    // Helper function to wrap the return in a Result.
871                    fn get_command_permissions_result(
872                        permissions_tlv_slice: &[u8],
873                        driver_num: usize,
874                        offset: usize,
875                    ) -> Result<CommandPermissions, ()> {
876                        let mut found_driver_num: bool = false;
877                        let perm_len = size_of::<TbfHeaderDriverPermission>();
878
879                        // Read the number of stored permissions.
880                        let number_perms = u16::from_le_bytes(
881                            permissions_tlv_slice
882                                .get(0..2)
883                                .ok_or(())?
884                                .try_into()
885                                .or(Err(()))?,
886                        );
887                        // Get the remaining slice of just the permissions.
888                        let permissions_slice = permissions_tlv_slice.get(2..).ok_or(())?;
889
890                        // Iterate the permissions to find a match.
891                        for i in 0..number_perms as usize {
892                            let perm: TbfHeaderDriverPermission = permissions_slice
893                                .get((i * perm_len)..((i + 1) * perm_len))
894                                .ok_or(())?
895                                .try_into()
896                                .or(Err(()))?;
897
898                            if perm.driver_number == driver_num as u32 {
899                                found_driver_num = true;
900                                if perm.offset == offset as u32 {
901                                    return Ok(CommandPermissions::Mask(perm.allowed_commands));
902                                }
903                            }
904                        }
905
906                        if found_driver_num {
907                            // We found this driver number but nothing matched the
908                            // requested offset. Since permissions are default off,
909                            // we can return a mask of all zeros.
910                            Ok(CommandPermissions::Mask(0))
911                        } else {
912                            Ok(CommandPermissions::NoPermsThisDriver)
913                        }
914                    }
915
916                    get_command_permissions_result(permissions_tlv_slice, driver_num, offset)
917                        .unwrap_or(CommandPermissions::NoPermsAtAll)
918                }
919                _ => CommandPermissions::NoPermsAtAll,
920            },
921            _ => CommandPermissions::NoPermsAtAll,
922        }
923    }
924
925    /// Get the process `write_id`.
926    ///
927    /// Returns `None` if a `write_id` is not included. This indicates the TBF
928    /// does not have the ability to store new items.
929    pub fn get_storage_write_id(&self) -> Option<core::num::NonZeroU32> {
930        match self {
931            TbfHeader::TbfHeaderV2(hd) => match hd.storage_permissions {
932                Some(storage_permissions_tlv_slice) => {
933                    // write_id
934                    core::num::NonZeroU32::new(u32::from_le_bytes(
935                        storage_permissions_tlv_slice.get(0..4)?.try_into().ok()?,
936                    ))
937                }
938                _ => None,
939            },
940            _ => None,
941        }
942    }
943
944    /// Get the number of valid `read_ids` and the `read_ids`.
945    /// Returns `None` if a `read_ids` is not included.
946    pub fn get_storage_read_ids(&self) -> Option<(usize, [u32; NUM_STORAGE_PERMISSIONS])> {
947        match self {
948            TbfHeader::TbfHeaderV2(hd) => match hd.storage_permissions {
949                Some(storage_permissions_tlv_slice) => {
950                    let storage_permissions: TbfHeaderV2StoragePermissions<
951                        NUM_STORAGE_PERMISSIONS,
952                    > = storage_permissions_tlv_slice.try_into().ok()?;
953
954                    Some((
955                        storage_permissions.read_length.into(),
956                        storage_permissions.read_ids,
957                    ))
958                }
959                _ => None,
960            },
961            _ => None,
962        }
963    }
964
965    /// Get the number of valid `access_ids` and the `access_ids`.
966    /// Returns `None` if a `access_ids` is not included.
967    pub fn get_storage_modify_ids(&self) -> Option<(usize, [u32; NUM_STORAGE_PERMISSIONS])> {
968        match self {
969            TbfHeader::TbfHeaderV2(hd) => match hd.storage_permissions {
970                Some(storage_permissions_tlv_slice) => {
971                    let storage_permissions: TbfHeaderV2StoragePermissions<
972                        NUM_STORAGE_PERMISSIONS,
973                    > = storage_permissions_tlv_slice.try_into().ok()?;
974
975                    Some((
976                        storage_permissions.modify_length.into(),
977                        storage_permissions.modify_ids,
978                    ))
979                }
980                _ => None,
981            },
982            _ => None,
983        }
984    }
985
986    /// Get the minimum compatible kernel version this process requires.
987    /// Returns `None` if the kernel compatibility header is not included.
988    pub fn get_kernel_version(&self) -> Option<(u16, u16)> {
989        match self {
990            TbfHeader::TbfHeaderV2(hd) => match hd.kernel_version {
991                Some(kernel_version) => Some((kernel_version.major, kernel_version.minor)),
992                _ => None,
993            },
994            _ => None,
995        }
996    }
997
998    /// Return the offset where the binary ends in the TBF or 0 if there
999    /// is no binary. If there is a Main header the end offset is the size
1000    /// of the TBF, while if there is a Program header it can be smaller.
1001    pub fn get_binary_end(&self) -> u32 {
1002        match self {
1003            TbfHeader::TbfHeaderV2(hd) => hd
1004                .program
1005                .map_or(hd.base.total_size, |p| p.binary_end_offset),
1006            _ => 0,
1007        }
1008    }
1009
1010    /// Return the version number of the Userspace Binary in this TBF
1011    /// Object, or 0 if there is no binary or no version number.
1012    pub fn get_binary_version(&self) -> u32 {
1013        match self {
1014            TbfHeader::TbfHeaderV2(hd) => hd.program.map_or(0, |p| p.version),
1015            _ => 0,
1016        }
1017    }
1018
1019    /// Return the fixed ShortId of the application if it was specified in the
1020    /// TBF header.
1021    pub fn get_fixed_short_id(&self) -> Option<core::num::NonZeroU32> {
1022        match self {
1023            TbfHeader::TbfHeaderV2(hd) => hd.short_id.and_then(|si| si.short_id),
1024            _ => None,
1025        }
1026    }
1027}