From 5def9208170b3aa78fbab1222f440496e11734bc Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 6 Jan 2021 08:33:28 -0800 Subject: [PATCH] Create an `init` process in Rust that can mount the initial file systems appropriately. --- .gitignore | 4 +- Cargo.toml | 11 ++++ README.md | 48 ++++++++++++++++ docker/Dockerfile.kernel | 2 +- docker/Dockerfile.rust | 12 ++++ docker/buildramfs.sh | 36 ++---------- scripts/build-docker-envs.sh | 4 ++ scripts/build-ramfs.sh | 12 +++- scripts/rust-build.sh | 3 + scripts/rust-clippy.sh | 3 + src/bin/init.rs | 106 +++++++++++++++++++++++++++++++++++ 11 files changed, 205 insertions(+), 36 deletions(-) create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 docker/Dockerfile.rust create mode 100755 scripts/build-docker-envs.sh create mode 100755 scripts/rust-build.sh create mode 100755 scripts/rust-clippy.sh create mode 100644 src/bin/init.rs diff --git a/.gitignore b/.gitignore index 7946141..20fdcc5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ old/ tmp/ -artifacts/ \ No newline at end of file +artifacts/ +target/ +Cargo.lock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..edb439c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lam" +version = "0.1.0" +authors = ["Adam Wick "] +edition = "2018" + +[dependencies] +libc = "0.2.81" +log = "0.4.11" +simple_logger = "1.11.0" +thiserror = "1.0.23" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..01fa357 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Introduction + +The repository is an exploration of the question: what if we wanted to use +the Linux kernel as a poor man's microkernel? Microkernels are cool and all, +but figuring out how to deal with all the device drivers is a bit soul +crushing. So here, we're going to skip over those details, and see what it'd +look like if we just built everything else: the network stack, the file +systems, the graphical infrastructure, virtualization, etc. And let's use +Rust to do it, because I'm a fanboy. And let's not keep ourselves to +POSIX and standard Linux file system organization, because why bother? + +# To Hit Go + +Well, you'll need [Docker](https://www.docker.com/products/docker-desktop). + +You'll need to use a fancy Rust Docker container or two. Run this: + +``` +% ./scripts/build-docker-envs.sh +``` + +To build your handy-dandy Docker containers. + +To build the linux kernel, which you'll need, you should run: + +``` +% ./scripts/build-kernel.sh +``` + +You'll then want to run the Rust build: + +``` +% cargo build --release --target x86_65-unknown-linux-musl +``` + +And then build this all up into a nice ramdisk: + +``` +% ./scripts/build-ramfs.sh +``` + +And run the demo: + +``` +% ./scripts/run.sh +``` + +Wheee!! Fun!! \ No newline at end of file diff --git a/docker/Dockerfile.kernel b/docker/Dockerfile.kernel index 4fa06d6..41c5be8 100644 --- a/docker/Dockerfile.kernel +++ b/docker/Dockerfile.kernel @@ -20,5 +20,5 @@ RUN dnf install -y \ RUN mkdir -p /build/ramfs RUN curl https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${KERNEL_VERSION}.tar.xz | tar xJC /build RUN mv /build/linux* /build/kernel -COPY buildramfs.sh /build/ramfs/buildramfs.sh COPY lam.config /build/kernel/.config +COPY buildramfs.sh /build/ramfs/buildramfs.sh diff --git a/docker/Dockerfile.rust b/docker/Dockerfile.rust new file mode 100644 index 0000000..3f80a2e --- /dev/null +++ b/docker/Dockerfile.rust @@ -0,0 +1,12 @@ +FROM fedora:latest +ENV USER=lam +ENV PASS=lampass + +RUN useradd -m -G wheel -p ${PASS} ${USER} +RUN dnf install -y \ + clang \ + gcc +USER ${USER} +WORKDIR /home/${USER} +RUN mkdir -p /home/${USER}/lam +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain stable -t x86_64-unknown-linux-musl -y \ No newline at end of file diff --git a/docker/buildramfs.sh b/docker/buildramfs.sh index 21f5ba4..c9beabc 100755 --- a/docker/buildramfs.sh +++ b/docker/buildramfs.sh @@ -1,40 +1,12 @@ #!/bin/bash -ARCH="x86_64" -BB_VER="1.31.0" - # Dirs -mkdir -p root -cd root -mkdir -p bin dev etc lib mnt proc sbin sys tmp var +mkdir -p image/{bin,dev,etc,lib,mnt,proc,sbin,sys,tmp,var} +cd image +mkdir -p cd - -# Utils -if [ ! -f "root/bin/busybox" ]; then - curl -L "https://www.busybox.net/downloads/binaries/${BB_VER}-defconfig-multiarch-musl/busybox-${ARCH}" >root/bin/busybox -fi -cd root/bin -chmod +x busybox -ln -s busybox mount -ln -s busybox sh -cd - - -# Init process - -cat >>root/init << EOF -#!/bin/busybox sh -/bin/busybox --install -s /bin -mount -t devtmpfs devtmpfs /dev -mount -t proc proc /proc -mount -t sysfs sysfs /sys -mount -t tmpfs tmpfs /tmp -setsid cttyhack sh -exec /bin/sh -EOF -chmod +x root/init - # initramfs creation - -cd root +cd image find . | cpio -ov --format=newc | gzip -9 >../initramfs cd - diff --git a/scripts/build-docker-envs.sh b/scripts/build-docker-envs.sh new file mode 100755 index 0000000..0b5de78 --- /dev/null +++ b/scripts/build-docker-envs.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +docker build -f docker/Dockerfile.rust -t lam-rust:latest docker +docker build -f docker/Dockerfile.kernel -t lam-kernel:latest docker \ No newline at end of file diff --git a/scripts/build-ramfs.sh b/scripts/build-ramfs.sh index 89da5f0..127af74 100755 --- a/scripts/build-ramfs.sh +++ b/scripts/build-ramfs.sh @@ -1,4 +1,12 @@ #!/bin/sh -mkdir -p artifacts/ramdisk -docker run --rm -v "$(pwd)/artifacts/ramdisk:/build/artifacts" lam-kernel bash -c "cd /build/ramfs && ./buildramfs.sh && cp initramfs /build/artifacts/initramfs.cpio.gz" \ No newline at end of file +BASE=target/x86_64-unknown-linux-musl/release/ + +mkdir -p target/image +cp ${BASE}/init target/image/init + +docker run --rm \ + -v "$(pwd)/artifacts/ramdisk:/build/artifacts" \ + -v "$(pwd)/target/image:/build/ramfs/image" \ + lam-kernel \ + bash -c "cd /build/ramfs && ./buildramfs.sh && cp initramfs /build/artifacts/initramfs.cpio.gz" \ No newline at end of file diff --git a/scripts/rust-build.sh b/scripts/rust-build.sh new file mode 100755 index 0000000..b40ee39 --- /dev/null +++ b/scripts/rust-build.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker run -v $(pwd):/home/lam/build -w /home/lam/build lam-rust:latest bash -lc "cargo build --release --target x86_64-unknown-linux-musl" \ No newline at end of file diff --git a/scripts/rust-clippy.sh b/scripts/rust-clippy.sh new file mode 100755 index 0000000..841e6ca --- /dev/null +++ b/scripts/rust-clippy.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker run -v $(pwd):/home/lam/build -w /home/lam/build lam-rust:latest bash -lc "cargo clippy --release --target x86_64-unknown-linux-musl" \ No newline at end of file diff --git a/src/bin/init.rs b/src/bin/init.rs new file mode 100644 index 0000000..5d0b747 --- /dev/null +++ b/src/bin/init.rs @@ -0,0 +1,106 @@ +use log::{error, info}; +use simple_logger::SimpleLogger; +use std::convert::Infallible; +use std::ffi::{CString, NulError}; +use std::fmt; +use std::io; +use thiserror::Error; + +#[derive(Error, Debug)] +enum InitError { + IOError(#[from] io::Error), + NullReference(#[from] NulError), + TheUniverseBroke(#[from] Infallible), + MountError(libc::c_int, String), +} + +impl fmt::Display for InitError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InitError::IOError(e) => write!(f, "IO error: {}", e), + InitError::NullReference(ne) => write!( + f, + "This shouldn't happen, but encountered a null error: {}", + ne + ), + InitError::TheUniverseBroke(e) => write!(f, "The world broke, somehow: {}", e), + InitError::MountError(e, s) => { + write!(f, "Failed to mount basic target {} with errno {}", s, e) + } + } + } +} + +fn mount( + source: &str, + fs_type: &str, + target: &str, + flags: &str, + permissions: libc::c_ulong, +) -> Result<(), InitError> { + unsafe { + let source_c = CString::new(source)?; + let target_c = CString::new(target)?; + let fs_type_c = CString::new(fs_type)?; + let flags_c = CString::new(flags)?; + let res = libc::mount( + source_c.as_ptr(), + target_c.as_ptr(), + fs_type_c.as_ptr(), + permissions, + flags_c.as_ptr() as *const libc::c_void, + ); + if res != 0 { + let errno = *(libc::__errno_location()); + Err(InitError::MountError(errno, source.to_string())) + } else { + info!("Mounted {} on {}", fs_type, target); + Ok(()) + } + } +} + +fn real_main() -> Result<(), InitError> { + mount( + "proc", + "proc", + "/proc", + "hidepid=2", + libc::MS_NOSUID | libc::MS_NOEXEC | libc::MS_NODEV, + )?; + mount( + "devtmpfs", + "devtmpfs", + "/dev", + "mode=755", + libc::MS_NOSUID | libc::MS_STRICTATIME, + )?; + mount( + "tmpfs", + "tmpfs", + "/tmp", + "", + libc::MS_NOSUID | libc::MS_NODEV, + )?; + mount( + "sysfs", + "sysfs", + "/sys", + "", + libc::MS_NOSUID | libc::MS_NOEXEC | libc::MS_NODEV, + )?; + + Ok(()) +} + +fn main() { + SimpleLogger::new().init().unwrap(); + + if let Err(e) = real_main() { + error!("Init exited with a top-level error: {}", e); + } + + unsafe { + libc::reboot(libc::LINUX_REBOOT_CMD_POWER_OFF); + } +}