1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! This loadstone sub-crate contains all definitions to help generate
//! final loadstone binaries.
//!
//! NOTE: This code is not included anywhere from Loadstone itself! This
//! is a dependency of the Loadstone **build script**. The build script
//! uses this dependency to help generate the code that Loadstone includes
//! (things like feature flags, memory map configuration, etc).

#![feature(stmt_expr_attributes)]
#![feature(bool_to_option)]

use std::{array::IntoIter, fmt::Display};

use features::{BootMetrics, FeatureConfiguration, Serial};
use memory::{external_flash, MemoryConfiguration};
use port::Port;
use security::{SecurityConfiguration, SecurityMode};
use serde::{Deserialize, Serialize};

pub mod port;
pub mod pins;
pub mod memory;
pub mod features;
pub mod security;
pub mod codegen;

#[derive(Serialize, Deserialize, Default, Debug)]
/// Defines all configuration for a "codegen" loadstone port. This struct
/// is meant to be modified live by the `loadstone_front` GUI, then serialized
/// into a .ron file, which will be read by the loadstone `build.rs` script
/// and turned into the port source.
pub struct Configuration {
    /// The target chip, usually defined at the chip subfamily level (e.g stm32f412).
    pub port: Port,
    /// Internal and external flash configuration, including firmware image
    /// banks and bank sizes.
    pub memory_configuration: MemoryConfiguration,
    /// Miscellaneous features such as serial communication or boot metrics.
    pub feature_configuration: FeatureConfiguration,
    /// Image authenticity, integrity and (potentially) secrecy options (ECDSA, CRC, etc).
    pub security_configuration: SecurityConfiguration,
}

impl Configuration {
    /// True if the configuration is comprehensive enough to generate a loadstone binary.
    pub fn complete(&self) -> bool { self.required_configuration_steps().count() == 0 }

    /// Returns an iterator over the feature flags that will be necessary to compile loadstone
    /// when using this configuration struct.
    pub fn required_feature_flags(&self) -> impl Iterator<Item = &'static str> {
        let mut flags = vec![];
        match self.port {
            Port::Stm32F412 => flags.push("stm32f412"),
            Port::Wgm160P => flags.push("wgm160p"),
        };

        if self.security_configuration.security_mode == SecurityMode::P256ECDSA {
            flags.push("ecdsa-verify");
        };

        flags.into_iter()
    }

    /// Missing configuration steps to have enough information to generate a loadstone binary.
    pub fn required_configuration_steps(&self) -> impl Iterator<Item = RequiredConfigurationStep> {
        #[rustfmt::skip]
        IntoIter::new([
            self.memory_configuration.internal_memory_map.bootable_index.is_none()
                .then_some(RequiredConfigurationStep::BootableBank),

            (self.security_configuration.security_mode == SecurityMode::P256ECDSA
                && self.security_configuration.verifying_key_raw.is_empty())
                .then_some(RequiredConfigurationStep::PublicKey),

        ])
        .flatten()
    }

    /// Cleans up the configuration, enforcing all internal invariants.
    // TODO replace with typestates / type safety wherever possible, by adjusting the loadstone
    // front app to match.
    pub fn cleanup(&mut self) {
        if !features::Serial::supported(&self.port) {
            self.feature_configuration.serial = Serial::Disabled;
        }

        if !features::BootMetrics::timing_supported(&self.port) {
            if let BootMetrics::Enabled{timing} = &mut self.feature_configuration.boot_metrics {
                *timing = false
            }
        }

        if !external_flash(&self.port).any(|f| Some(f) == self.memory_configuration.external_flash)
        {
            self.memory_configuration.external_flash = None;
        }

        if self.memory_configuration.external_flash.is_none() {
            self.memory_configuration.external_memory_map.banks.clear();
        }
    }
}

/// Configuration steps that may be required to properly define a loadstone binary.
pub enum RequiredConfigurationStep {
    PublicKey,
    SerialTxPin,
    SerialRxPin,
    BootableBank,
}

impl Display for RequiredConfigurationStep {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            RequiredConfigurationStep::PublicKey => {
                "[Security] Provide P256 ECDSA public key or enable CRC32 mode"
            }
            RequiredConfigurationStep::SerialTxPin => "[Features] Define Serial Tx pin",
            RequiredConfigurationStep::SerialRxPin => "[Features] Define Serial Rx pin",
            RequiredConfigurationStep::BootableBank => "[Memory Map] Define a bootable bank",
        })
    }
}