From daad28efa2bfd8445abe4821fcecb5585477b9b3 Mon Sep 17 00:00:00 2001
From: Samuel Tardieu <sam@rfc1149.net>
Date: Tue, 30 Jul 2024 19:00:00 +0200
Subject: [PATCH] Check that the bootloader and firmware-updater are compatible

---
 Cargo.lock                         | 111 +++++++++++++++++++++++++++++
 bootloader/python/bootloader.py    |   2 +-
 firmware-updater/Cargo.toml        |   3 +
 firmware-updater/src/bootloader.rs |  35 ++++-----
 firmware-updater/src/main.rs       |   3 +
 5 files changed, 132 insertions(+), 22 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 31bc357..9ab363e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,15 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "anstream"
 version = "0.6.15"
@@ -667,6 +676,19 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
 
+[[package]]
+name = "env_logger"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
 [[package]]
 name = "eyre"
 version = "0.6.12"
@@ -686,7 +708,10 @@ dependencies = [
  "color-eyre",
  "elf",
  "indicatif",
+ "log",
+ "pretty_env_logger",
  "rppal",
+ "semver 1.0.23",
  "thiserror",
 ]
 
@@ -788,6 +813,18 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
 [[package]]
 name = "i2c2-target"
 version = "0.1.0"
@@ -832,6 +869,17 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "is-terminal"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys",
+]
+
 [[package]]
 name = "is_terminal_polyfill"
 version = "1.70.1"
@@ -856,6 +904,12 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
 
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
 [[package]]
 name = "memchr"
 version = "2.7.4"
@@ -957,6 +1011,16 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
 
+[[package]]
+name = "pretty_env_logger"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c"
+dependencies = [
+ "env_logger",
+ "log",
+]
+
 [[package]]
 name = "proc-macro-error"
 version = "1.0.4"
@@ -1005,6 +1069,35 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 
+[[package]]
+name = "regex"
+version = "1.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
 [[package]]
 name = "rppal"
 version = "0.18.0"
@@ -1136,6 +1229,15 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
 [[package]]
 name = "thiserror"
 version = "1.0.63"
@@ -1258,6 +1360,15 @@ dependencies = [
  "vcell",
 ]
 
+[[package]]
+name = "winapi-util"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+dependencies = [
+ "windows-sys",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.52.0"
diff --git a/bootloader/python/bootloader.py b/bootloader/python/bootloader.py
index dbb7544..7c72417 100644
--- a/bootloader/python/bootloader.py
+++ b/bootloader/python/bootloader.py
@@ -1,7 +1,7 @@
 import struct
 
 # Major and minor version of required firmware
-_REQUIRED_FIRMWARE_VERSION = (0, 1)
+_REQUIRED_FIRMWARE_VERSION = (0, 2)
 
 
 class FirmwareVersionMismatch(Exception):
diff --git a/firmware-updater/Cargo.toml b/firmware-updater/Cargo.toml
index 41a7a09..7805229 100644
--- a/firmware-updater/Cargo.toml
+++ b/firmware-updater/Cargo.toml
@@ -12,5 +12,8 @@ clap = { version = "4.5.9", features = ["cargo", "derive"] }
 color-eyre = "0.6.3"
 elf = "0.7.4"
 indicatif = "0.17.8"
+log = "0.4.22"
+pretty_env_logger = "0.5.0"
 rppal = "0.18.0"
+semver = "1.0.23"
 thiserror = "1.0.63"
diff --git a/firmware-updater/src/bootloader.rs b/firmware-updater/src/bootloader.rs
index eaaae3c..dd8c36f 100644
--- a/firmware-updater/src/bootloader.rs
+++ b/firmware-updater/src/bootloader.rs
@@ -5,30 +5,17 @@ use bootloader_params::commands::{
 };
 use indicatif::ProgressBar;
 use rppal::i2c::{self, I2c};
+use semver::{Version, VersionReq};
 use std::{fmt::Display, thread, time::Duration};
 
 use crate::elf_loader::Segment;
 
-pub struct Version {
-    major: u8,
-    minor: u8,
-    patch: u8,
-}
-
-impl Display for Version {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
-    }
-}
-
-impl From<&[u8; 3]> for Version {
-    fn from(value: &[u8; 3]) -> Self {
-        Self {
-            major: value[0],
-            minor: value[1],
-            patch: value[2],
-        }
-    }
+fn version_from_bytes(bytes: &[u8; 3]) -> Version {
+    Version::new(
+        u64::from(bytes[0]),
+        u64::from(bytes[1]),
+        u64::from(bytes[2]),
+    )
 }
 
 pub enum ActiveProgram {
@@ -59,6 +46,8 @@ pub enum Error {
     ChecksumMismatch,
     #[error("memory does not match expected content")]
     MemoryContentMismatch,
+    #[error(transparent)]
+    SemVer(#[from] semver::Error),
 }
 
 pub type Result<T, E = Error> = std::result::Result<T, E>;
@@ -82,10 +71,14 @@ pub fn active_program(i2c: &mut I2c) -> Result<ActiveProgram> {
             && buffer[0] == device
             && i2c.block_read(CMD_FIRMWARE_VERSION, &mut buffer).is_ok()
         {
-            let version = Version::from(&buffer);
+            let version = version_from_bytes(&buffer);
             if device == CONTROLLER_ID {
                 return Ok(ActiveProgram::Controller(version));
             } else {
+                let fwupd_req = VersionReq::parse(env!("CARGO_PKG_VERSION"))?;
+                if !fwupd_req.matches(&version) {
+                    log::warn!("Bootloader version {version} does not match requirement for firmware-updater {fwupd_req}");
+                }
                 return Ok(ActiveProgram::Bootloader(version));
             }
         }
diff --git a/firmware-updater/src/main.rs b/firmware-updater/src/main.rs
index 6b6bba6..b40b5bc 100644
--- a/firmware-updater/src/main.rs
+++ b/firmware-updater/src/main.rs
@@ -32,6 +32,9 @@ type Result<T, E = Error> = std::result::Result<T, E>;
 
 fn main() -> color_eyre::Result<()> {
     color_eyre::install()?;
+    pretty_env_logger::formatted_builder()
+        .filter_level(log::LevelFilter::Info)
+        .init();
     match Cli::parse().command {
         Command::CheckFile(args) => cmd_check_file(args)?,
         Command::Flash(args) => cmd_flash(args)?,
-- 
GitLab