mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-12 23:06:51 +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
|
**/*.rs.bk
|
||||||
|
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|
||||||
|
# Ramdisk file
|
||||||
|
src/ramdisk/initramfs/
|
||||||
|
src/ramdisk/build/
|
||||||
|
2
Makefile
2
Makefile
@ -9,6 +9,7 @@ setup:
|
|||||||
@cargo install mdbook
|
@cargo install mdbook
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
@make --no-print-directory -C src/ramdisk
|
||||||
@cd src && cargo kbuild
|
@cd src && cargo kbuild
|
||||||
@cd src && cargo kimage
|
@cd src && cargo kimage
|
||||||
|
|
||||||
@ -33,3 +34,4 @@ check:
|
|||||||
clean:
|
clean:
|
||||||
@cd src && cargo clean
|
@cd src && cargo clean
|
||||||
@cd docs && mdbook 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"}
|
typeflags-util = {path="../typeflags-util"}
|
||||||
jinux-rights-proc = {path="../jinux-rights-proc"}
|
jinux-rights-proc = {path="../jinux-rights-proc"}
|
||||||
jinux-util = {path="../jinux-util"}
|
jinux-util = {path="../jinux-util"}
|
||||||
|
cpio-decoder = {path="../cpio-decoder"}
|
||||||
virtio-input-decoder = "0.1.4"
|
virtio-input-decoder = "0.1.4"
|
||||||
ascii = { version = "1.1", default-features = false, features = ["alloc"] }
|
ascii = { version = "1.1", default-features = false, features = ["alloc"] }
|
||||||
intrusive-collections = "0.9.5"
|
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 {
|
impl From<Error> for jinux_frame::Error {
|
||||||
fn from(error: Error) -> Self {
|
fn from(error: Error) -> Self {
|
||||||
match error.errno {
|
match error.errno {
|
||||||
|
@ -89,6 +89,10 @@ impl InodeHandle_ {
|
|||||||
*offset
|
*offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.dentry.vnode().len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn access_mode(&self) -> AccessMode {
|
pub fn access_mode(&self) -> AccessMode {
|
||||||
self.access_mode
|
self.access_mode
|
||||||
}
|
}
|
||||||
@ -128,6 +132,10 @@ impl<R> InodeHandle<R> {
|
|||||||
self.0.offset()
|
self.0.offset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn access_mode(&self) -> AccessMode {
|
pub fn access_mode(&self) -> AccessMode {
|
||||||
self.0.access_mode()
|
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_handle;
|
||||||
pub mod file_table;
|
pub mod file_table;
|
||||||
pub mod fs_resolver;
|
pub mod fs_resolver;
|
||||||
|
pub mod initramfs;
|
||||||
pub mod ramfs;
|
pub mod ramfs;
|
||||||
pub mod stdio;
|
pub mod stdio;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
@ -55,6 +55,7 @@ pub fn init() {
|
|||||||
driver::init();
|
driver::init();
|
||||||
process::fifo_scheduler::init();
|
process::fifo_scheduler::init();
|
||||||
jinux_frame::enable_interrupts();
|
jinux_frame::enable_interrupts();
|
||||||
|
fs::initramfs::init(read_ramdisk_content()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_thread() {
|
pub fn init_thread() {
|
||||||
@ -79,15 +80,16 @@ pub fn init_thread() {
|
|||||||
println!("[kernel] Running test programs");
|
println!("[kernel] Running test programs");
|
||||||
println!("");
|
println!("");
|
||||||
// Run test apps
|
// Run test apps
|
||||||
for app in get_all_apps().into_iter() {
|
for app in get_all_apps().unwrap().into_iter() {
|
||||||
let UserApp {
|
let UserApp {
|
||||||
elf_path: app_name,
|
elf_path: app_name,
|
||||||
app_content,
|
app_content,
|
||||||
argv,
|
argv,
|
||||||
envp,
|
envp,
|
||||||
} = app;
|
} = app;
|
||||||
|
let app_content = app_content.into_boxed_slice();
|
||||||
println!("[jinux-std/lib.rs] spwan {:?} process", app_name);
|
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
|
// Run busybox ash
|
||||||
@ -96,10 +98,11 @@ pub fn init_thread() {
|
|||||||
app_content,
|
app_content,
|
||||||
argv,
|
argv,
|
||||||
envp,
|
envp,
|
||||||
} = get_busybox_app();
|
} = get_busybox_app().unwrap();
|
||||||
|
let app_content = app_content.into_boxed_slice();
|
||||||
println!("");
|
println!("");
|
||||||
println!("BusyBox v1.35.0 built-in shell (ash)\n");
|
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 {
|
loop {
|
||||||
// We don't have preemptive scheduler now.
|
// 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
|
/// first process never return
|
||||||
#[controlled]
|
#[controlled]
|
||||||
pub fn run_first_process() -> ! {
|
pub fn run_first_process() -> ! {
|
||||||
|
@ -1,21 +1,35 @@
|
|||||||
|
use crate::fs::{
|
||||||
|
fs_resolver::{FsPath, FsResolver},
|
||||||
|
utils::AccessMode,
|
||||||
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub struct UserApp {
|
pub struct UserApp {
|
||||||
pub elf_path: CString,
|
pub elf_path: CString,
|
||||||
pub app_content: &'static [u8],
|
pub app_content: Vec<u8>,
|
||||||
pub argv: Vec<CString>,
|
pub argv: Vec<CString>,
|
||||||
pub envp: Vec<CString>,
|
pub envp: Vec<CString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserApp {
|
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();
|
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,
|
elf_path: app_name,
|
||||||
app_content,
|
app_content,
|
||||||
argv: Vec::new(),
|
argv: Vec::new(),
|
||||||
envp: Vec::new(),
|
envp: Vec::new(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_argv(&mut self, argv: Vec<CString>) {
|
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);
|
let mut res = Vec::with_capacity(16);
|
||||||
|
|
||||||
// Most simple hello world, written in assembly
|
// 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);
|
res.push(asm_hello_world);
|
||||||
|
|
||||||
// Hello world, written in C language.
|
// Hello world, written in C language.
|
||||||
// Since glibc requires the elf path starts with "/", and we don't have filesystem now.
|
// 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.
|
// 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);
|
res.push(hello_c);
|
||||||
|
|
||||||
// Fork process, written in assembly
|
// 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);
|
res.push(asm_fork);
|
||||||
|
|
||||||
// Execve, written in C language.
|
// 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);
|
res.push(execve_c);
|
||||||
|
|
||||||
// Fork new process, written in C language. (Fork in glibc uses syscall clone actually)
|
// 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);
|
res.push(fork_c);
|
||||||
|
|
||||||
// signal test
|
// 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);
|
res.push(signal_test);
|
||||||
|
|
||||||
// pthread 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.push(pthread_test);
|
||||||
|
|
||||||
res
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_busybox_app() -> UserApp {
|
pub fn get_busybox_app() -> Result<UserApp> {
|
||||||
// busybox
|
// 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
|
// -l option means the busybox is running as logging shell
|
||||||
let argv = ["/busybox", "sh", "-l"];
|
let argv = ["/busybox", "sh", "-l"];
|
||||||
let envp = [
|
let envp = [
|
||||||
@ -78,23 +92,11 @@ pub fn get_busybox_app() -> UserApp {
|
|||||||
"OLDPWD=/",
|
"OLDPWD=/",
|
||||||
];
|
];
|
||||||
|
|
||||||
let argv = to_vec_cstring(&argv).unwrap();
|
let argv = to_vec_cstring(&argv)?;
|
||||||
let envp = to_vec_cstring(&envp).unwrap();
|
let envp = to_vec_cstring(&envp)?;
|
||||||
busybox.set_argv(argv);
|
busybox.set_argv(argv);
|
||||||
busybox.set_envp(envp);
|
busybox.set_envp(envp);
|
||||||
busybox
|
Ok(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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_execve_content() -> &'static [u8] {
|
fn read_execve_content() -> &'static [u8] {
|
||||||
@ -105,22 +107,6 @@ pub fn read_execve_hello_content() -> &'static [u8] {
|
|||||||
include_bytes!("../../../../apps/execve/hello")
|
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>> {
|
fn to_vec_cstring(raw_strs: &[&str]) -> Result<Vec<CString>> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for raw_str in raw_strs {
|
for raw_str in raw_strs {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user