Merge branch 'merge_bootstrap_into_initfs' into 'master'

Allow embedding bootstrap code into the initfs blob

See merge request redox-os/redox-initfs!2
This commit is contained in:
Jeremy Soller
2024-03-12 20:38:56 +00:00
5 changed files with 77 additions and 8 deletions
+1
View File
@@ -1,4 +1,5 @@
#![no_std]
#![feature(offset_of)]
//! A super simple initfs, only meant to be loaded into RAM by the bootloader, and then directly be
//! read.
+9
View File
@@ -1,3 +1,5 @@
use core::mem::offset_of;
pub const MAGIC_LEN: usize = 8;
pub const MAGIC: [u8; 8] = *b"RedoxFtw";
@@ -69,8 +71,15 @@ pub struct Header {
pub inode_table_offset: Offset,
pub creation_time: Timespec,
pub inode_count: U16,
pub bootstrap_entry: U64,
pub initfs_size: U64,
}
const _: () = {
// Ensure the offsets of field used by the bootloader stay stable.
assert!(offset_of!(Header, bootstrap_entry) == 0x1a);
};
#[repr(packed)]
#[derive(Clone, Copy, Debug)]
pub struct InodeHeader {
+1
View File
@@ -14,6 +14,7 @@ fn archive_and_read() -> Result<()> {
let args = self::archive::Args {
destination_path: Path::new("out.img"),
source: Path::new("data"),
bootstrap_code: None,
max_size: self::archive::DEFAULT_MAX_SIZE,
};
self::archive::archive(&args).context("failed to archive")?;
+58 -8
View File
@@ -355,8 +355,16 @@ pub struct Args<'a> {
pub destination_path: &'a Path,
pub max_size: u64,
pub source: &'a Path,
pub bootstrap_code: Option<&'a Path>,
}
pub fn archive(&Args { destination_path, max_size, source }: &Args) -> Result<()> {
pub fn archive(
&Args {
destination_path,
max_size,
source,
bootstrap_code,
}: &Args,
) -> Result<()> {
let previous_extension = destination_path.extension().map_or("", |ext| {
ext.to_str()
.expect("expected destination path to be valid UTF-8")
@@ -407,15 +415,30 @@ pub fn archive(&Args { destination_path, max_size, source }: &Args) -> Result<()
log::debug!("there are {} inodes", state.inode_count);
// NOTE: The header is always stored at offset zero.
let header_offset = bump_alloc(
&mut state,
std::mem::size_of::<initfs::Header>()
.try_into()
.expect("expected header size to fit"),
"allocate header",
)?;
let header_offset = bump_alloc(&mut state, 4096, "allocate header")?;
assert_eq!(header_offset, 0);
let bootstrap_entry = if let Some(bootstrap_code) = bootstrap_code {
allocate_and_write_file(
&mut state,
&File::open(bootstrap_code).with_context(|| {
anyhow!(
"failed to open bootstrap code file `{}`",
bootstrap_code.to_string_lossy(),
)
})?,
)?;
let bootstrap_data = std::fs::read(bootstrap_code).with_context(|| {
anyhow!(
"failed to read bootstrap code file `{}`",
bootstrap_code.to_string_lossy(),
)
})?;
elf_entry(&bootstrap_data)
} else {
u64::MAX
};
let inode_table_length = {
let inode_entry_size: u64 = std::mem::size_of::<initfs::InodeHeader>()
.try_into()
@@ -463,6 +486,8 @@ pub fn archive(&Args { destination_path, max_size, source }: &Args) -> Result<()
},
inode_count: state.inode_count.into(),
inode_table_offset,
bootstrap_entry: bootstrap_entry.into(),
initfs_size: state.file.metadata().context("failed to get initfs size")?.len().into(),
};
write_all_at(&*state.file, &header_bytes, header_offset, "writing header")
.context("failed to write header")?;
@@ -475,3 +500,28 @@ pub fn archive(&Args { destination_path, max_size, source }: &Args) -> Result<()
Ok(())
}
fn elf_entry(data: &[u8]) -> u64 {
assert!(&data[..4] == b"\x7FELF");
match (data[4], data[5]) {
// 32-bit, little endian
(1, 1) => u32::from_le_bytes(
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
) as u64,
// 32-bit, big endian
(1, 2) => u32::from_be_bytes(
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
) as u64,
// 64-bit, little endian
(2, 1) => u64::from_le_bytes(
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
),
// 64-bit, big endian
(2, 2) => u64::from_be_bytes(
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
),
(ei_class, ei_data) => {
panic!("Unsupported ELF EI_CLASS {} EI_DATA {}", ei_class, ei_data);
}
}
}
+8
View File
@@ -24,6 +24,11 @@ fn main() -> Result<()> {
.required(true)
.help("Specify the source directory to build the image from."),
)
.arg(
Arg::new("BOOTSTRAP_CODE")
.required(false)
.help("Specify the bootstrap ELF file to include in the image.")
)
.arg(
Arg::new("OUTPUT")
.required(true)
@@ -47,12 +52,15 @@ fn main() -> Result<()> {
.get_one::<String>("SOURCE")
.expect("expected the required arg SOURCE to exist");
let bootstrap_code = matches.get_one::<String>("BOOTSTRAP_CODE");
let destination = matches
.get_one::<String>("OUTPUT")
.expect("expected the required arg OUTPUT to exist");
let args = Args {
source: Path::new(source),
bootstrap_code: bootstrap_code.map(|bootstrap_code| Path::new(bootstrap_code)),
destination_path: Path::new(destination),
max_size,
};