mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Add support to prepare the initramfs from CPIO file
This commit is contained in:
parent
ece1a545ce
commit
7f80df621d
4
.gitignore
vendored
4
.gitignore
vendored
@ -11,3 +11,7 @@ target/
|
||||
**/*.rs.bk
|
||||
|
||||
**/.DS_Store
|
||||
|
||||
# Ramdisk file
|
||||
src/ramdisk/initramfs/
|
||||
src/ramdisk/build/
|
||||
|
2
Makefile
2
Makefile
@ -9,6 +9,7 @@ setup:
|
||||
@cargo install mdbook
|
||||
|
||||
build:
|
||||
@make --no-print-directory -C src/ramdisk
|
||||
@cd src && cargo kbuild
|
||||
@cd src && cargo kimage
|
||||
|
||||
@ -33,3 +34,4 @@ check:
|
||||
clean:
|
||||
@cd src && cargo clean
|
||||
@cd docs && mdbook clean
|
||||
@make --no-print-directory -C src/ramdisk clean
|
||||
|
33
src/ramdisk/Makefile
Normal file
33
src/ramdisk/Makefile
Normal file
@ -0,0 +1,33 @@
|
||||
APPS := ../apps
|
||||
BUILD_DIR := ./build
|
||||
INITRAMFS := ./initramfs
|
||||
RAMDISK := $(BUILD_DIR)/ramdisk.cpio
|
||||
SHELL := /bin/bash
|
||||
|
||||
ifneq (, $(wildcard $(APPS)/. ))
|
||||
APPS_DIRS := $(shell find $(APPS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
|
||||
APPS_FILES := $(shell find $(APPS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
|
||||
endif
|
||||
|
||||
ifneq (, $(wildcard $(INITRAMFS)/. ))
|
||||
INITRAMFS_DIRS := $(shell find $(INITRAMFS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
|
||||
INITRAMFS_FILES := $(shell find $(INITRAMFS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
|
||||
endif
|
||||
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(RAMDISK)
|
||||
|
||||
$(INITRAMFS): $(APPS) $(APPS_DIRS) $(APPS_FILES)
|
||||
@mkdir -p $@
|
||||
@cp -a $(APPS)/* $@
|
||||
@cd $@ && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete
|
||||
|
||||
$(RAMDISK): $(INITRAMFS) $(INITRAMFS_DIRS) $(INITRAMFS_FILES)
|
||||
@echo "Generating the ramdisk image..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@./mkinitramfs $(INITRAMFS) $@
|
||||
|
||||
clean:
|
||||
@rm -rf $(INITRAMFS) $(BUILD_DIR)
|
15
src/ramdisk/mkinitramfs
Executable file
15
src/ramdisk/mkinitramfs
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: mkinitramfs <dir> <cpio>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "$1" ]; then
|
||||
echo "Creating $2 from $1"
|
||||
(cd "$1"; find . | cpio -o -H newc) > "$2"
|
||||
else
|
||||
echo "The first argument must be a directory"
|
||||
exit 1
|
||||
fi
|
@ -16,6 +16,7 @@ typeflags = {path="../typeflags"}
|
||||
typeflags-util = {path="../typeflags-util"}
|
||||
jinux-rights-proc = {path="../jinux-rights-proc"}
|
||||
jinux-util = {path="../jinux-util"}
|
||||
cpio-decoder = {path="../cpio-decoder"}
|
||||
virtio-input-decoder = "0.1.4"
|
||||
ascii = { version = "1.1", default-features = false, features = ["alloc"] }
|
||||
intrusive-collections = "0.9.5"
|
||||
|
@ -211,6 +211,31 @@ impl From<core::ffi::FromBytesWithNulError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cpio_decoder::error::Error> for Error {
|
||||
fn from(cpio_error: cpio_decoder::error::Error) -> Self {
|
||||
match cpio_error {
|
||||
cpio_decoder::error::Error::MagicError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid magic number")
|
||||
}
|
||||
cpio_decoder::error::Error::Utf8Error => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid utf-8 string")
|
||||
}
|
||||
cpio_decoder::error::Error::ParseIntError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO parse int error")
|
||||
}
|
||||
cpio_decoder::error::Error::FileTypeError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid file type")
|
||||
}
|
||||
cpio_decoder::error::Error::FileNameError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid file name")
|
||||
}
|
||||
cpio_decoder::error::Error::BufferShortError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO buffer is too short")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for jinux_frame::Error {
|
||||
fn from(error: Error) -> Self {
|
||||
match error.errno {
|
||||
|
@ -89,6 +89,10 @@ impl InodeHandle_ {
|
||||
*offset
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.dentry.vnode().len()
|
||||
}
|
||||
|
||||
pub fn access_mode(&self) -> AccessMode {
|
||||
self.access_mode
|
||||
}
|
||||
@ -128,6 +132,10 @@ impl<R> InodeHandle<R> {
|
||||
self.0.offset()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn access_mode(&self) -> AccessMode {
|
||||
self.0.access_mode()
|
||||
}
|
||||
|
55
src/services/libs/jinux-std/src/fs/initramfs.rs
Normal file
55
src/services/libs/jinux-std/src/fs/initramfs.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::fs_resolver::{FsPath, FsResolver};
|
||||
use super::utils::{InodeMode, InodeType};
|
||||
use cpio_decoder::{CpioDecoder, FileType};
|
||||
|
||||
/// Unpack and prepare the fs from the ramdisk CPIO buffer.
|
||||
pub fn init(ramdisk_buf: &[u8]) -> Result<()> {
|
||||
let decoder = CpioDecoder::new(ramdisk_buf);
|
||||
let fs = FsResolver::new();
|
||||
for entry_result in decoder.decode_entries() {
|
||||
let entry = entry_result?;
|
||||
|
||||
// Make sure the name is a relative path, and is not end with "/".
|
||||
let entry_name = entry.name().trim_start_matches('/').trim_end_matches('/');
|
||||
if entry_name.is_empty() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid entry name");
|
||||
}
|
||||
if entry_name == "." {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Here we assume that the directory referred by "prefix" must has been created.
|
||||
// The basis of this assumption is:
|
||||
// The mkinitramfs script uses `find` command to ensure that the entries are
|
||||
// sorted that a directory always appears before its child directories and files.
|
||||
let (parent, name) = if let Some((prefix, last)) = entry_name.rsplit_once('/') {
|
||||
(fs.lookup(&FsPath::try_from(prefix)?)?, last)
|
||||
} else {
|
||||
(fs.root().clone(), entry_name)
|
||||
};
|
||||
|
||||
let metadata = entry.metadata();
|
||||
let mode = InodeMode::from_bits_truncate(metadata.permission_mode());
|
||||
match metadata.file_type() {
|
||||
FileType::File => {
|
||||
let dentry = parent.create(name, InodeType::File, mode)?;
|
||||
dentry.vnode().write_at(0, entry.data())?;
|
||||
}
|
||||
FileType::Dir => {
|
||||
let _ = parent.create(name, InodeType::Dir, mode)?;
|
||||
}
|
||||
FileType::Link => {
|
||||
let dentry = parent.create(name, InodeType::SymLink, mode)?;
|
||||
let link_content = core::str::from_utf8(entry.data())?;
|
||||
dentry.vnode().write_link(link_content)?;
|
||||
}
|
||||
type_ => {
|
||||
warn!("unsupported file type = {:?} in initramfs", type_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
pub mod file_handle;
|
||||
pub mod file_table;
|
||||
pub mod fs_resolver;
|
||||
pub mod initramfs;
|
||||
pub mod ramfs;
|
||||
pub mod stdio;
|
||||
pub mod utils;
|
||||
|
@ -55,6 +55,7 @@ pub fn init() {
|
||||
driver::init();
|
||||
process::fifo_scheduler::init();
|
||||
jinux_frame::enable_interrupts();
|
||||
fs::initramfs::init(read_ramdisk_content()).unwrap();
|
||||
}
|
||||
|
||||
pub fn init_thread() {
|
||||
@ -79,15 +80,16 @@ pub fn init_thread() {
|
||||
println!("[kernel] Running test programs");
|
||||
println!("");
|
||||
// Run test apps
|
||||
for app in get_all_apps().into_iter() {
|
||||
for app in get_all_apps().unwrap().into_iter() {
|
||||
let UserApp {
|
||||
elf_path: app_name,
|
||||
app_content,
|
||||
argv,
|
||||
envp,
|
||||
} = app;
|
||||
let app_content = app_content.into_boxed_slice();
|
||||
println!("[jinux-std/lib.rs] spwan {:?} process", app_name);
|
||||
Process::spawn_user_process(app_name.clone(), app_content, argv, Vec::new());
|
||||
Process::spawn_user_process(app_name.clone(), Box::leak(app_content), argv, Vec::new());
|
||||
}
|
||||
|
||||
// Run busybox ash
|
||||
@ -96,10 +98,11 @@ pub fn init_thread() {
|
||||
app_content,
|
||||
argv,
|
||||
envp,
|
||||
} = get_busybox_app();
|
||||
} = get_busybox_app().unwrap();
|
||||
let app_content = app_content.into_boxed_slice();
|
||||
println!("");
|
||||
println!("BusyBox v1.35.0 built-in shell (ash)\n");
|
||||
Process::spawn_user_process(app_name.clone(), app_content, argv, Vec::new());
|
||||
Process::spawn_user_process(app_name.clone(), Box::leak(app_content), argv, Vec::new());
|
||||
|
||||
loop {
|
||||
// We don't have preemptive scheduler now.
|
||||
@ -108,6 +111,10 @@ pub fn init_thread() {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ramdisk_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../ramdisk/build/ramdisk.cpio")
|
||||
}
|
||||
|
||||
/// first process never return
|
||||
#[controlled]
|
||||
pub fn run_first_process() -> ! {
|
||||
|
@ -1,21 +1,35 @@
|
||||
use crate::fs::{
|
||||
fs_resolver::{FsPath, FsResolver},
|
||||
utils::AccessMode,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct UserApp {
|
||||
pub elf_path: CString,
|
||||
pub app_content: &'static [u8],
|
||||
pub app_content: Vec<u8>,
|
||||
pub argv: Vec<CString>,
|
||||
pub envp: Vec<CString>,
|
||||
}
|
||||
|
||||
impl UserApp {
|
||||
pub fn new(elf_path: &str, app_content: &'static [u8]) -> Self {
|
||||
pub fn new(elf_path: &str) -> Result<Self> {
|
||||
let app_name = CString::new(elf_path).unwrap();
|
||||
UserApp {
|
||||
let app_content = {
|
||||
let fs = FsResolver::new();
|
||||
let file = fs.open(&FsPath::try_from(elf_path)?, AccessMode::O_RDONLY as u32, 0)?;
|
||||
let mut content = Vec::new();
|
||||
let len = file.read_to_end(&mut content)?;
|
||||
if len != file.len() {
|
||||
return_errno_with_message!(Errno::EINVAL, "read len is not equal to file size");
|
||||
}
|
||||
content
|
||||
};
|
||||
Ok(UserApp {
|
||||
elf_path: app_name,
|
||||
app_content,
|
||||
argv: Vec::new(),
|
||||
envp: Vec::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_argv(&mut self, argv: Vec<CString>) {
|
||||
@ -27,45 +41,45 @@ impl UserApp {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_apps() -> Vec<UserApp> {
|
||||
pub fn get_all_apps() -> Result<Vec<UserApp>> {
|
||||
let mut res = Vec::with_capacity(16);
|
||||
|
||||
// Most simple hello world, written in assembly
|
||||
let asm_hello_world = UserApp::new("hello_world", read_hello_world_content());
|
||||
let asm_hello_world = UserApp::new("hello_world/hello_world")?;
|
||||
res.push(asm_hello_world);
|
||||
|
||||
// Hello world, written in C language.
|
||||
// Since glibc requires the elf path starts with "/", and we don't have filesystem now.
|
||||
// So we manually add a leading "/" for app written in C language.
|
||||
let hello_c = UserApp::new("/hello_c", read_hello_c_content());
|
||||
let hello_c = UserApp::new("/hello_c/hello")?;
|
||||
res.push(hello_c);
|
||||
|
||||
// Fork process, written in assembly
|
||||
let asm_fork = UserApp::new("fork", read_fork_content());
|
||||
let asm_fork = UserApp::new("fork/fork")?;
|
||||
res.push(asm_fork);
|
||||
|
||||
// Execve, written in C language.
|
||||
let execve_c = UserApp::new("/execve", read_execve_content());
|
||||
let execve_c = UserApp::new("/execve/execve")?;
|
||||
res.push(execve_c);
|
||||
|
||||
// Fork new process, written in C language. (Fork in glibc uses syscall clone actually)
|
||||
let fork_c = UserApp::new("/fork", read_fork_c_content());
|
||||
let fork_c = UserApp::new("/fork_c/fork")?;
|
||||
res.push(fork_c);
|
||||
|
||||
// signal test
|
||||
let signal_test = UserApp::new("/signal_test", read_signal_test_content());
|
||||
let signal_test = UserApp::new("/signal_c/signal_test")?;
|
||||
res.push(signal_test);
|
||||
|
||||
// pthread test
|
||||
let pthread_test = UserApp::new("/pthread_test", read_pthread_test_content());
|
||||
let pthread_test = UserApp::new("/pthread/pthread_test")?;
|
||||
res.push(pthread_test);
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn get_busybox_app() -> UserApp {
|
||||
pub fn get_busybox_app() -> Result<UserApp> {
|
||||
// busybox
|
||||
let mut busybox = UserApp::new("/busybox", read_busybox_content());
|
||||
let mut busybox = UserApp::new("/busybox/busybox")?;
|
||||
// -l option means the busybox is running as logging shell
|
||||
let argv = ["/busybox", "sh", "-l"];
|
||||
let envp = [
|
||||
@ -78,23 +92,11 @@ pub fn get_busybox_app() -> UserApp {
|
||||
"OLDPWD=/",
|
||||
];
|
||||
|
||||
let argv = to_vec_cstring(&argv).unwrap();
|
||||
let envp = to_vec_cstring(&envp).unwrap();
|
||||
let argv = to_vec_cstring(&argv)?;
|
||||
let envp = to_vec_cstring(&envp)?;
|
||||
busybox.set_argv(argv);
|
||||
busybox.set_envp(envp);
|
||||
busybox
|
||||
}
|
||||
|
||||
fn read_hello_world_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/hello_world/hello_world")
|
||||
}
|
||||
|
||||
fn read_hello_c_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/hello_c/hello")
|
||||
}
|
||||
|
||||
fn read_fork_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/fork/fork")
|
||||
Ok(busybox)
|
||||
}
|
||||
|
||||
fn read_execve_content() -> &'static [u8] {
|
||||
@ -105,22 +107,6 @@ pub fn read_execve_hello_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/execve/hello")
|
||||
}
|
||||
|
||||
fn read_fork_c_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/fork_c/fork")
|
||||
}
|
||||
|
||||
fn read_signal_test_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/signal_c/signal_test")
|
||||
}
|
||||
|
||||
fn read_pthread_test_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/pthread/pthread_test")
|
||||
}
|
||||
|
||||
fn read_busybox_content() -> &'static [u8] {
|
||||
include_bytes!("../../../../apps/busybox/busybox")
|
||||
}
|
||||
|
||||
fn to_vec_cstring(raw_strs: &[&str]) -> Result<Vec<CString>> {
|
||||
let mut res = Vec::new();
|
||||
for raw_str in raw_strs {
|
||||
|
Loading…
x
Reference in New Issue
Block a user