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