More work on EFI boot records and kernel updates

This commit is contained in:
2026-09-04 17:57:13 +02:00
parent c61d283177
commit f35c274e50
3 changed files with 196 additions and 35 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ pub struct HiveConfig {
#[derive(Debug, Serialize, Deserialize)]
pub struct KernelConfig {
/// kernel cmd string that will be passed as kernel boot parameter.
cmd: String,
pub cmd: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
+165 -20
View File
@@ -20,15 +20,19 @@
use std::fs;
use std::process::Command;
use std::vec;
use time_format;
const SNAPS_DIR: &str = "/bzz_snaps";
const SNAPS_DIR: &str = "/bzz-snaps";
const EFI_BOOT_MOUNT_POINT: &str = "/beefi";
const UKI_IMAGE_NAME: &str = "/beefi/EFI/kira-linux-lts.efi";
const UKI_IMAGE_BACK_NAME: &str = "/beefi/EFI/kira-linux-lts-back.efi";
const EFI_PARTLABEL: &str = "bee-efi";
const BOOT_MENU_LABEL: &str = "Kira OS";
const UKI_IMAGE_NAME: &str = "\\EFI\\kira-linux-lts.efi";
const BOOT_MENU_LABEL: &str = "Kira OS";
const BOOT_MENU_LABEL_BACK: &str = "Kira OS Fallback";
const BOOT_UKI_IMAGE_NAME: &str = "\\EFI\\kira-linux-lts.efi";
const BOOT_UKI_IMAGE_NAME_BACK: &str = "\\EFI\\kira-linux-lts-back.efi";
#[derive(Debug)]
pub struct CommandFailed {
@@ -309,7 +313,6 @@ pub fn find_partlabel(partlabel: &str) -> Result<Option<String>, ExecCommandErro
}
}
///
/// We need to use lsblk to find out parent device, or read topology from sysfs
/// (basename $(readlink -f /sys/class/block/sda1/..))
@@ -319,8 +322,11 @@ pub fn find_partlabel(partlabel: &str) -> Result<Option<String>, ExecCommandErro
/// Returns: touple of partition number and parent device as Strings (part_num, par_dev)
/// Returns Error if no efi partition was found
///
fn get_efi_part_info() -> Result<(String,String), ExecCommandError> {
/// We need more complex logic for it.
/// Need to alterate between two boot records.
///
///
fn get_efi_part_info() -> Result<(String, String), ExecCommandError> {
use regex::Regex;
let re = Regex::new(r#"^PARTN="([0-9]+)" PKNAME="([0-9a-Z]+)"$"#).unwrap();
@@ -332,29 +338,77 @@ fn get_efi_part_info() -> Result<(String,String), ExecCommandError> {
// let part_no = captured[1];
// let parent_dev = captured[2];
Ok((captured[1].to_string(), captured[2].to_string()))
} else {
Err(ExecCommandError::Other(format!(
"No EFI partition (with PARTLABEL={}) was found!",
EFI_PARTLABEL
)))
}
else {
Err(ExecCommandError::Other(format!("No EFI partition (with PARTLABEL={}) was found!", EFI_PARTLABEL)))
}
}
///
/// efibootmgr
/// List all boot entries hex numbers
/// Boot0000* Nyan
///
fn list_efi_bootnums() -> Result<Vec<String>, ExecCommandError> {
use regex::Regex;
let re = Regex::new(r#"^Boot([0-9A-F]{4})\* "#).unwrap();
let str_res = exec_command_out("efibootmgr", &vec![])?;
// for each line in output match bootnum
// and collect to vector
Ok(str_res
.lines()
.filter_map(|hs| re.captures(hs))
.map(|captured| captured[1].to_string())
.collect())
}
///
/// sudo efibootmgr -B -b 0001
/// -B (--delete-bootnum)
///
fn delete_efi_bootnum(bootnum: &str) -> Result<(), ExecCommandError> {
exec_command("efibootmgr", &vec!["-B", "-b", bootnum])
}
///
/// Delets all EFI bootnums
/// Returns number of deleted bootnums on success
///
fn delete_all_efi_bootnums() -> Result<usize, ExecCommandError> {
let bootnums = list_efi_bootnums()?;
for bootnum in &bootnums {
delete_efi_bootnum(bootnum)?;
}
Ok(bootnums.len())
}
///
/// Creates EFI boot record
/// boot_index - inserting it at specified boot index (bootorder)
/// Assuming boot partition has "bee-efi" PARTLABEL
///
/// Generaly lets just add new record with date. Label modification not allways works?
/// May add deletion of old record latel
/// sudo efibootmgr --create --disk /dev/nvme0n1 --part 1 --label "Kira OS" --loader '\EFI\nyanlinux\kira-bzz.efi' --unicode 'initrd=\EFI\initrd.img'
pub fn boot_record_update(maybe_kernel_cmd: Option<&str>) -> Result<(), ExecCommandError> {
fn efi_boot_record_create(boot_index: u16, boot_label: &str, uki_path: &str, maybe_kernel_cmd: &Option<String>) -> Result<(), ExecCommandError> {
let (efi_partno, efi_parent_dev) = get_efi_part_info()?;
let local_date = time_format::strftime_local("%Y-%m-%d", time_format::now().unwrap()).unwrap();
let boot_label = format!("{} {}", BOOT_MENU_LABEL, local_date);
//let local_date = time_format::strftime_local("%Y-%m-%d", time_format::now().unwrap()).unwrap();
let string_boot_index = boot_index.to_string();
let mut efibootmgr_args: Vec<&str> = vec!["--create", "--disk", &efi_parent_dev, "--part", &efi_partno, "--label", &boot_label, "--loader", UKI_IMAGE_NAME];
let mut efibootmgr_args: Vec<&str> = vec![
"--create",
"--index",
&string_boot_index,
"--disk",
&efi_parent_dev,
"--part",
&efi_partno,
"--label",
boot_label,
"--loader",
uki_path,
];
if let Some(kernel_cmd) = maybe_kernel_cmd {
efibootmgr_args.push("--unicode");
efibootmgr_args.push(kernel_cmd);
@@ -362,5 +416,96 @@ pub fn boot_record_update(maybe_kernel_cmd: Option<&str>) -> Result<(), ExecComm
exec_command("efibootmgr", &efibootmgr_args)?;
Ok(())
}
///
/// Deletes all existing boot records and create a default
///
pub fn init_efi_boot_record(maybe_kernel_cmd: &Option<String>) -> Result<(), ExecCommandError> {
// 1 delete all bootnums
delete_all_efi_bootnums()?;
// 2 create defaul
efi_boot_record_create(0, BOOT_MENU_LABEL, BOOT_UKI_IMAGE_NAME, maybe_kernel_cmd)?;
efi_boot_record_create(1, BOOT_MENU_LABEL_BACK, BOOT_UKI_IMAGE_NAME_BACK, maybe_kernel_cmd)?;
Ok(())
}
///
/// List installed kernels by inspecting /usr/lib/modules directory
/// Result is a sorted vector of string
///
fn list_installed_kernels() -> Result<Vec<String>, ExecCommandError> {
use regex::Regex;
let re = Regex::new(r#"^[0-9]+\.[0-9]+\.[0-9]+-[0-9]-kira-lts"#).unwrap();
let mut kernel_dirs: Vec<String> = Vec::new();
// Oh boy we roll
// finding first dir in /usr/lib/modules that matches common kernel name
for r_d_entry in fs::read_dir("/usr/lib/modules")? {
let d_entry = r_d_entry?;
if d_entry.file_type()?.is_dir() {
let maybe_file_name = d_entry.file_name().to_str().map(|s|s.to_string());
if let Some(file_name) = maybe_file_name {
if re.is_match(&file_name) {
kernel_dirs.push(file_name);
}
}
}
}
kernel_dirs.sort();
Ok(kernel_dirs)
}
pub fn init_efi_boot_partition_content() -> Result<(), ExecCommandError> {
let kernel_dirs = list_installed_kernels()?;
// check if kernel even exist lol
if let Some(kernel_dir) = kernel_dirs.last() {
let kernel_img_path = format!("/usr/lib/modules/{}/vmlinuz", kernel_dir);
let efi_dir = format!("{}/EFI", EFI_BOOT_MOUNT_POINT);
println!("Romoving efi dir {}",efi_dir);
fs::remove_dir_all(&efi_dir)?;
println!("Creating efi dir {}",efi_dir);
fs::create_dir(efi_dir)?;
println!("Copy {} to {}", kernel_img_path, UKI_IMAGE_NAME);
fs::copy(&kernel_img_path, UKI_IMAGE_NAME)?;
println!("Copy {} to {}", kernel_img_path, UKI_IMAGE_BACK_NAME);
fs::copy(&kernel_img_path, UKI_IMAGE_BACK_NAME)?;
Ok(())
}
else {
Err(ExecCommandError::Other("Cannot find kernel files under /usr/lib/modules dir!".into()))
}
}
///
/// Ok. Keep it simple kira.
/// Lets put new kernel as kira-linux-lts.efi to EFI part.
/// Should we save old kernel? Somehow.
/// lets rename kira-linux-lts.efi to kira-linux-lts-back.efi
/// Lets do not do much else.
/// sudo cp /usr/lib/modules/6.18.46-1-kira-lts/vmlinuz /beefi/EFI/kira-linux-lts.efi
///
pub fn boot_update() -> Result<(), ExecCommandError> {
// find vmlinuz path in filesystem
let kernel_dirs = list_installed_kernels()?;
// check if kernel even exist lol
if let Some(kernel_dir) = kernel_dirs.last() {
let kernel_img_path = format!("/usr/lib/modules/{}/vmlinuz", kernel_dir);
// rename old kernel to
println!("Renaming old kernel {} to {}",UKI_IMAGE_NAME,UKI_IMAGE_BACK_NAME);
fs::rename(UKI_IMAGE_NAME, UKI_IMAGE_BACK_NAME)?;
// copy new kernel image
println!("Copy new kernel from {} to {}",kernel_img_path,UKI_IMAGE_NAME);
fs::copy(&kernel_img_path, UKI_IMAGE_NAME)?;
Ok(())
}
else {
Err(ExecCommandError::Other("Cannot find kernel files under /usr/lib/modules dir!".into()))
}
}
+19 -3
View File
@@ -15,10 +15,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use clap::{Args, Parser, Subcommand};
use signal_hook::{consts::SIGINT, iterator::Signals};
use signal_hook;
use std;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::{AtomicBool};
mod hive_system;
@@ -129,6 +129,12 @@ struct CliArgs {
/// Install new kernel into boot partition
#[arg(long)]
update_boot: bool,
/// Cleans up and recreates UFI boot menu
#[arg(long)]
init_boot_menu: bool,
/// Cleans up and recreates UFI boot menu and /EFI dir content
#[arg(long)]
init_boot: bool,
}
#[derive(Subcommand, Debug)]
@@ -198,7 +204,17 @@ fn main() -> Result<(), HiveUpError> {
if cli_args.upgrade {
cli_upgrade(bzz_config)?;
} else if cli_args.update_boot {
println!("Updating boot...");
hive_system::boot_update()?;
} else if cli_args.init_boot_menu {
println!("Init EFI boot menu...");
hive_system::init_efi_boot_record(&bzz_config.kernel.cmd)?;
} else if cli_args.init_boot {
println!("Init EFI boot menu and EFI partition content...");
println!("Init EFI Partition content...");
hive_system::init_efi_boot_record(&bzz_config.kernel.cmd)?;
println!("Init EFI boot menu...");
hive_system::init_efi_boot_partition_content()?;
}
}
HiveUpMode::Remote(remote_args) => {