Create an init process in Rust that can mount the initial file systems appropriately.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
old/
|
old/
|
||||||
tmp/
|
tmp/
|
||||||
artifacts/
|
artifacts/
|
||||||
|
target/
|
||||||
|
Cargo.lock
|
||||||
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "lam"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Adam Wick <awick@uhsure.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2.81"
|
||||||
|
log = "0.4.11"
|
||||||
|
simple_logger = "1.11.0"
|
||||||
|
thiserror = "1.0.23"
|
||||||
48
README.md
Normal file
48
README.md
Normal file
@@ -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!!
|
||||||
@@ -20,5 +20,5 @@ RUN dnf install -y \
|
|||||||
RUN mkdir -p /build/ramfs
|
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 curl https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${KERNEL_VERSION}.tar.xz | tar xJC /build
|
||||||
RUN mv /build/linux* /build/kernel
|
RUN mv /build/linux* /build/kernel
|
||||||
COPY buildramfs.sh /build/ramfs/buildramfs.sh
|
|
||||||
COPY lam.config /build/kernel/.config
|
COPY lam.config /build/kernel/.config
|
||||||
|
COPY buildramfs.sh /build/ramfs/buildramfs.sh
|
||||||
|
|||||||
12
docker/Dockerfile.rust
Normal file
12
docker/Dockerfile.rust
Normal file
@@ -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
|
||||||
@@ -1,40 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
ARCH="x86_64"
|
|
||||||
BB_VER="1.31.0"
|
|
||||||
|
|
||||||
# Dirs
|
# Dirs
|
||||||
mkdir -p root
|
mkdir -p image/{bin,dev,etc,lib,mnt,proc,sbin,sys,tmp,var}
|
||||||
cd root
|
cd image
|
||||||
mkdir -p bin dev etc lib mnt proc sbin sys tmp var
|
mkdir -p
|
||||||
cd -
|
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
|
# initramfs creation
|
||||||
|
cd image
|
||||||
cd root
|
|
||||||
find . | cpio -ov --format=newc | gzip -9 >../initramfs
|
find . | cpio -ov --format=newc | gzip -9 >../initramfs
|
||||||
cd -
|
cd -
|
||||||
|
|||||||
4
scripts/build-docker-envs.sh
Executable file
4
scripts/build-docker-envs.sh
Executable file
@@ -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
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
mkdir -p artifacts/ramdisk
|
BASE=target/x86_64-unknown-linux-musl/release/
|
||||||
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"
|
|
||||||
|
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"
|
||||||
3
scripts/rust-build.sh
Executable file
3
scripts/rust-build.sh
Executable file
@@ -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"
|
||||||
3
scripts/rust-clippy.sh
Executable file
3
scripts/rust-clippy.sh
Executable file
@@ -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"
|
||||||
106
src/bin/init.rs
Normal file
106
src/bin/init.rs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user