Skip to content

Commit d8f1e51

Browse files
committed
feat: integrate U-Boot boot flow (#4)
1 parent a07e0ae commit d8f1e51

File tree

17 files changed

+408
-82
lines changed

17 files changed

+408
-82
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.rugpi
2-
/target
2+
/target
3+
/playground

.vscode/cspell.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"rbind",
3737
"resolv",
3838
"rootfstype",
39+
"rootwait",
3940
"rugpi",
4041
"rustfmt",
4142
"sfdisk",
@@ -45,6 +46,7 @@
4546
"tempdir",
4647
"tmpfs",
4748
"tryboot",
49+
"uboot",
4850
"unistd",
4951
"upperdir",
5052
"uuid",

Cargo.lock

Lines changed: 16 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ anyhow = "1.0.71"
1414
camino = "1.1.6"
1515
clap = { version = "4.3", features = ["derive"] }
1616
serde = { version = "1", features = ["derive"] }
17-
xscript = "0.2.0"
17+
xscript = "0.2.0"

crates/rugpi-bakery/src/config.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
};
77

88
use camino::Utf8PathBuf;
9-
use rugpi_common::Anyhow;
9+
use rugpi_common::{boot::BootFlow, Anyhow};
1010
use serde::{Deserialize, Serialize};
1111

1212
use crate::recipes::{ParameterValue, RecipeName};
@@ -17,15 +17,18 @@ pub struct BakeryConfig {
1717
/// The recipes to include.
1818
#[serde(default)]
1919
pub recipes: HashSet<RecipeName>,
20-
/// Include firmware files for Pi 4.
21-
#[serde(default)]
22-
pub include_firmware: IncludeFirmware,
2320
/// The recipes to exclude.
2421
#[serde(default)]
2522
pub exclude: HashSet<RecipeName>,
2623
/// Parameters for the recipes.
2724
#[serde(default)]
2825
pub parameters: HashMap<RecipeName, HashMap<String, ParameterValue>>,
26+
/// Indicates whether to include firmware files in the image.
27+
#[serde(default)]
28+
pub include_firmware: IncludeFirmware,
29+
/// Indicates which boot flow to use for the image.
30+
#[serde(default)]
31+
pub boot_flow: BootFlow,
2932
}
3033

3134
#[derive(Debug, Clone, Serialize, Deserialize, Default)]

crates/rugpi-bakery/src/tasks/bake.rs

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ use std::fs;
55
use camino::Utf8Path;
66
use clap::Parser;
77
use rugpi_common::{
8+
boot::{uboot::UBootEnv, BootFlow},
89
loop_dev::LoopDevice,
910
mount::Mounted,
1011
partitions::{get_disk_id, mkfs_ext4, mkfs_vfat, sfdisk_apply_layout, sfdisk_image_layout},
11-
patch_cmdline, patch_config, Anyhow,
12+
patch_boot, patch_config, Anyhow,
1213
};
1314
use tempdir::TempDir;
1415
use xscript::{run, Run};
1516

16-
use crate::config::load_config;
17+
use crate::config::{load_config, BakeryConfig, IncludeFirmware};
1718

1819
#[derive(Debug, Parser)]
1920
pub struct BakeTask {
@@ -39,35 +40,58 @@ pub fn run(task: &BakeTask) -> Anyhow<()> {
3940
mkfs_vfat(loop_device.partition(1), "CONFIG")?;
4041
mkfs_vfat(loop_device.partition(2), "BOOT-A")?;
4142
mkfs_ext4(loop_device.partition(5), "system-a")?;
42-
let temp_dir = TempDir::new("rugpi")?;
43-
let temp_dir_path = Utf8Path::from_path(temp_dir.path()).unwrap();
44-
{
45-
let _mounted_root = Mounted::mount(loop_device.partition(5), temp_dir_path)?;
46-
let boot_dir = temp_dir_path.join("boot");
47-
fs::create_dir_all(&boot_dir)?;
48-
let _mounted_boot = Mounted::mount(loop_device.partition(2), &boot_dir)?;
49-
println!("Writing system files...");
50-
run!(["tar", "-x", "-f", &task.archive, "-C", temp_dir_path])?;
51-
println!("Patching `cmdline.txt`...");
52-
patch_cmdline(
53-
boot_dir.join("cmdline.txt"),
54-
format!("PARTUUID={disk_id}-05"),
55-
)?;
56-
println!("Patching `config.txt`...");
57-
patch_config(boot_dir.join("config.txt"))?;
43+
let root_dir = TempDir::new("rugpi")?;
44+
let root_dir_path = Utf8Path::from_path(root_dir.path()).unwrap();
45+
let mounted_root = Mounted::mount(loop_device.partition(5), root_dir_path)?;
46+
let mut boot_dir = root_dir_path.join("boot");
47+
let boot_firmware_dir = boot_dir.join("firmware");
48+
if boot_firmware_dir.exists() {
49+
boot_dir = boot_firmware_dir;
5850
}
59-
{
60-
let _mounted_config = Mounted::mount(loop_device.partition(1), temp_dir_path)?;
61-
run!(["cp", "-rTp", "/usr/share/rugpi/files/config", temp_dir_path])?;
62-
match config.include_firmware {
63-
crate::config::IncludeFirmware::None => { /* Do not include any firmware. */ }
64-
crate::config::IncludeFirmware::Pi4 => include_pi4_firmware(&temp_dir_path)?,
65-
crate::config::IncludeFirmware::Pi5 => include_pi5_firmware(&temp_dir_path)?,
66-
}
51+
fs::create_dir_all(&boot_dir)?;
52+
let mounted_boot = Mounted::mount(loop_device.partition(2), &boot_dir)?;
53+
let config_dir = TempDir::new("rugpi")?;
54+
let config_dir_path = Utf8Path::from_path(config_dir.path()).unwrap();
55+
let mounted_config = Mounted::mount(loop_device.partition(1), &config_dir_path)?;
56+
let ctx = BakeCtx {
57+
config,
58+
mounted_boot,
59+
mounted_root,
60+
mounted_config,
61+
};
62+
63+
run!(["tar", "-x", "-f", &task.archive, "-C", root_dir_path])?;
64+
println!("Patching boot configuration...");
65+
patch_boot(ctx.mounted_boot.path(), format!("PARTUUID={disk_id}-05"))?;
66+
println!("Patching `config.txt`...");
67+
patch_config(boot_dir.join("config.txt"))?;
68+
69+
match ctx.config.boot_flow {
70+
BootFlow::Tryboot => setup_tryboot_boot_flow(&ctx)?,
71+
BootFlow::UBoot => setup_uboot_boot_flow(&ctx)?,
72+
}
73+
74+
std::fs::copy(
75+
"/usr/share/rugpi/boot/u-boot/bin/second.scr",
76+
ctx.mounted_boot.path().join("second.scr"),
77+
)?;
78+
79+
match ctx.config.include_firmware {
80+
IncludeFirmware::None => { /* Do not include any firmware. */ }
81+
IncludeFirmware::Pi4 => include_pi4_firmware(&root_dir_path)?,
82+
IncludeFirmware::Pi5 => include_pi5_firmware(&root_dir_path)?,
6783
}
6884
Ok(())
6985
}
7086

87+
struct BakeCtx {
88+
config: BakeryConfig,
89+
mounted_boot: Mounted,
90+
#[allow(unused)]
91+
mounted_root: Mounted,
92+
mounted_config: Mounted,
93+
}
94+
7195
fn calculate_image_size(archive: &Utf8Path) -> Anyhow<u64> {
7296
let archive_bytes = fs::metadata(archive)?.len();
7397
let total_bytes = archive_bytes + (256 + 128 + 128) * 1024 * 1024;
@@ -76,6 +100,54 @@ fn calculate_image_size(archive: &Utf8Path) -> Anyhow<u64> {
76100
Ok(actual_blocks * 4096)
77101
}
78102

103+
fn setup_tryboot_boot_flow(ctx: &BakeCtx) -> Anyhow<()> {
104+
run!([
105+
"cp",
106+
"-rTp",
107+
"/usr/share/rugpi/boot/tryboot",
108+
ctx.mounted_config.path()
109+
])?;
110+
Ok(())
111+
}
112+
113+
fn setup_uboot_boot_flow(ctx: &BakeCtx) -> Anyhow<()> {
114+
run!([
115+
"cp",
116+
"-rTp",
117+
ctx.mounted_boot.path(),
118+
ctx.mounted_config.path()
119+
])?;
120+
std::fs::remove_file(&ctx.mounted_config.path().join("kernel8.img"))?;
121+
std::fs::copy(
122+
"/usr/share/rugpi/boot/u-boot/arm64_config.txt",
123+
ctx.mounted_config.path().join("config.txt"),
124+
)?;
125+
std::fs::copy(
126+
"/usr/share/rugpi/boot/u-boot/bin/u-boot-arm64.bin",
127+
ctx.mounted_config.path().join("u-boot-arm64.bin"),
128+
)?;
129+
std::fs::copy(
130+
"/usr/share/rugpi/boot/u-boot/bin/boot.scr",
131+
ctx.mounted_config.path().join("boot.scr"),
132+
)?;
133+
std::fs::write(ctx.mounted_config.path().join("cmdline.txt"), "")?;
134+
135+
let mut env = UBootEnv::new();
136+
env.set("bootpart", "2".to_owned());
137+
env.save(ctx.mounted_config.path().join("bootpart.default.env"))?;
138+
139+
let mut env = UBootEnv::new();
140+
env.set("boot_spare", "0".to_owned());
141+
env.save(ctx.mounted_config.path().join("boot_spare.disabled.env"))?;
142+
env.save(ctx.mounted_config.path().join("boot_spare.env"))?;
143+
144+
let mut env = UBootEnv::new();
145+
env.set("boot_spare", "1".to_owned());
146+
env.save(ctx.mounted_config.path().join("boot_spare.enabled.env"))?;
147+
148+
Ok(())
149+
}
150+
79151
fn include_pi4_firmware(autoboot_path: &Utf8Path) -> Anyhow<()> {
80152
run!([
81153
"cp",

crates/rugpi-common/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@ homepage.workspace = true
1111
[dependencies]
1212
anyhow.workspace = true
1313
camino.workspace = true
14+
crc32fast = "1.3.2"
1415
indoc = "2.0.3"
16+
serde.workspace = true
17+
thiserror = "1.0.50"
1518
xscript.workspace = true
15 Bytes
Binary file not shown.
15 Bytes
Binary file not shown.

crates/rugpi-common/src/boot.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
pub mod uboot;
4+
5+
/// Rugpi boot flows.
6+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
7+
#[serde(rename_all = "lowercase")]
8+
pub enum BootFlow {
9+
/// Use the `tryboot` feature for booting and partition switching.
10+
#[default]
11+
Tryboot,
12+
/// Use U-Boot for booting and partition switching.
13+
#[serde(rename = "u-boot")]
14+
UBoot,
15+
}
16+
17+
impl BootFlow {
18+
/// The string representation of the boot flow.
19+
pub fn as_str(self) -> &'static str {
20+
match self {
21+
BootFlow::Tryboot => "tryboot",
22+
BootFlow::UBoot => "u-boot",
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)