Skip to content

Commit

Permalink
Merge branch 'next/patch' of github.com:Start9Labs/start-os into bugf…
Browse files Browse the repository at this point in the history
…ix/cleanup-after-commit
  • Loading branch information
dr-bonez committed Nov 16, 2023
2 parents ffa9d32 + 749cde1 commit 53ca9e0
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 95 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ secrets.db
/results
/dpkg-workdir
/compiled.tar
/compiled-*.tar
/compiled-*.tar
/firmware
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ ARCH := $(shell if [ "$(PLATFORM)" = "raspberrypi" ]; then echo aarch64; else ec
IMAGE_TYPE=$(shell if [ "$(PLATFORM)" = raspberrypi ]; then echo img; else echo iso; fi)
BINS := core/target/$(ARCH)-unknown-linux-gnu/release/startbox core/target/aarch64-unknown-linux-musl/release/container-init core/target/x86_64-unknown-linux-musl/release/container-init
WEB_UIS := web/dist/raw/ui web/dist/raw/setup-wizard web/dist/raw/diagnostic-ui web/dist/raw/install-wizard
BUILD_SRC := $(shell git ls-files build) build/lib/depends build/lib/conflicts
FIRMWARE_ROMS := ./firmware/$(PLATFORM) $(shell jq --raw-output '.[] | select(.platform[] | contains("$(PLATFORM)")) | "./firmware/$(PLATFORM)/" + .id + ".rom.gz"' build/lib/firmware.json)
BUILD_SRC := $(shell git ls-files build) build/lib/depends build/lib/conflicts $(FIRMWARE_ROMS)
DEBIAN_SRC := $(shell git ls-files debian/)
IMAGE_RECIPE_SRC := $(shell git ls-files image-recipe/)
STARTD_SRC := core/startos/startd.service $(BUILD_SRC)
Expand Down Expand Up @@ -72,6 +73,7 @@ clean:
rm -rf dpkg-workdir
rm -rf image-recipe/deb
rm -rf results
rm -rf build/lib/firmware
rm -f ENVIRONMENT.txt
rm -f PLATFORM.txt
rm -f GIT_HASH.txt
Expand Down Expand Up @@ -134,6 +136,8 @@ install: $(ALL_TARGETS)
$(call cp,system-images/compat/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/compat.tar)
$(call cp,system-images/utils/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/utils.tar)
$(call cp,system-images/binfmt/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/binfmt.tar)

$(call cp,firmware/$(PLATFORM),$(DESTDIR)/usr/lib/startos/firmware)

update-overlay: $(ALL_TARGETS)
@echo "\033[33m!!! THIS WILL ONLY REFLASH YOUR DEVICE IN MEMORY !!!\033[0m"
Expand Down Expand Up @@ -165,6 +169,9 @@ upload-ota: results/$(BASENAME).squashfs
build/lib/depends build/lib/conflicts: build/dpkg-deps/*
build/dpkg-deps/generate.sh

$(FIRMWARE_ROMS): build/lib/firmware.json download-firmware.sh $(PLATFORM_FILE)
./download-firmware.sh $(PLATFORM)

system-images/compat/docker-images/$(ARCH).tar: $(COMPAT_SRC) core/Cargo.lock
cd system-images/compat && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar

Expand Down
13 changes: 13 additions & 0 deletions build/lib/firmware.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"id": "pureboot-librem_mini_v2-basic_usb_autoboot_blob_jail-Release-28.3",
"platform": ["x86_64"],
"system-product-name": "librem_mini_v2",
"bios-version": {
"semver-prefix": "PureBoot-Release-",
"semver-range": "<28.3"
},
"url": "https://source.puri.sm/firmware/releases/-/raw/master/librem_mini_v2/custom/pureboot-librem_mini_v2-basic_usb_autoboot_blob_jail-Release-28.3.rom.gz",
"shasum": "5019bcf53f7493c7aa74f8ef680d18b5fc26ec156c705a841433aaa2fdef8f35"
}
]
Binary file not shown.
4 changes: 4 additions & 0 deletions core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/startos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ rpassword = "7.2.0"
rpc-toolkit = "0.2.2"
rust-argon2 = "2.0.0"
scopeguard = "1.1" # because avahi-sys fucks your shit up
semver = { version = "1.0.20", features = ["serde"] }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_cbor = { package = "ciborium", version = "0.2.1" }
serde_json = "1.0"
Expand Down
19 changes: 13 additions & 6 deletions core/startos/src/bins/start_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use tracing::instrument;

use crate::context::rpc::RpcContextConfig;
use crate::context::{DiagnosticContext, InstallContext, SetupContext};
use crate::disk::fsck::RepairStrategy;
use crate::disk::fsck::{RepairStrategy, RequiresReboot};
use crate::disk::main::DEFAULT_PASSWORD;
use crate::disk::REPAIR_DISK_PATH;
use crate::firmware::update_firmware;
Expand All @@ -30,11 +30,18 @@ async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Er
}
}));

if update_firmware().await?.0 {
return Ok(Some(Shutdown {
export_args: None,
restart: true,
}));
match update_firmware().await {
Ok(RequiresReboot(true)) => {
return Ok(Some(Shutdown {
export_args: None,
restart: true,
}))
}
Err(e) => {
tracing::warn!("Error performing firmware update: {e}");
tracing::debug!("{e:?}");
}
_ => (),
}

Command::new("ln")
Expand Down
2 changes: 1 addition & 1 deletion core/startos/src/disk/fsck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::Error;
pub mod btrfs;
pub mod ext4;

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
#[must_use]
pub struct RequiresReboot(pub bool);
impl std::ops::BitOrAssign for RequiresReboot {
Expand Down
167 changes: 123 additions & 44 deletions core/startos/src/firmware.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,63 @@
use std::collections::BTreeSet;
use std::path::Path;

use async_compression::tokio::bufread::GzipDecoder;
use clap::ArgMatches;
use rpc_toolkit::command;
use serde::{Deserialize, Serialize};
use tokio::fs::File;
use tokio::io::{AsyncRead, BufReader};
use tokio::io::BufReader;
use tokio::process::Command;

use crate::disk::fsck::RequiresReboot;
use crate::prelude::*;
use crate::util::Invoke;
use crate::PLATFORM;

/// Part of the Firmware, look there for more about
#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct VersionMatcher {
/// Strip this prefix on the version matcher
semver_prefix: Option<String>,
/// Match the semver to this range
semver_range: Option<semver::VersionReq>,
/// Strip this suffix on the version matcher
semver_suffix: Option<String>,
}

/// Inside a file that is firmware.json, we
/// wanted a structure that could help decide what to do
/// for each of the firmware versions
#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct Firmware {
id: String,
/// This is the platform(s) the firmware was built for
platform: BTreeSet<String>,
/// This usally comes from the dmidecode
system_product_name: Option<String>,
/// The version comes from dmidecode, then we decide if it matches
bios_version: Option<VersionMatcher>,
/// the hash of the firmware rom.gz
shasum: String,
}

fn display_firmware_update_result(arg: RequiresReboot, _: &ArgMatches) {
if arg.0 {
println!("Firmware successfully updated! Reboot to apply changes.");
} else {
println!("No firmware update available.");
}
}

/// We wanted to make sure during every init
/// that the firmware was the correct and updated for
/// systems like the Pure System that a new firmware
/// was released and the updates where pushed through the pure os.
#[command(rename = "update-firmware", display(display_firmware_update_result))]
pub async fn update_firmware() -> Result<RequiresReboot, Error> {
let product_name = String::from_utf8(
let system_product_name = String::from_utf8(
Command::new("dmidecode")
.arg("-s")
.arg("system-product-name")
Expand All @@ -19,52 +66,84 @@ pub async fn update_firmware() -> Result<RequiresReboot, Error> {
)?
.trim()
.to_owned();
if product_name.is_empty() {
let bios_version = String::from_utf8(
Command::new("dmidecode")
.arg("-s")
.arg("bios-version")
.invoke(ErrorKind::Firmware)
.await?,
)?
.trim()
.to_owned();
if system_product_name.is_empty() || bios_version.is_empty() {
return Ok(RequiresReboot(false));
}
let firmware_dir = Path::new("/usr/lib/startos/firmware").join(&product_name);
if tokio::fs::metadata(&firmware_dir).await.is_ok() {
let current_firmware = String::from_utf8(
Command::new("dmidecode")
.arg("-s")
.arg("bios-version")
.invoke(ErrorKind::Firmware)
.await?,
)?
.trim()
.to_owned();
if tokio::fs::metadata(firmware_dir.join(format!("{current_firmware}.rom.gz")))
.await
.is_err()
&& tokio::fs::metadata(firmware_dir.join(format!("{current_firmware}.rom")))
.await
.is_err()
{
let mut firmware_read_dir = tokio::fs::read_dir(&firmware_dir).await?;
while let Some(entry) = firmware_read_dir.next_entry().await? {
let filename = entry.file_name().to_string_lossy().into_owned();
let rdr: Option<Box<dyn AsyncRead + Unpin + Send>> =
if filename.ends_with(".rom.gz") {
Some(Box::new(GzipDecoder::new(BufReader::new(
File::open(entry.path()).await?,
))))
} else if filename.ends_with(".rom") {
Some(Box::new(File::open(entry.path()).await?))
} else {
None
};
if let Some(mut rdr) = rdr {
Command::new("flashrom")
.arg("-p")
.arg("internal")
.arg("-w-")
.input(Some(&mut rdr))
.invoke(ErrorKind::Firmware)
.await?;
return Ok(RequiresReboot(true));

let firmware_dir = Path::new("/usr/lib/startos/firmware");

for firmware in serde_json::from_str::<Vec<Firmware>>(
&tokio::fs::read_to_string("/usr/lib/startos/firmware.json").await?,
)
.with_kind(ErrorKind::Deserialization)?
{
let id = firmware.id;
let matches_product_name = firmware
.system_product_name
.map_or(true, |spn| spn == system_product_name);
let matches_bios_version = firmware
.bios_version
.map_or(Some(true), |bv| {
let mut semver_str = bios_version.as_str();
if let Some(prefix) = &bv.semver_prefix {
semver_str = semver_str.strip_prefix(prefix)?;
}
if let Some(suffix) = &bv.semver_suffix {
semver_str = semver_str.strip_suffix(suffix)?;
}
}
let semver = semver_str
.split(".")
.filter_map(|v| v.parse().ok())
.chain(std::iter::repeat(0))
.take(3)
.collect::<Vec<_>>();
let semver = semver::Version::new(semver[0], semver[1], semver[2]);
Some(
bv.semver_range
.as_ref()
.map_or(true, |r| r.matches(&semver)),
)
})
.unwrap_or(false);
if firmware.platform.contains(&*PLATFORM) && matches_product_name && matches_bios_version {
let filename = format!("{id}.rom.gz");
let firmware_path = firmware_dir.join(&filename);
Command::new("sha256sum")
.arg("-c")
.input(Some(&mut std::io::Cursor::new(format!(
"{} {}",
firmware.shasum,
firmware_path.display()
))))
.invoke(ErrorKind::Filesystem)
.await?;
let mut rdr = if tokio::fs::metadata(&firmware_path).await.is_ok() {
GzipDecoder::new(BufReader::new(File::open(&firmware_path).await?))
} else {
return Err(Error::new(
eyre!("Firmware {id}.rom.gz not found in {firmware_dir:?}"),
ErrorKind::NotFound,
));
};
Command::new("flashrom")
.arg("-p")
.arg("internal")
.arg("-w-")
.input(Some(&mut rdr))
.invoke(ErrorKind::Firmware)
.await?;
return Ok(RequiresReboot(true));
}
}

Ok(RequiresReboot(false))
}
Loading

0 comments on commit 53ca9e0

Please sign in to comment.