diff --git a/.gitignore b/.gitignore
index 5ecbcc3fb..a9e70705c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,7 @@ target/
**/*.rs.bk
**/.DS_Store
+
+# Ramdisk file
+src/ramdisk/initramfs/
+src/ramdisk/build/
diff --git a/Makefile b/Makefile
index 5f583fe97..426a2f8d4 100644
--- a/Makefile
+++ b/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
diff --git a/src/ramdisk/Makefile b/src/ramdisk/Makefile
new file mode 100644
index 000000000..f2e12bcba
--- /dev/null
+++ b/src/ramdisk/Makefile
@@ -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)
diff --git a/src/ramdisk/mkinitramfs b/src/ramdisk/mkinitramfs
new file mode 100755
index 000000000..52b8fc549
--- /dev/null
+++ b/src/ramdisk/mkinitramfs
@@ -0,0 +1,15 @@
+#!/bin/bash
+set -e
+
+if [ $# -ne 2 ]; then
+ echo "Usage: mkinitramfs
"
+ 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
diff --git a/src/services/libs/jinux-std/Cargo.toml b/src/services/libs/jinux-std/Cargo.toml
index 31202f4ef..5655a2e06 100644
--- a/src/services/libs/jinux-std/Cargo.toml
+++ b/src/services/libs/jinux-std/Cargo.toml
@@ -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"
diff --git a/src/services/libs/jinux-std/src/error.rs b/src/services/libs/jinux-std/src/error.rs
index 15bf0a26e..06516b5cc 100644
--- a/src/services/libs/jinux-std/src/error.rs
+++ b/src/services/libs/jinux-std/src/error.rs
@@ -211,6 +211,31 @@ impl From for Error {
}
}
+impl From 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 for jinux_frame::Error {
fn from(error: Error) -> Self {
match error.errno {
diff --git a/src/services/libs/jinux-std/src/fs/file_handle/inode_handle/mod.rs b/src/services/libs/jinux-std/src/fs/file_handle/inode_handle/mod.rs
index 61b287a3d..a5132b6c8 100644
--- a/src/services/libs/jinux-std/src/fs/file_handle/inode_handle/mod.rs
+++ b/src/services/libs/jinux-std/src/fs/file_handle/inode_handle/mod.rs
@@ -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 InodeHandle {
self.0.offset()
}
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
pub fn access_mode(&self) -> AccessMode {
self.0.access_mode()
}
diff --git a/src/services/libs/jinux-std/src/fs/initramfs.rs b/src/services/libs/jinux-std/src/fs/initramfs.rs
new file mode 100644
index 000000000..430a41889
--- /dev/null
+++ b/src/services/libs/jinux-std/src/fs/initramfs.rs
@@ -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(())
+}
diff --git a/src/services/libs/jinux-std/src/fs/mod.rs b/src/services/libs/jinux-std/src/fs/mod.rs
index 77018bc58..ed7305f6a 100644
--- a/src/services/libs/jinux-std/src/fs/mod.rs
+++ b/src/services/libs/jinux-std/src/fs/mod.rs
@@ -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;
diff --git a/src/services/libs/jinux-std/src/lib.rs b/src/services/libs/jinux-std/src/lib.rs
index 6088d441f..cb046e549 100644
--- a/src/services/libs/jinux-std/src/lib.rs
+++ b/src/services/libs/jinux-std/src/lib.rs
@@ -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() -> ! {
diff --git a/src/services/libs/jinux-std/src/user_apps.rs b/src/services/libs/jinux-std/src/user_apps.rs
index bba25e2c7..bd17b5eee 100644
--- a/src/services/libs/jinux-std/src/user_apps.rs
+++ b/src/services/libs/jinux-std/src/user_apps.rs
@@ -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,
pub argv: Vec,
pub envp: Vec,
}
impl UserApp {
- pub fn new(elf_path: &str, app_content: &'static [u8]) -> Self {
+ pub fn new(elf_path: &str) -> Result {
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) {
@@ -27,45 +41,45 @@ impl UserApp {
}
}
-pub fn get_all_apps() -> Vec {
+pub fn get_all_apps() -> Result> {
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 {
// 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> {
let mut res = Vec::new();
for raw_str in raw_strs {