Skip to content

Commit

Permalink
Merge pull request #4 from turing-machines/feature/userspace_installer
Browse files Browse the repository at this point in the history
Feature/userspace installer
  • Loading branch information
svenrademakers authored May 31, 2024
2 parents 217a36e + a694ef1 commit 60b14cb
Show file tree
Hide file tree
Showing 8 changed files with 480 additions and 410 deletions.
File renamed without changes.
427 changes: 19 additions & 408 deletions src/bin/sdcard.rs

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/bin/sdcard_userspace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use bmc_installer::turing_pi::{led, read_from_sdcard, upgrade_bmc};

fn main() -> anyhow::Result<()> {
let led_tx = led::led_blink_thread();
let (bootloader, rootfs) = read_from_sdcard()?;
upgrade_bmc(rootfs, bootloader, || (), led_tx)
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod format;
pub mod image;
pub mod nand;
pub mod turing_pi;
pub mod ubi;
pub mod util;
210 changes: 210 additions & 0 deletions src/turing_pi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
pub mod led;

use anyhow::Context;
use nix::errno::Errno;
use nix::mount::{mount, MsFlags};
use retry::{delay::Fixed, retry};
use std::fmt::Debug;
use std::io::{self, Read, Seek};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use std::{fs, path::Path};

use crate::{
format, image,
nand::{mtd::MtdNand, Nand},
ubi::{
self,
ubinize::{BasicVolume, Volume},
VolType,
},
};

use self::led::LedState;
const BANNER: &str = r"
_____ _ _ ____ ___ _ _ ____
|_ _| | | | _ \|_ _| \ | |/ ___|
| | | | | | |_) || || \| | | _
| | | |_| | _ < | || |\ | |_| |
|_| \___/|_| \_\___|_| \_|\____|
";

/// Set up the basic environment (e.g. mount points).
pub fn setup_initramfs() -> anyhow::Result<()> {
// Handle mounts
for (mount_dev, mount_path, mount_type) in [
(None, "/dev", "devtmpfs"),
(None, "/proc", "proc"),
(None, "/sys", "sysfs"),
] {
let path = Path::new(mount_path);

if !path.is_dir() {
fs::create_dir(path)?;
}

let result = mount(
mount_dev.or(Some(path)),
path,
Some(mount_type),
MsFlags::empty(),
None::<&str>,
);

match result {
// Ignore EBUSY, which indicates that the mountpoint is already mounted.
Err(Errno::EBUSY) => (),
r => r?,
};
}

Ok(())
}

/// Sleep until the user cuts power.
pub fn wait_forever() -> ! {
loop {
thread::sleep(Duration::from_secs(3600));
}
}

/// This is the core function of the installer. Several tasks are executed to
/// upgrade from v1.x firmware or to install onto new flash.
pub fn upgrade_bmc(
mut rootfs: impl Read + Seek,
bootloader: impl Read,
pre_upgrade: impl FnOnce(),
led_tx: mpsc::Sender<&'static [LedState]>,
) -> anyhow::Result<()> {
eprintln!("{}", BANNER);

// Open the NAND flash partitions
let nand_boot = MtdNand::open_named("boot")?;
let nand_ubi = MtdNand::open_named("ubi")?;

// Locate the rootfs and bootloader to be written
let rootfs_size = image::erofs_size(&mut rootfs)?;

// Define the UBI image
let ubi_volumes: Vec<Box<dyn Volume + '_>> = vec![
Box::new(
BasicVolume::new(VolType::Dynamic)
.id(0)
.name("uboot-env")
.size(65536),
),
Box::new(
BasicVolume::new(VolType::Static)
.name("rootfs")
.skipcheck() // Opening the volume at boot takes ~10sec. longer without this flag
.size(rootfs_size)
.image(&mut rootfs),
),
];

// These are the tasks to be run once the user confirms the operation:
struct TaskCtx<'a, N: Nand, R: Read> {
rpt: howudoin::Tx,
nand_boot: N,
nand_ubi: N,
ebt: Option<ubi::Ebt>,
ubi_volumes: Vec<Box<dyn Volume + 'a>>,
bootloader: R,
}
type TaskFn<Ctx> = fn(&mut Ctx) -> anyhow::Result<()>;
let tasks: [(&str, TaskFn<TaskCtx<'_, _, _>>); 5] = [
("Purging boot0 code", |ctx| {
let purged = format::purge_boot0(&mut ctx.nand_boot)?;
if purged {
ctx.rpt
.add_info("Legacy Allwinner boot code has been found and erased");
}
Ok(())
}),
("Analyzing UBI partition", |ctx| {
let ebt = ubi::scan_blocks(&mut ctx.nand_ubi)?;
ctx.ebt = Some(ebt);
Ok(())
}),
("Formatting UBI partition", |ctx| {
ubi::format(&mut ctx.nand_ubi, ctx.ebt.as_mut().unwrap())?;
Ok(())
}),
("Writing rootfs", |ctx| {
ubi::write_volumes(
&mut ctx.nand_ubi,
ctx.ebt.as_mut().unwrap(),
ctx.ubi_volumes.split_off(0),
)?;
Ok(())
}),
("Updating bootloader", |ctx| {
format::raw::write_raw_image(&mut ctx.nand_boot, &mut ctx.bootloader, false)?;
Ok(())
}),
];

// Ready...
let _ = led_tx.send(led::LED_READY);

pre_upgrade();

// ...go!
howudoin::init(howudoin::consumers::TermLine::default());
let rpt = howudoin::new()
.label("Installing BMC firmware")
.set_len(u64::try_from(tasks.len()).ok());
let mut ctx = TaskCtx {
rpt,
nand_boot,
nand_ubi,
ebt: None,
ubi_volumes,
bootloader,
};
let _ = led_tx.send(led::LED_BUSY);
for (desc, task) in tasks {
ctx.rpt.desc(desc);
ctx.rpt.inc();

if let Err(error) = task(&mut ctx) {
howudoin::disable();
thread::sleep(Duration::from_millis(10)); // Give howudoin time to shut down
return Err(error);
}
}

ctx.rpt.finish();
howudoin::disable();
thread::sleep(Duration::from_millis(10)); // Give howudoin time to shut down
let _ = led_tx.send(led::LED_DONE);

Ok(())
}

/// Locate the rootfs and bootloader to be written from a fixed partitioned SDcard layout
///
/// # Returns
///
/// tuple (bootloader, rootfs)
pub fn read_from_sdcard() -> anyhow::Result<(impl Read + Debug, impl Read + Seek + Debug)> {
const ROOTFS_PATH: &str = "/dev/mmcblk0p2";
const BOOTLOADER_PATH: &str = "/dev/mmcblk0";
const BOOTLOADER_SIZE: u64 = 6 * 64 * 2048;
const BOOTLOADER_OFFSET: u64 = 8192; // Boot ROM expects this offset, so it will never change

let rootfs = retry(Fixed::from_millis(100).take(10), || {
fs::File::open(ROOTFS_PATH)
})
.context(ROOTFS_PATH)?;

let mut bootloader = retry(Fixed::from_millis(100).take(10), || {
fs::File::open(BOOTLOADER_PATH)
})
.context(BOOTLOADER_PATH)?;

bootloader.seek(io::SeekFrom::Start(BOOTLOADER_OFFSET))?;
let bootloader = bootloader.take(BOOTLOADER_SIZE);
Ok((bootloader, rootfs))
}
Loading

0 comments on commit 60b14cb

Please sign in to comment.